943 lines
30 KiB
Diff
943 lines
30 KiB
Diff
From 74f71d2e97bc15350b05967e6cff590a6b287a21 Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Mon, 4 Oct 2021 11:53:55 +0200
|
|
Subject: [PATCH] s4:mitkdc: Add support for MIT Kerberos 1.20
|
|
|
|
This also addresses CVE-2020-17049.
|
|
|
|
MIT Kerberos 1.20 is in pre-release state at the time writing this commit. It
|
|
will be released in autumn 2022. We need to support MIT Kerberos 1.19 till
|
|
enough distributions have been released with MIT Kerberos 1.20.
|
|
|
|
Pair-Programmed-With: Robbie Harwood <rharwood@redhat.com>
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
---
|
|
.../samba/tests/krb5/compatability_tests.py | 9 +-
|
|
selftest/knownfail_mit_kdc | 25 +-
|
|
selftest/knownfail_mit_kdc_1_20 | 9 +
|
|
selftest/wscript | 6 +
|
|
source4/kdc/mit-kdb/kdb_samba.c | 7 +-
|
|
source4/kdc/mit-kdb/kdb_samba.h | 10 +
|
|
source4/kdc/mit-kdb/kdb_samba_policies.c | 125 ++++-
|
|
source4/kdc/mit_samba.c | 481 +++++++++++++++++-
|
|
source4/kdc/mit_samba.h | 11 +-
|
|
source4/selftest/tests.py | 7 +-
|
|
wscript_configure_system_mitkrb5 | 4 +
|
|
11 files changed, 661 insertions(+), 33 deletions(-)
|
|
create mode 100644 selftest/knownfail_mit_kdc_1_20
|
|
|
|
diff --git a/python/samba/tests/krb5/compatability_tests.py b/python/samba/tests/krb5/compatability_tests.py
|
|
index 44c2afd41dc..b862f381bc5 100755
|
|
--- a/python/samba/tests/krb5/compatability_tests.py
|
|
+++ b/python/samba/tests/krb5/compatability_tests.py
|
|
@@ -120,7 +120,12 @@ class SimpleKerberosTests(KDCBaseTest):
|
|
self.fail(
|
|
"(Heimdal) Salt populated for ARCFOUR_HMAC_MD5 encryption")
|
|
|
|
- def test_heimdal_ticket_signature(self):
|
|
+ # This tests also passes again Samba AD built with MIT Kerberos 1.20 which
|
|
+ # is not released yet.
|
|
+ #
|
|
+ # FIXME: Should be moved to to a new kdc_tgt_tests.py once MIT KRB5 1.20
|
|
+ # is released.
|
|
+ def test_ticket_signature(self):
|
|
# Ensure that a DC correctly issues tickets signed with its krbtgt key.
|
|
user_creds = self.get_client_creds()
|
|
target_creds = self.get_service_creds()
|
|
@@ -141,7 +146,7 @@ class SimpleKerberosTests(KDCBaseTest):
|
|
self.verify_ticket(service_ticket, key, service_ticket=True,
|
|
expect_ticket_checksum=True)
|
|
|
|
- def test_mit_ticket_signature(self):
|
|
+ def test_mit_pre_1_20_ticket_signature(self):
|
|
# Ensure that a DC does not issue tickets signed with its krbtgt key.
|
|
user_creds = self.get_client_creds()
|
|
target_creds = self.get_service_creds()
|
|
diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
|
|
index 6d07ca4efb6..f9d5c4b0b46 100644
|
|
--- a/selftest/knownfail_mit_kdc
|
|
+++ b/selftest/knownfail_mit_kdc
|
|
@@ -294,8 +294,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
|
|
#
|
|
# KDC TGS PAC tests
|
|
#
|
|
-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_client_no_auth_data_required\(ad_dc\)
|
|
-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_client_no_auth_data_required\(ad_dc\)
|
|
^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_service_no_auth_data_required\(ad_dc\)
|
|
^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac\(ad_dc\)
|
|
^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_client_no_auth_data_required\(ad_dc\)
|
|
@@ -321,7 +319,10 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
|
|
#
|
|
^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_enc_timestamp_spn(?!_)
|
|
^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_enc_timestamp_spn_realm
|
|
-
|
|
+^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_enc_timestamp_aes128_rc4.*fl2003dc
|
|
+^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_enc_timestamp_mac_aes128_rc4.*fl2003dc
|
|
+^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth.*aes.*rc4.*fl2003dc
|
|
+^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth.*rc4.*aes.*fl2003dc
|
|
# Differences in our KDC compared to windows
|
|
#
|
|
^samba4.krb5.kdc .*.as-req-pac-request # We should reply to a request for a PAC over UDP with KRB5KRB_ERR_RESPONSE_TOO_BIG unconditionally
|
|
@@ -373,30 +374,14 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
|
|
^samba4.blackbox.pkinit_pac.netr-mem-arcfour.s4u2proxy-arcfour.ad_dc_ntvfs:local
|
|
^samba4.blackbox.pkinit_pac.netr-mem-arcfour.verify-sig-arcfour.ad_dc:local
|
|
^samba4.blackbox.pkinit_pac.netr-mem-arcfour.verify-sig-arcfour.ad_dc_ntvfs:local
|
|
-^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2000dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2003dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2008dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2008r2dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2000dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2003dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2008dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2008r2dc
|
|
^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2000dc
|
|
^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2003dc
|
|
^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2008dc
|
|
^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2008r2dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2000dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2003dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2008dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2008r2dc
|
|
^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2000dc
|
|
^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2003dc
|
|
^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2008dc
|
|
^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2008r2dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2000dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2003dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2008dc
|
|
-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2008r2dc
|
|
#
|
|
# Alias tests
|
|
#
|
|
@@ -444,8 +429,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
|
|
^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_false
|
|
^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_none
|
|
^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_true
|
|
-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_authdata_no_pac
|
|
-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac
|
|
^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_req(?!_invalid)
|
|
^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_allowed_denied
|
|
^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_denied
|
|
diff --git a/selftest/knownfail_mit_kdc_1_20 b/selftest/knownfail_mit_kdc_1_20
|
|
new file mode 100644
|
|
index 00000000000..4a47ab974ae
|
|
--- /dev/null
|
|
+++ b/selftest/knownfail_mit_kdc_1_20
|
|
@@ -0,0 +1,9 @@
|
|
+^samba.tests.krb5.compatability_tests.samba.tests.krb5.compatability_tests.SimpleKerberosTests.test_mit_pre_1_20_ticket_signature
|
|
+#
|
|
+# FAST tests
|
|
+# https://github.com/krb5/krb5/pull/1225#issuecomment-996418770
|
|
+#
|
|
+^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_encrypted_challenge_as_req_self\(
|
|
+^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_simple_as_req_self\(
|
|
+^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_simple_as_req_self_pac_request_none\(
|
|
+^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_simple_as_req_self_pac_request_true\(
|
|
diff --git a/selftest/wscript b/selftest/wscript
|
|
index e207b87eeb8..c92b37bd5e1 100644
|
|
--- a/selftest/wscript
|
|
+++ b/selftest/wscript
|
|
@@ -260,6 +260,12 @@ def cmd_testonly(opt):
|
|
env.OPTIONS += " --mitkrb5 --exclude=${srcdir}/selftest/skip_mit_kdc"
|
|
env.FILTER_XFAIL += " --expected-failures=${srcdir}/selftest/"\
|
|
"knownfail_mit_kdc"
|
|
+
|
|
+ if CONFIG_GET(opt, 'HAVE_MIT_KRB5_PRE_1_20'):
|
|
+ env.FILTER_XFAIL += ' --expected-failures=${srcdir}/selftest/knownfail_mit_kdc_pre_1_20'
|
|
+
|
|
+ if CONFIG_GET(opt, 'HAVE_MIT_KRB5_1_20'):
|
|
+ env.FILTER_XFAIL += ' --expected-failures=${srcdir}/selftest/knownfail_mit_kdc_1_20'
|
|
else:
|
|
env.FILTER_XFAIL += " --expected-failures=${srcdir}/selftest/"\
|
|
"knownfail_heimdal_kdc"
|
|
diff --git a/source4/kdc/mit-kdb/kdb_samba.c b/source4/kdc/mit-kdb/kdb_samba.c
|
|
index 02bbdca9f54..f5092f75873 100644
|
|
--- a/source4/kdc/mit-kdb/kdb_samba.c
|
|
+++ b/source4/kdc/mit-kdb/kdb_samba.c
|
|
@@ -166,10 +166,15 @@ kdb_vftabl kdb_function_table = {
|
|
.decrypt_key_data = kdb_samba_dbekd_decrypt_key_data,
|
|
.encrypt_key_data = kdb_samba_dbekd_encrypt_key_data,
|
|
|
|
- .sign_authdata = kdb_samba_db_sign_auth_data,
|
|
.check_policy_as = kdb_samba_db_check_policy_as,
|
|
.audit_as_req = kdb_samba_db_audit_as_req,
|
|
.check_allowed_to_delegate = kdb_samba_db_check_allowed_to_delegate,
|
|
|
|
.free_principal_e_data = kdb_samba_db_free_principal_e_data,
|
|
+
|
|
+#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
|
|
+ .issue_pac = kdb_samba_db_issue_pac,
|
|
+#else
|
|
+ .sign_authdata = kdb_samba_db_sign_auth_data,
|
|
+#endif
|
|
};
|
|
diff --git a/source4/kdc/mit-kdb/kdb_samba.h b/source4/kdc/mit-kdb/kdb_samba.h
|
|
index e9613e2fc7e..dd97061130c 100644
|
|
--- a/source4/kdc/mit-kdb/kdb_samba.h
|
|
+++ b/source4/kdc/mit-kdb/kdb_samba.h
|
|
@@ -113,6 +113,16 @@ krb5_error_code kdb_samba_dbekd_encrypt_key_data(krb5_context context,
|
|
krb5_key_data *key_data);
|
|
|
|
/* from kdb_samba_policies.c */
|
|
+krb5_error_code kdb_samba_db_issue_pac(krb5_context context,
|
|
+ unsigned int flags,
|
|
+ krb5_db_entry *client,
|
|
+ krb5_keyblock *replaced_reply_key,
|
|
+ krb5_db_entry *server,
|
|
+ krb5_db_entry *signing_krbtgt,
|
|
+ krb5_timestamp authtime,
|
|
+ krb5_pac old_pac,
|
|
+ krb5_pac new_pac,
|
|
+ krb5_data ***auth_indicators);
|
|
|
|
krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
|
|
unsigned int flags,
|
|
diff --git a/source4/kdc/mit-kdb/kdb_samba_policies.c b/source4/kdc/mit-kdb/kdb_samba_policies.c
|
|
index 793fe366c35..cbc9bbb9dae 100644
|
|
--- a/source4/kdc/mit-kdb/kdb_samba_policies.c
|
|
+++ b/source4/kdc/mit-kdb/kdb_samba_policies.c
|
|
@@ -190,6 +190,7 @@ static krb5_error_code ks_get_pac(krb5_context context,
|
|
return code;
|
|
}
|
|
|
|
+#if KRB5_KDB_DAL_MAJOR_VERSION < 9
|
|
static krb5_error_code ks_verify_pac(krb5_context context,
|
|
unsigned int flags,
|
|
krb5_const_principal client_princ,
|
|
@@ -557,6 +558,128 @@ done:
|
|
|
|
return code;
|
|
}
|
|
+#else /* KRB5_KDB_DAL_MAJOR_VERSION >= 9 */
|
|
+static krb5_error_code ks_update_pac(krb5_context context,
|
|
+ int flags,
|
|
+ krb5_db_entry *client,
|
|
+ krb5_db_entry *server,
|
|
+ krb5_db_entry *signing_krbtgt,
|
|
+ krb5_pac old_pac,
|
|
+ krb5_pac new_pac)
|
|
+{
|
|
+ struct mit_samba_context *mit_ctx = NULL;
|
|
+ krb5_error_code code;
|
|
+
|
|
+ mit_ctx = ks_get_context(context);
|
|
+ if (mit_ctx == NULL) {
|
|
+ return KRB5_KDB_DBNOTINITED;
|
|
+ }
|
|
+
|
|
+ code = mit_samba_update_pac(mit_ctx,
|
|
+ context,
|
|
+ flags,
|
|
+ client,
|
|
+ server,
|
|
+ signing_krbtgt,
|
|
+ old_pac,
|
|
+ new_pac);
|
|
+ if (code != 0) {
|
|
+ return code;
|
|
+ }
|
|
+
|
|
+ return code;
|
|
+}
|
|
+
|
|
+krb5_error_code kdb_samba_db_issue_pac(krb5_context context,
|
|
+ unsigned int flags,
|
|
+ krb5_db_entry *client,
|
|
+ krb5_keyblock *replaced_reply_key,
|
|
+ krb5_db_entry *server,
|
|
+ krb5_db_entry *signing_krbtgt,
|
|
+ krb5_timestamp authtime,
|
|
+ krb5_pac old_pac,
|
|
+ krb5_pac new_pac,
|
|
+ krb5_data ***auth_indicators)
|
|
+{
|
|
+ char *client_name = NULL;
|
|
+ char *server_name = NULL;
|
|
+ krb5_error_code code = EINVAL;
|
|
+
|
|
+ /* The KDC handles both signing and verification for us. */
|
|
+
|
|
+ if (client != NULL) {
|
|
+ code = krb5_unparse_name(context,
|
|
+ client->princ,
|
|
+ &client_name);
|
|
+ if (code != 0) {
|
|
+ return code;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (server != NULL) {
|
|
+ code = krb5_unparse_name(context,
|
|
+ server->princ,
|
|
+ &server_name);
|
|
+ if (code != 0) {
|
|
+ SAFE_FREE(client_name);
|
|
+ return code;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Get a new PAC for AS-REQ or S4U2Self for our realm.
|
|
+ *
|
|
+ * For a simple cross-realm S4U2Proxy there will be the following TGS
|
|
+ * requests after the client realm is identified:
|
|
+ *
|
|
+ * 1. server@SREALM to SREALM for krbtgt/CREALM@SREALM -- a regular TGS
|
|
+ * request with server's normal TGT and no S4U2Self padata.
|
|
+ * 2. server@SREALM to CREALM for server@SREALM (expressed as an
|
|
+ * enterprise principal), with the TGT from #1 as header ticket and
|
|
+ * S4U2Self padata identifying the client.
|
|
+ * 3. server@SREALM to SREALM for server@SREALM with S4U2Self padata,
|
|
+ * with the referral TGT from #2 as header ticket
|
|
+ *
|
|
+ * In request 2 the PROTOCOL_TRANSITION and CROSS_REALM flags are set,
|
|
+ * and the request is for a local client (so client != NULL) and we
|
|
+ * want to make a new PAC.
|
|
+ *
|
|
+ * In request 3 the PROTOCOL_TRANSITION and CROSS_REALM flags are also
|
|
+ * set, but the request is for a non-local client (so client == NULL)
|
|
+ * and we want to copy the subject PAC contained in the referral TGT.
|
|
+ */
|
|
+ if (old_pac == NULL ||
|
|
+ (client != NULL && (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION))) {
|
|
+ DBG_NOTICE("Generate PAC for AS-REQ [client=%s, flags=%#08x]\n",
|
|
+ client_name != NULL ? client_name : "<unknown>",
|
|
+ flags);
|
|
+
|
|
+ code = ks_get_pac(context,
|
|
+ client,
|
|
+ server,
|
|
+ replaced_reply_key,
|
|
+ &new_pac);
|
|
+ } else {
|
|
+ DBG_NOTICE("Update PAC for TGS-REQ [client=%s, server=%s, "
|
|
+ "flags=%#08x]\n",
|
|
+ client_name != NULL ? client_name : "<unknown>",
|
|
+ server_name != NULL ? server_name : "<unknown>",
|
|
+ flags);
|
|
+
|
|
+ code = ks_update_pac(context,
|
|
+ flags,
|
|
+ client,
|
|
+ server,
|
|
+ signing_krbtgt,
|
|
+ old_pac,
|
|
+ new_pac);
|
|
+ }
|
|
+ SAFE_FREE(client_name);
|
|
+ SAFE_FREE(server_name);
|
|
+
|
|
+ return code;
|
|
+}
|
|
+#endif /* KRB5_KDB_DAL_MAJOR_VERSION */
|
|
|
|
krb5_error_code kdb_samba_db_check_allowed_to_delegate(krb5_context context,
|
|
krb5_const_principal client,
|
|
@@ -635,4 +758,4 @@ void kdb_samba_db_audit_as_req(krb5_context context,
|
|
samba_bad_password_count(client, error_code);
|
|
|
|
/* TODO: perform proper audit logging for addresses */
|
|
-}
|
|
+}
|
|
\ No newline at end of file
|
|
diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c
|
|
index cb72b5de294..d58bbea4a5d 100644
|
|
--- a/source4/kdc/mit_samba.c
|
|
+++ b/source4/kdc/mit_samba.c
|
|
@@ -229,6 +229,27 @@ int mit_samba_get_principal(struct mit_samba_context *ctx,
|
|
sflags |= SDB_F_FORCE_CANON;
|
|
#endif
|
|
|
|
+#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
|
|
+ if (kflags & KRB5_KDB_FLAG_REFERRAL_OK) {
|
|
+ sflags |= SDB_F_CANON;
|
|
+ }
|
|
+
|
|
+ if (kflags & KRB5_KDB_FLAG_CLIENT) {
|
|
+ sflags |= SDB_F_GET_CLIENT;
|
|
+
|
|
+ if (!(kflags & KRB5_KDB_FLAG_REFERRAL_OK)) {
|
|
+ sflags |= SDB_F_FOR_AS_REQ;
|
|
+ }
|
|
+ } else if (ks_is_tgs_principal(ctx, principal)) {
|
|
+ sflags |= SDB_F_GET_KRBTGT;
|
|
+ } else {
|
|
+ sflags |= SDB_F_GET_SERVER;
|
|
+
|
|
+ if (!(kflags & KRB5_KDB_FLAG_REFERRAL_OK)) {
|
|
+ sflags |= SDB_F_FOR_TGS_REQ;
|
|
+ }
|
|
+ }
|
|
+#else /* KRB5_KDB_DAL_MAJOR_VERSION < 9 */
|
|
if (kflags & KRB5_KDB_FLAG_CANONICALIZE) {
|
|
sflags |= SDB_F_CANON;
|
|
}
|
|
@@ -247,6 +268,7 @@ int mit_samba_get_principal(struct mit_samba_context *ctx,
|
|
} else {
|
|
sflags |= SDB_F_GET_SERVER|SDB_F_FOR_TGS_REQ;
|
|
}
|
|
+#endif /* KRB5_KDB_DAL_MAJOR_VERSION */
|
|
|
|
/* always set this or the created_by data will not be populated by samba's
|
|
* backend and we will fail to parse the entry later */
|
|
@@ -434,7 +456,7 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
|
|
krb5_context context,
|
|
krb5_db_entry *client,
|
|
krb5_db_entry *server,
|
|
- krb5_keyblock *client_key,
|
|
+ krb5_keyblock *replaced_reply_key,
|
|
krb5_pac *pac)
|
|
{
|
|
TALLOC_CTX *tmp_ctx;
|
|
@@ -461,12 +483,10 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
|
|
return ENOMEM;
|
|
}
|
|
|
|
-#if 0 /* TODO Find out if this is a pkinit_reply key */
|
|
/* Check if we have a PREAUTH key */
|
|
- if (client_key != NULL) {
|
|
+ if (replaced_reply_key != NULL) {
|
|
cred_ndr_ptr = &cred_ndr;
|
|
}
|
|
-#endif
|
|
|
|
is_krbtgt = ks_is_tgs_principal(smb_ctx, server->princ);
|
|
|
|
@@ -488,9 +508,9 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
|
|
return EINVAL;
|
|
}
|
|
|
|
- if (cred_ndr != NULL) {
|
|
+ if (replaced_reply_key != NULL && cred_ndr != NULL) {
|
|
code = samba_kdc_encrypt_pac_credentials(context,
|
|
- client_key,
|
|
+ replaced_reply_key,
|
|
cred_ndr,
|
|
tmp_ctx,
|
|
&cred_blob);
|
|
@@ -514,6 +534,7 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
|
|
return code;
|
|
}
|
|
|
|
+#if KRB5_KDB_DAL_MAJOR_VERSION < 9
|
|
krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
|
|
krb5_context context,
|
|
int flags,
|
|
@@ -999,6 +1020,454 @@ done:
|
|
talloc_free(tmp_ctx);
|
|
return code;
|
|
}
|
|
+#else
|
|
+krb5_error_code mit_samba_update_pac(struct mit_samba_context *ctx,
|
|
+ krb5_context context,
|
|
+ int flags,
|
|
+ krb5_db_entry *client,
|
|
+ krb5_db_entry *server,
|
|
+ krb5_db_entry *krbtgt,
|
|
+ krb5_pac old_pac,
|
|
+ krb5_pac new_pac)
|
|
+{
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ krb5_error_code code;
|
|
+ NTSTATUS nt_status;
|
|
+ DATA_BLOB *pac_blob = NULL;
|
|
+ DATA_BLOB *upn_blob = NULL;
|
|
+ DATA_BLOB *requester_sid_blob = NULL;
|
|
+ struct samba_kdc_entry *client_skdc_entry = NULL;
|
|
+ struct samba_kdc_entry *server_skdc_entry = NULL;
|
|
+ struct samba_kdc_entry *krbtgt_skdc_entry = NULL;
|
|
+ bool is_in_db = false;
|
|
+ bool is_untrusted = false;
|
|
+ bool is_krbtgt = false;
|
|
+ size_t num_types = 0;
|
|
+ uint32_t *types = NULL;
|
|
+ size_t i = 0;
|
|
+ ssize_t logon_info_idx = -1;
|
|
+ ssize_t delegation_idx = -1;
|
|
+ ssize_t logon_name_idx = -1;
|
|
+ ssize_t upn_dns_info_idx = -1;
|
|
+ ssize_t srv_checksum_idx = -1;
|
|
+ ssize_t kdc_checksum_idx = -1;
|
|
+ ssize_t tkt_checksum_idx = -1;
|
|
+ ssize_t attrs_info_idx = -1;
|
|
+ ssize_t requester_sid_idx = -1;
|
|
+
|
|
+ /* Create a memory context early so code can use talloc_stackframe() */
|
|
+ tmp_ctx = talloc_named(ctx, 0, "mit_samba_update_pac context");
|
|
+ if (tmp_ctx == NULL) {
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ if (client != NULL) {
|
|
+ client_skdc_entry =
|
|
+ talloc_get_type_abort(client->e_data,
|
|
+ struct samba_kdc_entry);
|
|
+
|
|
+ /*
|
|
+ * Check the objectSID of the client and pac data are the same.
|
|
+ * Does a parse and SID check, but no crypto.
|
|
+ */
|
|
+ code = samba_kdc_validate_pac_blob(context,
|
|
+ client_skdc_entry,
|
|
+ old_pac);
|
|
+ if (code != 0) {
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (krbtgt == NULL) {
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ krbtgt_skdc_entry =
|
|
+ talloc_get_type_abort(krbtgt->e_data,
|
|
+ struct samba_kdc_entry);
|
|
+
|
|
+ /*
|
|
+ * If the krbtgt was generated by an RODC, and we are not that
|
|
+ * RODC, then we need to regenerate the PAC - we can't trust
|
|
+ * it, and confirm that the RODC was permitted to print this ticket
|
|
+ *
|
|
+ * Because of the samba_kdc_validate_pac_blob() step we can be
|
|
+ * sure that the record in 'client' or 'server' matches the SID in the
|
|
+ * original PAC.
|
|
+ */
|
|
+ code = samba_krbtgt_is_in_db(krbtgt_skdc_entry,
|
|
+ &is_in_db,
|
|
+ &is_untrusted);
|
|
+ if (code != 0) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (is_untrusted) {
|
|
+ struct auth_user_info_dc *user_info_dc = NULL;
|
|
+ WERROR werr;
|
|
+
|
|
+ if (client == NULL) {
|
|
+ code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ nt_status = samba_kdc_get_pac_blobs(tmp_ctx,
|
|
+ client_skdc_entry,
|
|
+ &pac_blob,
|
|
+ NULL,
|
|
+ &upn_blob,
|
|
+ NULL,
|
|
+ PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY,
|
|
+ &requester_sid_blob,
|
|
+ &user_info_dc);
|
|
+ if (!NT_STATUS_IS_OK(nt_status)) {
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Check if the SID list in the user_info_dc intersects
|
|
+ * correctly with the RODC allow/deny lists.
|
|
+ */
|
|
+ werr = samba_rodc_confirm_user_is_allowed(user_info_dc->num_sids,
|
|
+ user_info_dc->sids,
|
|
+ krbtgt_skdc_entry,
|
|
+ client_skdc_entry);
|
|
+ if (!W_ERROR_IS_OK(werr)) {
|
|
+ code = KRB5KDC_ERR_TGT_REVOKED;
|
|
+ if (W_ERROR_EQUAL(werr,
|
|
+ WERR_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
+ code = KRB5KDC_ERR_POLICY;
|
|
+ }
|
|
+ goto done;
|
|
+ }
|
|
+ } else {
|
|
+ pac_blob = talloc_zero(tmp_ctx, DATA_BLOB);
|
|
+ if (pac_blob == NULL) {
|
|
+ code = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ nt_status = samba_kdc_update_pac_blob(tmp_ctx,
|
|
+ context,
|
|
+ krbtgt_skdc_entry->kdc_db_ctx->samdb,
|
|
+ old_pac,
|
|
+ pac_blob,
|
|
+ NULL,
|
|
+ NULL);
|
|
+ if (!NT_STATUS_IS_OK(nt_status)) {
|
|
+ DEBUG(0, ("Update PAC blob failed: %s\n",
|
|
+ nt_errstr(nt_status)));
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Check the types of the given PAC */
|
|
+ code = krb5_pac_get_types(context, old_pac, &num_types, &types);
|
|
+ if (code != 0) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < num_types; i++) {
|
|
+ switch (types[i]) {
|
|
+ case PAC_TYPE_LOGON_INFO:
|
|
+ if (logon_info_idx != -1) {
|
|
+ DBG_WARNING("logon info type[%u] twice [%zd] and "
|
|
+ "[%zu]: \n",
|
|
+ types[i],
|
|
+ logon_info_idx,
|
|
+ i);
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ logon_info_idx = i;
|
|
+ break;
|
|
+ case PAC_TYPE_CONSTRAINED_DELEGATION:
|
|
+ if (delegation_idx != -1) {
|
|
+ DBG_WARNING("constrained delegation type[%u] "
|
|
+ "twice [%zd] and [%zu]: \n",
|
|
+ types[i],
|
|
+ delegation_idx,
|
|
+ i);
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ delegation_idx = i;
|
|
+ break;
|
|
+ case PAC_TYPE_LOGON_NAME:
|
|
+ if (logon_name_idx != -1) {
|
|
+ DBG_WARNING("logon name type[%u] twice [%zd] "
|
|
+ "and [%zu]: \n",
|
|
+ types[i],
|
|
+ logon_name_idx,
|
|
+ i);
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ logon_name_idx = i;
|
|
+ break;
|
|
+ case PAC_TYPE_UPN_DNS_INFO:
|
|
+ if (upn_dns_info_idx != -1) {
|
|
+ DBG_WARNING("upn dns info type[%u] twice [%zd] "
|
|
+ "and [%zu]: \n",
|
|
+ types[i],
|
|
+ upn_dns_info_idx,
|
|
+ i);
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ upn_dns_info_idx = i;
|
|
+ break;
|
|
+ case PAC_TYPE_SRV_CHECKSUM:
|
|
+ if (srv_checksum_idx != -1) {
|
|
+ DBG_WARNING("srv checksum type[%u] twice [%zd] "
|
|
+ "and [%zu]: \n",
|
|
+ types[i],
|
|
+ srv_checksum_idx,
|
|
+ i);
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ srv_checksum_idx = i;
|
|
+ break;
|
|
+ case PAC_TYPE_KDC_CHECKSUM:
|
|
+ if (kdc_checksum_idx != -1) {
|
|
+ DBG_WARNING("kdc checksum type[%u] twice [%zd] "
|
|
+ "and [%zu]: \n",
|
|
+ types[i],
|
|
+ kdc_checksum_idx,
|
|
+ i);
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ kdc_checksum_idx = i;
|
|
+ break;
|
|
+ case PAC_TYPE_TICKET_CHECKSUM:
|
|
+ if (tkt_checksum_idx != -1) {
|
|
+ DBG_WARNING("ticket checksum type[%u] twice "
|
|
+ "[%zd] and [%zu]: \n",
|
|
+ types[i],
|
|
+ tkt_checksum_idx,
|
|
+ i);
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ tkt_checksum_idx = i;
|
|
+ break;
|
|
+ case PAC_TYPE_ATTRIBUTES_INFO:
|
|
+ if (attrs_info_idx != -1) {
|
|
+ DBG_WARNING("attributes info type[%u] twice "
|
|
+ "[%zd] and [%zu]: \n",
|
|
+ types[i],
|
|
+ attrs_info_idx,
|
|
+ i);
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ attrs_info_idx = i;
|
|
+ break;
|
|
+ case PAC_TYPE_REQUESTER_SID:
|
|
+ if (requester_sid_idx != -1) {
|
|
+ DBG_WARNING("requester sid type[%u] twice"
|
|
+ "[%zd] and [%zu]: \n",
|
|
+ types[i],
|
|
+ requester_sid_idx,
|
|
+ i);
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ requester_sid_idx = i;
|
|
+ break;
|
|
+ default:
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (logon_info_idx == -1) {
|
|
+ DBG_WARNING("PAC_TYPE_LOGON_INFO missing\n");
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ if (logon_name_idx == -1) {
|
|
+ DBG_WARNING("PAC_TYPE_LOGON_NAME missing\n");
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ if (srv_checksum_idx == -1) {
|
|
+ DBG_WARNING("PAC_TYPE_SRV_CHECKSUM missing\n");
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ if (kdc_checksum_idx == -1) {
|
|
+ DBG_WARNING("PAC_TYPE_KDC_CHECKSUM missing\n");
|
|
+ code = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ if (!(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) &&
|
|
+ requester_sid_idx == -1) {
|
|
+ DBG_WARNING("PAC_TYPE_REQUESTER_SID missing\n");
|
|
+ code = KRB5KDC_ERR_TGT_REVOKED;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ server_skdc_entry = talloc_get_type_abort(server->e_data,
|
|
+ struct samba_kdc_entry);
|
|
+
|
|
+ /*
|
|
+ * The server account may be set not to want the PAC.
|
|
+ *
|
|
+ * While this is wasteful if the above cacluations were done
|
|
+ * and now thrown away, this is cleaner as we do any ticket
|
|
+ * signature checking etc always.
|
|
+ *
|
|
+ * UF_NO_AUTH_DATA_REQUIRED is the rare case and most of the
|
|
+ * time (eg not accepting a ticket from the RODC) we do not
|
|
+ * need to re-generate anything anyway.
|
|
+ */
|
|
+ if (!samba_princ_needs_pac(server_skdc_entry)) {
|
|
+ code = 0;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ is_krbtgt = ks_is_tgs_principal(ctx, server->princ);
|
|
+
|
|
+ if (!is_untrusted && !is_krbtgt) {
|
|
+ /*
|
|
+ * The client may have requested no PAC when obtaining the
|
|
+ * TGT.
|
|
+ */
|
|
+ bool requested_pac = false;
|
|
+
|
|
+ code = samba_client_requested_pac(context,
|
|
+ &old_pac,
|
|
+ tmp_ctx,
|
|
+ &requested_pac);
|
|
+ if (code != 0 || !requested_pac) {
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+#define MAX_PAC_BUFFERS 64 /* Avoid infinite loops */
|
|
+
|
|
+ for (i = 0; i < MAX_PAC_BUFFERS;) {
|
|
+ krb5_data type_data;
|
|
+ DATA_BLOB type_blob = data_blob_null;
|
|
+ uint32_t type;
|
|
+
|
|
+ if (i < num_types) {
|
|
+ type = types[i];
|
|
+ i++;
|
|
+ } else {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch (type) {
|
|
+ case PAC_TYPE_LOGON_INFO:
|
|
+ type_blob = *pac_blob;
|
|
+ break;
|
|
+ case PAC_TYPE_CREDENTIAL_INFO:
|
|
+ /*
|
|
+ * Note that we copy the credential blob,
|
|
+ * as it's only usable with the PKINIT based
|
|
+ * AS-REP reply key, it's only available on the
|
|
+ * host which did the AS-REQ/AS-REP exchange.
|
|
+ *
|
|
+ * This matches Windows 2008R2...
|
|
+ */
|
|
+ break;
|
|
+ case PAC_TYPE_LOGON_NAME:
|
|
+ /*
|
|
+ * This is generated in the main KDC code
|
|
+ */
|
|
+ continue;
|
|
+ case PAC_TYPE_UPN_DNS_INFO:
|
|
+ /*
|
|
+ * Replace in the RODC case, otherwise
|
|
+ * upn_blob is NULL and we just copy.
|
|
+ */
|
|
+ if (upn_blob != NULL) {
|
|
+ type_blob = *upn_blob;
|
|
+ }
|
|
+ break;
|
|
+ case PAC_TYPE_SRV_CHECKSUM:
|
|
+ /*
|
|
+ * This is generated in the main KDC code
|
|
+ */
|
|
+ continue;
|
|
+ case PAC_TYPE_KDC_CHECKSUM:
|
|
+ /*
|
|
+ * This is generated in the main KDC code
|
|
+ */
|
|
+ continue;
|
|
+ case PAC_TYPE_TICKET_CHECKSUM:
|
|
+ /*
|
|
+ * This is generated in the main KDC code
|
|
+ */
|
|
+ continue;
|
|
+ case PAC_TYPE_CONSTRAINED_DELEGATION:
|
|
+ /*
|
|
+ * This is generated in the main KDC code
|
|
+ */
|
|
+ continue;
|
|
+ case PAC_TYPE_ATTRIBUTES_INFO:
|
|
+ if (!is_untrusted && is_krbtgt) {
|
|
+ /* just copy... */
|
|
+ break;
|
|
+ } else {
|
|
+ continue;
|
|
+ }
|
|
+ case PAC_TYPE_REQUESTER_SID:
|
|
+ if (is_krbtgt) {
|
|
+ /*
|
|
+ * Replace in the RODC case, otherwise
|
|
+ * requester_sid_blob is NULL and we just copy.
|
|
+ */
|
|
+ if (requester_sid_blob != NULL) {
|
|
+ type_blob = *requester_sid_blob;
|
|
+ }
|
|
+ break;
|
|
+ } else {
|
|
+ continue;
|
|
+ }
|
|
+ default:
|
|
+ /* just copy... */
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (type_blob.length != 0) {
|
|
+ code = smb_krb5_copy_data_contents(&type_data,
|
|
+ type_blob.data,
|
|
+ type_blob.length);
|
|
+ if (code != 0) {
|
|
+ goto done;
|
|
+ }
|
|
+ } else {
|
|
+ code = krb5_pac_get_buffer(context,
|
|
+ old_pac,
|
|
+ type,
|
|
+ &type_data);
|
|
+ if (code != 0) {
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ code = krb5_pac_add_buffer(context,
|
|
+ new_pac,
|
|
+ type,
|
|
+ &type_data);
|
|
+ smb_krb5_free_data_contents(context, &type_data);
|
|
+ if (code != 0) {
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+done:
|
|
+ SAFE_FREE(types);
|
|
+ talloc_free(tmp_ctx);
|
|
+ return code;
|
|
+}
|
|
+#endif
|
|
|
|
/* provide header, function is exported but there are no public headers */
|
|
|
|
diff --git a/source4/kdc/mit_samba.h b/source4/kdc/mit_samba.h
|
|
index 4431e82a1b2..f34fb1bbfd5 100644
|
|
--- a/source4/kdc/mit_samba.h
|
|
+++ b/source4/kdc/mit_samba.h
|
|
@@ -51,7 +51,7 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
|
|
krb5_context context,
|
|
krb5_db_entry *client,
|
|
krb5_db_entry *server,
|
|
- krb5_keyblock *client_key,
|
|
+ krb5_keyblock *replaced_reply_key,
|
|
krb5_pac *pac);
|
|
|
|
krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
|
|
@@ -64,6 +64,15 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
|
|
krb5_keyblock *krbtgt_keyblock,
|
|
krb5_pac *pac);
|
|
|
|
+krb5_error_code mit_samba_update_pac(struct mit_samba_context *ctx,
|
|
+ krb5_context context,
|
|
+ int flags,
|
|
+ krb5_db_entry *client,
|
|
+ krb5_db_entry *server,
|
|
+ krb5_db_entry *signing_krbtgt,
|
|
+ krb5_pac old_pac,
|
|
+ krb5_pac new_pac);
|
|
+
|
|
int mit_samba_check_client_access(struct mit_samba_context *ctx,
|
|
krb5_db_entry *client,
|
|
const char *client_name,
|
|
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
|
|
index 3af8e92d7f2..f451ad1cec2 100755
|
|
--- a/source4/selftest/tests.py
|
|
+++ b/source4/selftest/tests.py
|
|
@@ -964,7 +964,7 @@ for env in ['fileserver_smb1', 'nt4_member', 'clustere
|
|
have_fast_support = 1
|
|
claims_support = 0
|
|
compound_id_support = 0
|
|
-tkt_sig_support = int('SAMBA4_USES_HEIMDAL' in config_hash)
|
|
+tkt_sig_support = 1 if('SAMBA4_USES_HEIMDAL' in config_hash or 'HAVE_MIT_KRB5_1_20' in config_hash) else 0
|
|
full_sig_support = int('SAMBA4_USES_HEIMDAL' in config_hash)
|
|
expect_pac = int('SAMBA4_USES_HEIMDAL' in config_hash)
|
|
extra_pac_buffers = int('SAMBA4_USES_HEIMDAL' in config_hash)
|
|
diff --git a/wscript_configure_system_mitkrb5 b/wscript_configure_system_mitkrb5
|
|
index efdbced6e78..b0640654260 100644
|
|
--- a/wscript_configure_system_mitkrb5
|
|
+++ b/wscript_configure_system_mitkrb5
|
|
@@ -98,6 +98,10 @@ if conf.env.KRB5_CONFIG:
|
|
else:
|
|
Logs.info('MIT Kerberos %s detected, MIT krb5 build can proceed' % (krb5_version))
|
|
|
|
+ if parse_version(krb5_version) < parse_version('1.20'):
|
|
+ conf.DEFINE('HAVE_MIT_KRB5_PRE_1_20', 1)
|
|
+ if parse_version(krb5_version) >= parse_version('1.20'):
|
|
+ conf.DEFINE('HAVE_MIT_KRB5_1_20', 1)
|
|
conf.define('USING_SYSTEM_MITKRB5', '"%s"' % krb5_version)
|
|
|
|
conf.CHECK_HEADERS('krb5.h krb5/locate_plugin.h', lib='krb5')
|
|
--
|
|
2.37.1
|
|
|