From 74f71d2e97bc15350b05967e6cff590a6b287a21 Mon Sep 17 00:00:00 2001 From: Andreas Schneider 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 Signed-off-by: Andreas Schneider Signed-off-by: Robbie Harwood Reviewed-by: Stefan Metzmacher --- .../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 : "", + 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 : "", + server_name != NULL ? server_name : "", + 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