diff options
author | Joseph Sutton <josephsutton@catalyst.net.nz> | 2022-11-09 13:45:13 +1300 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2022-12-13 13:07:29 +0000 |
commit | a50a2be622afaa7a280312ea12f5eb9c9a0c41da (patch) | |
tree | cdc51bdbc2effdc4a65fe0af39ab5777cb5805e1 | |
parent | 538315a2aa6d03b7639b49eb1576efa8755fefec (diff) | |
download | samba-a50a2be622afaa7a280312ea12f5eb9c9a0c41da.tar.gz |
CVE-2022-37967 Add new PAC checksum
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15231
Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
-rw-r--r-- | librpc/idl/krb5pac.idl | 4 | ||||
-rwxr-xr-x | python/samba/tests/krb5/compatability_tests.py | 22 | ||||
-rw-r--r-- | python/samba/tests/krb5/kdc_base_test.py | 8 | ||||
-rwxr-xr-x | python/samba/tests/krb5/kdc_tgs_tests.py | 59 | ||||
-rw-r--r-- | python/samba/tests/krb5/raw_testcase.py | 78 | ||||
-rwxr-xr-x | python/samba/tests/krb5/rodc_tests.py | 4 | ||||
-rwxr-xr-x | python/samba/tests/krb5/s4u_tests.py | 61 | ||||
-rw-r--r-- | selftest/knownfail_mit_kdc | 30 | ||||
-rw-r--r-- | source4/kdc/pac-glue.c | 24 | ||||
-rw-r--r-- | source4/kdc/wdc-samba4.c | 2 | ||||
-rwxr-xr-x | source4/selftest/tests.py | 15 | ||||
-rw-r--r-- | source4/torture/rpc/remote_pac.c | 14 | ||||
-rw-r--r-- | third_party/heimdal/kdc/kerberos5.c | 1 | ||||
-rw-r--r-- | third_party/heimdal/kdc/krb5tgs.c | 2 | ||||
-rw-r--r-- | third_party/heimdal/lib/krb5/pac.c | 169 |
15 files changed, 439 insertions, 54 deletions
diff --git a/librpc/idl/krb5pac.idl b/librpc/idl/krb5pac.idl index d2f8414d69e..57c37656eb6 100644 --- a/librpc/idl/krb5pac.idl +++ b/librpc/idl/krb5pac.idl @@ -166,7 +166,8 @@ interface krb5pac PAC_TYPE_DEVICE_CLAIMS_INFO = 15, PAC_TYPE_TICKET_CHECKSUM = 16, PAC_TYPE_ATTRIBUTES_INFO = 17, - PAC_TYPE_REQUESTER_SID = 18 + PAC_TYPE_REQUESTER_SID = 18, + PAC_TYPE_FULL_CHECKSUM = 19 } PAC_TYPE; typedef struct { @@ -188,6 +189,7 @@ interface krb5pac [case(PAC_TYPE_CLIENT_CLAIMS_INFO)][subcontext(0)] DATA_BLOB_REM client_claims_info; [case(PAC_TYPE_DEVICE_INFO)][subcontext(0xFFFFFC01)] PAC_DEVICE_INFO_CTR device_info; [case(PAC_TYPE_DEVICE_CLAIMS_INFO)][subcontext(0)] DATA_BLOB_REM device_claims_info; + [case(PAC_TYPE_FULL_CHECKSUM)] PAC_SIGNATURE_DATA full_checksum; /* when new PAC info types are added they are supposed to be done in such a way that they are backwards compatible with existing servers. This makes it safe to just use a [default] for diff --git a/python/samba/tests/krb5/compatability_tests.py b/python/samba/tests/krb5/compatability_tests.py index b862f381bc5..72be7b34c4a 100755 --- a/python/samba/tests/krb5/compatability_tests.py +++ b/python/samba/tests/krb5/compatability_tests.py @@ -167,6 +167,28 @@ class SimpleKerberosTests(KDCBaseTest): self.verify_ticket(service_ticket, key, service_ticket=True, expect_ticket_checksum=False) + def test_full_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() + + krbtgt_creds = self.get_krbtgt_creds() + key = self.TicketDecryptionKey_from_creds(krbtgt_creds) + + # Get a TGT from the DC. + tgt = self.get_tgt(user_creds) + + # Ensure the PAC contains the expected checksums. + self.verify_ticket(tgt, key, service_ticket=False) + + # Get a service ticket from the DC. + service_ticket = self.get_service_ticket(tgt, target_creds) + + # Ensure the PAC contains the expected checksums. + self.verify_ticket(service_ticket, key, service_ticket=True, + expect_ticket_checksum=True, + expect_full_checksum=True) + def as_pre_auth_req(self, creds, etypes): user = creds.get_username() realm = creds.get_realm() diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 51a2163a0cf..df0f41db992 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1697,11 +1697,15 @@ class KDCBaseTest(RawKerberosTest): krbtgt_creds = self.get_krbtgt_creds() krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) + is_tgs_princ = self.is_tgs_principal(sname) expect_ticket_checksum = (self.tkt_sig_support - and not self.is_tgs_principal(sname)) + and not is_tgs_princ) + expect_full_checksum = (self.full_sig_support + and not is_tgs_princ) self.verify_ticket(service_ticket_creds, krbtgt_key, service_ticket=True, expect_pac=expect_pac, - expect_ticket_checksum=expect_ticket_checksum) + expect_ticket_checksum=expect_ticket_checksum, + expect_full_checksum=expect_full_checksum) self.tkt_cache[cache_key] = service_ticket_creds diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py index b54b249a54a..b897b6cafdd 100755 --- a/python/samba/tests/krb5/kdc_tgs_tests.py +++ b/python/samba/tests/krb5/kdc_tgs_tests.py @@ -1594,6 +1594,43 @@ class KdcTgsTests(KdcTgsBaseTests): self._fast(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED, expected_sname=self.get_krbtgt_sname()) + # Test making a TGS request with an RC4-encrypted TGT. + def test_tgs_rc4(self): + creds = self._get_creds() + tgt = self._get_tgt(creds, etype=kcrypto.Enctype.RC4) + self._run_tgs(tgt, expected_error=KDC_ERR_GENERIC) + + def test_renew_rc4(self): + creds = self._get_creds() + tgt = self._get_tgt(creds, renewable=True, etype=kcrypto.Enctype.RC4) + self._renew_tgt(tgt, expected_error=KDC_ERR_GENERIC, + expect_pac_attrs=True, + expect_pac_attrs_pac_request=True, + expect_requester_sid=True) + + def test_validate_rc4(self): + creds = self._get_creds() + tgt = self._get_tgt(creds, invalid=True, etype=kcrypto.Enctype.RC4) + self._validate_tgt(tgt, expected_error=KDC_ERR_GENERIC, + expect_pac_attrs=True, + expect_pac_attrs_pac_request=True, + expect_requester_sid=True) + + def test_s4u2self_rc4(self): + creds = self._get_creds() + tgt = self._get_tgt(creds, etype=kcrypto.Enctype.RC4) + self._s4u2self(tgt, creds, expected_error=KDC_ERR_GENERIC) + + def test_user2user_rc4(self): + creds = self._get_creds() + tgt = self._get_tgt(creds, etype=kcrypto.Enctype.RC4) + self._user2user(tgt, creds, expected_error=KDC_ERR_GENERIC) + + def test_fast_rc4(self): + creds = self._get_creds() + tgt = self._get_tgt(creds, etype=kcrypto.Enctype.RC4) + self._fast(tgt, creds, expected_error=KDC_ERR_GENERIC) + # Test user-to-user with incorrect service principal names. def test_user2user_matching_sname_host(self): creds = self._get_creds() @@ -2617,7 +2654,9 @@ class KdcTgsTests(KdcTgsBaseTests): can_modify_logon_info=True, can_modify_requester_sid=True, remove_pac_attrs=False, - remove_requester_sid=False): + remove_requester_sid=False, + etype=None, + cksum_etype=None): self.assertFalse(renewable and invalid) if remove_pac: @@ -2636,7 +2675,9 @@ class KdcTgsTests(KdcTgsBaseTests): can_modify_logon_info=can_modify_logon_info, can_modify_requester_sid=can_modify_requester_sid, remove_pac_attrs=remove_pac_attrs, - remove_requester_sid=remove_requester_sid) + remove_requester_sid=remove_requester_sid, + etype=None, + cksum_etype=cksum_etype) def _modify_tgt(self, tgt, @@ -2651,7 +2692,9 @@ class KdcTgsTests(KdcTgsBaseTests): can_modify_logon_info=True, can_modify_requester_sid=True, remove_pac_attrs=False, - remove_requester_sid=False): + remove_requester_sid=False, + etype=None, + cksum_etype=None): if from_rodc: krbtgt_creds = self.get_mock_rodc_krbtgt_creds() else: @@ -2690,13 +2733,19 @@ class KdcTgsTests(KdcTgsBaseTests): else: change_sid_fn = None - krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) + krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds, + etype) if remove_pac: checksum_keys = None else: + if etype == cksum_etype: + cksum_key = krbtgt_key + else: + cksum_key = self.TicketDecryptionKey_from_creds(krbtgt_creds, + cksum_etype) checksum_keys = { - krb5pac.PAC_TYPE_KDC_CHECKSUM: krbtgt_key + krb5pac.PAC_TYPE_KDC_CHECKSUM: cksum_key } if renewable: diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 4f683a9631b..4fb2b7bf181 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -557,7 +557,8 @@ class RawKerberosTest(TestCaseInTempDir): pac_checksum_types = {krb5pac.PAC_TYPE_SRV_CHECKSUM, krb5pac.PAC_TYPE_KDC_CHECKSUM, - krb5pac.PAC_TYPE_TICKET_CHECKSUM} + krb5pac.PAC_TYPE_TICKET_CHECKSUM, + krb5pac.PAC_TYPE_FULL_CHECKSUM} etypes_to_test = ( {"value": -1111, "name": "dummy", }, @@ -651,6 +652,12 @@ class RawKerberosTest(TestCaseInTempDir): tkt_sig_support = '0' cls.tkt_sig_support = bool(int(tkt_sig_support)) + full_sig_support = samba.tests.env_get_var_value('FULL_SIG_SUPPORT', + allow_missing=True) + if full_sig_support is None: + full_sig_support = '0' + cls.full_sig_support = bool(int(full_sig_support)) + gnutls_pbkdf2_support = samba.tests.env_get_var_value( 'GNUTLS_PBKDF2_SUPPORT', allow_missing=True) @@ -2434,6 +2441,7 @@ class RawKerberosTest(TestCaseInTempDir): unexpected_flags=None, ticket_decryption_key=None, expect_ticket_checksum=None, + expect_full_checksum=None, generate_fast_fn=None, generate_fast_armor_fn=None, generate_fast_padata_fn=None, @@ -2503,6 +2511,7 @@ class RawKerberosTest(TestCaseInTempDir): 'unexpected_flags': unexpected_flags, 'ticket_decryption_key': ticket_decryption_key, 'expect_ticket_checksum': expect_ticket_checksum, + 'expect_full_checksum': expect_full_checksum, 'generate_fast_fn': generate_fast_fn, 'generate_fast_armor_fn': generate_fast_armor_fn, 'generate_fast_padata_fn': generate_fast_padata_fn, @@ -2568,6 +2577,7 @@ class RawKerberosTest(TestCaseInTempDir): unexpected_flags=None, ticket_decryption_key=None, expect_ticket_checksum=None, + expect_full_checksum=None, generate_fast_fn=None, generate_fast_armor_fn=None, generate_fast_padata_fn=None, @@ -2638,6 +2648,7 @@ class RawKerberosTest(TestCaseInTempDir): 'unexpected_flags': unexpected_flags, 'ticket_decryption_key': ticket_decryption_key, 'expect_ticket_checksum': expect_ticket_checksum, + 'expect_full_checksum': expect_full_checksum, 'generate_fast_fn': generate_fast_fn, 'generate_fast_armor_fn': generate_fast_armor_fn, 'generate_fast_padata_fn': generate_fast_padata_fn, @@ -3120,7 +3131,8 @@ class RawKerberosTest(TestCaseInTempDir): self.check_pac_buffers(pac_data, kdc_exchange_dict) expect_ticket_checksum = kdc_exchange_dict['expect_ticket_checksum'] - if expect_ticket_checksum: + expect_full_checksum = kdc_exchange_dict['expect_full_checksum'] + if expect_ticket_checksum or expect_full_checksum: self.assertIsNotNone(ticket_decryption_key) if ticket_decryption_key is not None: @@ -3130,7 +3142,9 @@ class RawKerberosTest(TestCaseInTempDir): service_ticket=service_ticket, expect_pac=expect_pac, expect_ticket_checksum=expect_ticket_checksum - or self.tkt_sig_support) + or self.tkt_sig_support, + expect_full_checksum=expect_full_checksum + or self.full_sig_support) kdc_exchange_dict['rep_ticket_creds'] = ticket_creds @@ -3267,6 +3281,8 @@ class RawKerberosTest(TestCaseInTempDir): unchecked = set() if not self.tkt_sig_support: require_strict.add(krb5pac.PAC_TYPE_TICKET_CHECKSUM) + if not self.full_sig_support: + require_strict.add(krb5pac.PAC_TYPE_FULL_CHECKSUM) expected_client_claims = kdc_exchange_dict['expected_client_claims'] unexpected_client_claims = kdc_exchange_dict[ @@ -3323,6 +3339,7 @@ class RawKerberosTest(TestCaseInTempDir): if rep_msg_type == KRB_TGS_REP: if not self.is_tgs_principal(expected_sname): expected_types.append(krb5pac.PAC_TYPE_TICKET_CHECKSUM) + expected_types.append(krb5pac.PAC_TYPE_FULL_CHECKSUM) expect_extra_pac_buffers = self.is_tgs(expected_sname) @@ -4194,7 +4211,8 @@ class RawKerberosTest(TestCaseInTempDir): def verify_ticket(self, ticket, krbtgt_keys, service_ticket, expect_pac=True, - expect_ticket_checksum=True): + expect_ticket_checksum=True, + expect_full_checksum=None): # Decrypt the ticket. key = ticket.decryption_key @@ -4239,6 +4257,8 @@ class RawKerberosTest(TestCaseInTempDir): checksums = {} + full_checksum_buffer = None + for pac_buffer, raw_pac_buffer in zip(pac.buffers, raw_pac.buffers): buffer_type = pac_buffer.type if buffer_type in self.pac_checksum_types: @@ -4253,7 +4273,9 @@ class RawKerberosTest(TestCaseInTempDir): checksums[buffer_type] = checksum, ctype - if buffer_type != krb5pac.PAC_TYPE_TICKET_CHECKSUM: + if buffer_type == krb5pac.PAC_TYPE_FULL_CHECKSUM: + full_checksum_buffer = raw_pac_buffer + elif buffer_type != krb5pac.PAC_TYPE_TICKET_CHECKSUM: # Zero the checksum field so that we can later verify the # checksums. The ticket checksum field is not zeroed. @@ -4267,6 +4289,17 @@ class RawKerberosTest(TestCaseInTempDir): # Re-encode the PAC. pac_data = ndr_pack(raw_pac) + if full_checksum_buffer is not None: + signature = ndr_unpack( + krb5pac.PAC_SIGNATURE_DATA, + full_checksum_buffer.info.remaining) + signature.signature = bytes(len(checksum)) + full_checksum_buffer.info.remaining = ndr_pack( + signature) + + # Re-encode the PAC. + full_pac_data = ndr_pack(raw_pac) + # Verify the signatures. server_checksum, server_ctype = checksums[ @@ -4295,6 +4328,7 @@ class RawKerberosTest(TestCaseInTempDir): if not service_ticket: self.assertNotIn(krb5pac.PAC_TYPE_TICKET_CHECKSUM, checksums) + self.assertNotIn(krb5pac.PAC_TYPE_FULL_CHECKSUM, checksums) else: ticket_checksum, ticket_ctype = checksums.get( krb5pac.PAC_TYPE_TICKET_CHECKSUM, @@ -4313,6 +4347,19 @@ class RawKerberosTest(TestCaseInTempDir): ticket_ctype, ticket_checksum) + full_checksum, full_ctype = checksums.get( + krb5pac.PAC_TYPE_FULL_CHECKSUM, + (None, None)) + if expect_full_checksum: + self.assertIsNotNone(full_checksum) + elif expect_full_checksum is False: + self.assertIsNone(full_checksum) + if full_checksum is not None: + krbtgt_key.verify_rodc_checksum(KU_NON_KERB_CKSUM_SALT, + full_pac_data, + full_ctype, + full_checksum) + def modified_ticket(self, ticket, *, new_ticket_key=None, @@ -4370,6 +4417,14 @@ class RawKerberosTest(TestCaseInTempDir): checksum_keys[krb5pac.PAC_TYPE_TICKET_CHECKSUM] = ( kdc_checksum_key) + if krb5pac.PAC_TYPE_FULL_CHECKSUM not in checksum_keys: + # If the full signature key is not present, fall back to the key + # used for the KDC signature. + kdc_checksum_key = checksum_keys.get(krb5pac.PAC_TYPE_KDC_CHECKSUM) + if kdc_checksum_key is not None: + checksum_keys[krb5pac.PAC_TYPE_FULL_CHECKSUM] = ( + kdc_checksum_key) + # Decrypt the ticket. enc_part = ticket.ticket['enc-part'] @@ -4522,6 +4577,19 @@ class RawKerberosTest(TestCaseInTempDir): # Add the new checksum buffers to the PAC. pac.buffers = pac_buffers + # Calculate the full checksum and insert it into the PAC. + full_checksum_buffer = checksum_buffers.get( + krb5pac.PAC_TYPE_FULL_CHECKSUM) + if full_checksum_buffer is not None: + full_checksum_key = checksum_keys[krb5pac.PAC_TYPE_FULL_CHECKSUM] + + pac_data = ndr_pack(pac) + full_checksum = full_checksum_key.make_checksum( + KU_NON_KERB_CKSUM_SALT, + pac_data) + + full_checksum_buffer.info.signature = full_checksum + # Calculate the server and KDC checksums and insert them into the PAC. server_checksum_buffer = checksum_buffers.get( diff --git a/python/samba/tests/krb5/rodc_tests.py b/python/samba/tests/krb5/rodc_tests.py index 83ee35d650a..3e0e2a7712e 100755 --- a/python/samba/tests/krb5/rodc_tests.py +++ b/python/samba/tests/krb5/rodc_tests.py @@ -65,7 +65,9 @@ class RodcKerberosTests(KDCBaseTest): to_rodc=True) # Ensure the PAC contains the expected checksums. - self.verify_ticket(service_ticket, rodc_key, service_ticket=True) + self.verify_ticket(service_ticket, rodc_key, service_ticket=True, + expect_ticket_checksum=True, + expect_full_checksum=True) if __name__ == "__main__": diff --git a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py index 49727685f72..d59b1224e4e 100755 --- a/python/samba/tests/krb5/s4u_tests.py +++ b/python/samba/tests/krb5/s4u_tests.py @@ -1155,8 +1155,8 @@ class S4UKerberosTests(KDCBaseTest): def test_constrained_delegation_missing_service_checksum(self): # Present the service's ticket without the required checksums. - for checksum in filter(lambda x: x != krb5pac.PAC_TYPE_TICKET_CHECKSUM, - self.pac_checksum_types): + for checksum in (krb5pac.PAC_TYPE_SRV_CHECKSUM, + krb5pac.PAC_TYPE_KDC_CHECKSUM): with self.subTest(checksum=checksum): self._run_delegation_test( { @@ -1190,8 +1190,8 @@ class S4UKerberosTests(KDCBaseTest): def test_rbcd_missing_service_checksum(self): # Present the service's ticket without the required checksums. - for checksum in filter(lambda x: x != krb5pac.PAC_TYPE_TICKET_CHECKSUM, - self.pac_checksum_types): + for checksum in (krb5pac.PAC_TYPE_SRV_CHECKSUM, + krb5pac.PAC_TYPE_KDC_CHECKSUM): with self.subTest(checksum=checksum): self._run_delegation_test( { @@ -1380,6 +1380,33 @@ class S4UKerberosTests(KDCBaseTest): checksum=checksum, ctype=ctype) }) + def test_constrained_delegation_rc4_client_checksum(self): + # Present a user ticket with RC4 checksums. + expected_error_mode = (KDC_ERR_GENERIC, + KDC_ERR_INAPP_CKSUM) + + self._run_delegation_test( + { + 'expected_error_mode': expected_error_mode, + 'allow_delegation': True, + 'modify_client_tkt_fn': self.rc4_pac_checksums, + 'expect_edata': False, + }) + + def test_rbcd_rc4_client_checksum(self): + # Present a user ticket with RC4 checksums. + expected_error_mode = (KDC_ERR_GENERIC, + KDC_ERR_BADOPTION) + + self._run_delegation_test( + { + 'expected_error_mode': expected_error_mode, + 'expected_status': ntstatus.NT_STATUS_NOT_SUPPORTED, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_client_tkt_fn': self.rc4_pac_checksums, + }) + def remove_pac_checksum(self, ticket, checksum): checksum_keys = self.get_krbtgt_checksum_key() @@ -1421,6 +1448,7 @@ class S4UKerberosTests(KDCBaseTest): krb5pac.PAC_TYPE_SRV_CHECKSUM: server_key, krb5pac.PAC_TYPE_KDC_CHECKSUM: krbtgt_key, krb5pac.PAC_TYPE_TICKET_CHECKSUM: krbtgt_key, + krb5pac.PAC_TYPE_FULL_CHECKSUM: krbtgt_key, } # Make a copy of the existing key and change the ctype. @@ -1433,6 +1461,31 @@ class S4UKerberosTests(KDCBaseTest): checksum_keys=checksum_keys, include_checksums={checksum: True}) + def rc4_pac_checksums(self, ticket): + krbtgt_creds = self.get_krbtgt_creds() + rc4_krbtgt_key = self.TicketDecryptionKey_from_creds( + krbtgt_creds, etype=Enctype.RC4) + + server_key = ticket.decryption_key + + checksum_keys = { + krb5pac.PAC_TYPE_SRV_CHECKSUM: server_key, + krb5pac.PAC_TYPE_KDC_CHECKSUM: rc4_krbtgt_key, + krb5pac.PAC_TYPE_TICKET_CHECKSUM: rc4_krbtgt_key, + krb5pac.PAC_TYPE_FULL_CHECKSUM: rc4_krbtgt_key, + } + + include_checksums = { + krb5pac.PAC_TYPE_SRV_CHECKSUM: True, + krb5pac.PAC_TYPE_KDC_CHECKSUM: True, + krb5pac.PAC_TYPE_TICKET_CHECKSUM: True, + krb5pac.PAC_TYPE_FULL_CHECKSUM: True, + } + + return self.modified_ticket(ticket, + checksum_keys=checksum_keys, + include_checksums=include_checksums) + def add_delegation_info(self, ticket, services=None): def modify_pac_fn(pac): pac_buffers = pac.buffers diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc index b4845f42f2c..75ac7c2b239 100644 --- a/selftest/knownfail_mit_kdc +++ b/selftest/knownfail_mit_kdc @@ -260,21 +260,36 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ # # PAC tests # -^netr-bdc-arcfour.verify-sig-arcfour -^netr-bdc-arcfour.verify-sig-arcfour +^samba4.blackbox.pkinit_pac.STEP1 remote.pac verification.ad_dc:local ^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc:local ^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc:local ^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc:local ^samba4.blackbox.pkinit_pac.netr-mem-arcfour.s4u2proxy-arcfour.ad_dc:local ^samba4.blackbox.pkinit_pac.netr-mem-arcfour.verify-sig-arcfour.ad_dc: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 # @@ -290,6 +305,7 @@ 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_fast_pac_request_false ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_pac_request_none ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_pac_request_true +^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rc4.ad_dc ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_req ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_req_invalid ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_allowed_denied @@ -305,6 +321,7 @@ 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_renew_authdata_no_pac ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_pac_request_true +^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rc4.ad_dc ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_req ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_allowed_denied ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_denied @@ -322,6 +339,7 @@ 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_rc4.ad_dc ^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 ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_no_krbtgt_link @@ -335,6 +353,7 @@ 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_s4u2self_sid_mismatch_nonexisting ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_authdata_no_pac ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac +^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rc4.ad_dc ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rename ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_allowed_denied ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_denied @@ -348,6 +367,7 @@ 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_user2user_authdata_no_pac ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_sname +^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rc4.ad_dc ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_req_invalid ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_allowed_denied ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_denied @@ -367,6 +387,7 @@ 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_validate_authdata_no_pac ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_pac_request_true +^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rc4.ad_dc ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_req ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_allowed_denied ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_denied @@ -580,3 +601,8 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ ^samba.tests.krb5.etype_tests.samba.tests.krb5.etype_tests.EtypeTests.test_as_aes_supported_rc4_requested.ad_dc ^samba.tests.krb5.etype_tests.samba.tests.krb5.etype_tests.EtypeTests.test_as_rc4_requested.ad_dc ^samba.tests.krb5.etype_tests.samba.tests.krb5.etype_tests.EtypeTests.test_as_rc4_supported_rc4_requested.ad_dc +# +# KDC compatibility +# +^samba.tests.krb5.compatability_tests.samba.tests.krb5.compatability_tests.SimpleKerberosTests.test_full_signature.ad_dc +^samba.tests.krb5.compatability_tests.samba.tests.krb5.compatability_tests.SimpleKerberosTests.test_full_signature.fl2003dc diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c index 5e20f296a65..f844b08d513 100644 --- a/source4/kdc/pac-glue.c +++ b/source4/kdc/pac-glue.c @@ -1443,6 +1443,7 @@ krb5_error_code samba_kdc_update_pac(TALLOC_CTX *mem_ctx, ssize_t tkt_checksum_idx = -1; ssize_t attrs_info_idx = -1; ssize_t requester_sid_idx = -1; + ssize_t full_checksum_idx = -1; if (client != NULL) { /* @@ -1699,6 +1700,18 @@ krb5_error_code samba_kdc_update_pac(TALLOC_CTX *mem_ctx, } requester_sid_idx = i; break; + case PAC_TYPE_FULL_CHECKSUM: + if (full_checksum_idx != -1) { + DBG_WARNING("full checksum type[%u] twice " + "[%zd] and [%zu]: \n", + types[i], + full_checksum_idx, + i); + code = EINVAL; + goto done; + } + full_checksum_idx = i; + break; default: continue; } @@ -1906,6 +1919,17 @@ krb5_error_code samba_kdc_update_pac(TALLOC_CTX *mem_ctx, type_blob = *requester_sid_blob; } break; + case PAC_TYPE_FULL_CHECKSUM: + /* + * This is generated in the main KDC code + */ + if (flags & SAMBA_KDC_FLAG_SKIP_PAC_BUFFER) { + continue; + } + + type_blob = data_blob_const(&zero_byte, 1); + + break; default: /* just copy... */ break; diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c index f022dfbd766..1c10f13972f 100644 --- a/source4/kdc/wdc-samba4.c +++ b/source4/kdc/wdc-samba4.c @@ -319,7 +319,7 @@ static krb5_error_code samba_wdc_reget_pac2(astgs_request_t r, goto out; } - /* Check the KDC and ticket signatures. */ + /* Check the KDC, whole-PAC and ticket signatures. */ ret = krb5_pac_verify(context, *pac, 0, diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 763d119012f..3b11d9ac5f8 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -1002,6 +1002,11 @@ if ('SAMBA4_USES_HEIMDAL' in config_hash or else: tkt_sig_support = 0 +if 'SAMBA4_USES_HEIMDAL' in config_hash: + full_sig_support = 1 +else: + full_sig_support = 0 + gnutls_pbkdf2_support = int('HAVE_GNUTLS_PBKDF2' in config_hash) if 'HAVE_MIT_KRB5_1_20' in config_hash: @@ -1024,6 +1029,7 @@ krb5_environ = { 'CLAIMS_SUPPORT': claims_support, 'COMPOUND_ID_SUPPORT': compound_id_support, 'TKT_SIG_SUPPORT': tkt_sig_support, + 'FULL_SIG_SUPPORT': full_sig_support, 'GNUTLS_PBKDF2_SUPPORT': gnutls_pbkdf2_support, 'EXPECT_PAC': expect_pac, 'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers, @@ -1701,8 +1707,13 @@ for env in ["rodc", "promoted_dc", "fl2000dc", "fl2008r2dc"]: planpythontestsuite("ad_dc", "samba.tests.krb5.as_canonicalization_tests", environ=krb5_environ) -planpythontestsuite("ad_dc", "samba.tests.krb5.compatability_tests", - environ=krb5_environ) +for env, fast_support in [("ad_dc", True), + ("fl2003dc", False)]: + planpythontestsuite(env, "samba.tests.krb5.compatability_tests", + environ={ + **krb5_environ, + 'FAST_SUPPORT': int(fast_support), + }) planpythontestsuite("ad_dc", "samba.tests.krb5.kdc_tests", environ=krb5_environ) planpythontestsuite( diff --git a/source4/torture/rpc/remote_pac.c b/source4/torture/rpc/remote_pac.c index 83c13243c08..c3060069933 100644 --- a/source4/torture/rpc/remote_pac.c +++ b/source4/torture/rpc/remote_pac.c @@ -313,7 +313,7 @@ static bool test_PACVerify(struct torture_context *tctx, (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_pull_struct_blob of PAC_DATA structure failed"); - num_pac_buffers = 5; + num_pac_buffers = 6; if (expect_pac_upn_dns_info) { num_pac_buffers += 1; } @@ -370,6 +370,12 @@ static bool test_PACVerify(struct torture_context *tctx, pac_buf->info != NULL, "PAC_TYPE_TICKET_CHECKSUM info"); + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_FULL_CHECKSUM); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_FULL_CHECKSUM"); + torture_assert(tctx, + pac_buf->info != NULL, + "PAC_TYPE_FULL_CHECKSUM info"); + ok = netlogon_validate_pac(tctx, p, server_creds, secure_channel_type, test_machine_name, negotiate_flags, pac_data, session_info); @@ -1191,7 +1197,7 @@ static bool test_S4U2Proxy(struct torture_context *tctx, (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_pull_struct_blob of PAC_DATA structure failed"); - num_pac_buffers = 7; + num_pac_buffers = 8; torture_assert_int_equal(tctx, pac_data_struct.version, 0, "version"); torture_assert_int_equal(tctx, pac_data_struct.num_buffers, num_pac_buffers, "num_buffers"); @@ -1220,6 +1226,10 @@ static bool test_S4U2Proxy(struct torture_context *tctx, torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_TICKET_CHECKSUM"); torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_TICKET_CHECKSUM info"); + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_FULL_CHECKSUM); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_FULL_CHECKSUM"); + torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_FULL_CHECKSUM info"); + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_CONSTRAINED_DELEGATION); torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_CONSTRAINED_DELEGATION"); torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_CONSTRAINED_DELEGATION info"); diff --git a/third_party/heimdal/kdc/kerberos5.c b/third_party/heimdal/kdc/kerberos5.c index 3e0f2dbd6b6..b4968afcaaf 100644 --- a/third_party/heimdal/kdc/kerberos5.c +++ b/third_party/heimdal/kdc/kerberos5.c @@ -1913,6 +1913,7 @@ generate_pac(astgs_request_t r, const Key *skey, const Key *tkey, rodc_id, NULL, /* UPN */ canon_princ, + false, /* add_full_sig */ is_tgs ? &r->pac_attributes : NULL, &data); krb5_free_principal(r->context, client); diff --git a/third_party/heimdal/kdc/krb5tgs.c b/third_party/heimdal/kdc/krb5tgs.c index aab6806fbe1..893e77749cf 100644 --- a/third_party/heimdal/kdc/krb5tgs.c +++ b/third_party/heimdal/kdc/krb5tgs.c @@ -778,7 +778,7 @@ tgs_make_reply(astgs_request_t r, ret = _krb5_kdc_pac_sign_ticket(r->context, r->pac, r->client_princ, serverkey, krbtgtkey, rodc_id, NULL, r->canon_client_princ, - add_ticket_sig, et, + add_ticket_sig, add_ticket_sig, et, is_tgs ? &r->pac_attributes : NULL); if (ret) goto out; diff --git a/third_party/heimdal/lib/krb5/pac.c b/third_party/heimdal/lib/krb5/pac.c index e6dfe2aef3d..26403e665a0 100644 --- a/third_party/heimdal/lib/krb5/pac.c +++ b/third_party/heimdal/lib/krb5/pac.c @@ -74,6 +74,7 @@ struct krb5_pac_data { struct PAC_INFO_BUFFER *upn_dns_info; struct PAC_INFO_BUFFER *ticket_checksum; struct PAC_INFO_BUFFER *attributes_info; + struct PAC_INFO_BUFFER *full_checksum; krb5_data ticket_sign_data; /* PAC_UPN_DNS_INFO */ @@ -101,6 +102,7 @@ struct krb5_pac_data { #define PAC_TICKET_CHECKSUM 16 #define PAC_ATTRIBUTES_INFO 17 #define PAC_REQUESTOR_SID 18 +#define PAC_FULL_CHECKSUM 19 /* Flag in PAC_UPN_DNS_INFO */ #define PAC_EXTRA_LOGON_INFO_FLAGS_UPN_DEFAULTED 0x1 @@ -398,6 +400,13 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len, else p->attributes_info = &p->pac->buffers[i]; break; + case PAC_FULL_CHECKSUM: + if (p->full_checksum) + krb5_set_error_message(context, ret = EINVAL, + N_("PAC has multiple full checksums", "")); + else + p->full_checksum = &p->pac->buffers[i]; + break; default: break; } } @@ -668,7 +677,8 @@ verify_checksum(krb5_context context, const struct PAC_INFO_BUFFER *sig, const krb5_data *data, void *ptr, size_t len, - const krb5_keyblock *key) + const krb5_keyblock *key, + krb5_boolean strict_cksumtype_match) { krb5_storage *sp = NULL; uint32_t type; @@ -726,7 +736,7 @@ verify_checksum(krb5_context context, * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx * for Microsoft's explaination */ - if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5) { + if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5 && !strict_cksumtype_match) { Checksum local_checksum; memset(&local_checksum, 0, sizeof(local_checksum)); @@ -1192,7 +1202,7 @@ parse_attributes_info(krb5_context context, * @param pac the pac structure returned by krb5_pac_parse(). * @param authtime The time of the ticket the PAC belongs to. * @param principal the principal to verify. - * @param server The service key, most always be given. + * @param server The service key, may be given. * @param privsvr The KDC key, may be given. * @return Returns 0 to indicate success. Otherwise an kerberos et @@ -1210,6 +1220,21 @@ krb5_pac_verify(krb5_context context, const krb5_keyblock *privsvr) { krb5_error_code ret; + /* + * If we are in the KDC, we expect back a full signature in the PAC + * + * This is set up as a seperate variable to make it easier if a + * subsequent patch is added to make this configurable in the + * krb5.conf (or forced into the krb5_context via Samba) + */ + krb5_boolean expect_full_sig = privsvr != NULL; + + /* + * If we are on the KDC, then we trust we are not in a realm with + * buggy Windows 2008 or similar era DCs that give our HMAC-MD5 + * sigatures over AES keys. DES is also already gone. + */ + krb5_boolean strict_cksumtype_match = expect_full_sig; if (pac->server_checksum == NULL) { krb5_set_error_message(context, EINVAL, "PAC missing server checksum"); @@ -1223,6 +1248,10 @@ krb5_pac_verify(krb5_context context, krb5_set_error_message(context, EINVAL, "PAC missing logon name"); return EINVAL; } + if (expect_full_sig && pac->full_checksum == NULL) { + krb5_set_error_message(context, EINVAL, "PAC missing full checksum"); + return EINVAL; + } if (principal != NULL) { ret = verify_logonname(context, pac->logon_name, &pac->data, authtime, @@ -1235,14 +1264,15 @@ krb5_pac_verify(krb5_context context, pac->privsvr_checksum->buffersize < 4) return EINVAL; - /* - * in the service case, clean out data option of the privsvr and - * server checksum before checking the checksum. - */ - if (server != NULL) + if (server != NULL || privsvr != NULL) { krb5_data *copy; + /* + * in the service case, clean out data option of the privsvr and + * server checksum before checking the checksum. + */ + ret = krb5_copy_data(context, &pac->data, ©); if (ret) return ret; @@ -1255,15 +1285,43 @@ krb5_pac_verify(krb5_context context, 0, pac->privsvr_checksum->buffersize - 4); - ret = verify_checksum(context, - pac->server_checksum, - &pac->data, - copy->data, - copy->length, - server); + if (server != NULL) { + ret = verify_checksum(context, + pac->server_checksum, + &pac->data, + copy->data, + copy->length, + server, + strict_cksumtype_match); + if (ret) { + krb5_free_data(context, copy); + return ret; + } + } + + if (privsvr != NULL && pac->full_checksum != NULL) { + /* + * in the full checksum case, also clean out the full + * checksum before verifying it. + */ + memset((char *)copy->data + pac->full_checksum->offset + 4, + 0, + pac->full_checksum->buffersize - 4); + + ret = verify_checksum(context, + pac->full_checksum, + &pac->data, + copy->data, + copy->length, + privsvr, + strict_cksumtype_match); + if (ret) { + krb5_free_data(context, copy); + return ret; + } + } + krb5_free_data(context, copy); - if (ret) - return ret; } if (privsvr) { /* The priv checksum covers the server checksum */ @@ -1273,7 +1331,8 @@ krb5_pac_verify(krb5_context context, (char *)pac->data.data + pac->server_checksum->offset + 4, pac->server_checksum->buffersize - 4, - privsvr); + privsvr, + strict_cksumtype_match); if (ret) return ret; @@ -1286,7 +1345,8 @@ krb5_pac_verify(krb5_context context, ret = verify_checksum(context, pac->ticket_checksum, &pac->data, pac->ticket_sign_data.data, - pac->ticket_sign_data.length, privsvr); + pac->ticket_sign_data.length, privsvr, + strict_cksumtype_match); if (ret) return ret; } @@ -1377,6 +1437,7 @@ _krb5_pac_sign(krb5_context context, uint16_t rodc_id, krb5_const_principal upn_princ, krb5_const_principal canon_princ, + krb5_boolean add_full_sig, uint64_t *pac_attributes, /* optional */ krb5_data *data) { @@ -1384,7 +1445,7 @@ _krb5_pac_sign(krb5_context context, krb5_storage *sp = NULL, *spdata = NULL; uint32_t end; size_t server_size, priv_size; - uint32_t server_offset = 0, priv_offset = 0, ticket_offset = 0; + uint32_t server_offset = 0, priv_offset = 0, ticket_offset = 0, full_offset = 0; uint32_t server_cksumtype = 0, priv_cksumtype = 0; uint32_t num = 0; uint32_t i, sz; @@ -1461,6 +1522,16 @@ _krb5_pac_sign(krb5_context context, N_("PAC has multiple attributes info buffers", "")); goto out; } + } else if (p->pac->buffers[i].type == PAC_FULL_CHECKSUM) { + if (p->full_checksum == NULL) { + p->full_checksum = &p->pac->buffers[i]; + } + if (p->full_checksum != &p->pac->buffers[i]) { + ret = KRB5KDC_ERR_BADOPTION; + krb5_set_error_message(context, ret, + N_("PAC has multiple full checksums", "")); + goto out; + } } } @@ -1473,6 +1544,8 @@ _krb5_pac_sign(krb5_context context, num++; if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) num++; + if (add_full_sig && p->full_checksum == NULL) + num++; /* Allocate any missing-but-necessary buffers */ if (num) { @@ -1515,6 +1588,11 @@ _krb5_pac_sign(krb5_context context, p->ticket_checksum = &p->pac->buffers[p->pac->numbuffers++]; p->ticket_checksum->type = PAC_TICKET_CHECKSUM; } + if (add_full_sig && p->full_checksum == NULL) { + p->full_checksum = &p->pac->buffers[p->pac->numbuffers++]; + memset(p->full_checksum, 0, sizeof(*p->full_checksum)); + p->full_checksum->type = PAC_FULL_CHECKSUM; + } } /* Calculate LOGON NAME */ @@ -1647,6 +1725,31 @@ _krb5_pac_sign(krb5_context context, len += sizeof(rodc_id); CHECK(ret, krb5_store_uint16(spdata, rodc_id), out); } + } else if (add_full_sig && + p->pac->buffers[i].type == PAC_FULL_CHECKSUM) { + if (priv_size > UINT32_MAX - 4) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + goto out; + } + len = priv_size + 4; + if (end > UINT32_MAX - 4) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + goto out; + } + full_offset = end + 4; + CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out); + CHECK(ret, fill_zeros(context, spdata, priv_size), out); + if (rodc_id != 0) { + if (len > UINT32_MAX - sizeof(rodc_id)) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + goto out; + } + len += sizeof(rodc_id); + CHECK(ret, fill_zeros(context, spdata, sizeof(rodc_id)), out); + } } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { len = krb5_storage_write(spdata, logon.data, logon.length); if (logon.length != len) { @@ -1708,6 +1811,21 @@ _krb5_pac_sign(krb5_context context, p->ticket_sign_data.data, p->ticket_sign_data.length, (char *)d.data + ticket_offset, priv_size); + if (ret == 0 && add_full_sig) + ret = create_checksum(context, priv_key, priv_cksumtype, + d.data, d.length, + (char *)d.data + full_offset, priv_size); + if (ret == 0 && add_full_sig && rodc_id != 0) { + void *buf = (char *)d.data + full_offset + priv_size; + krb5_storage *rs = krb5_storage_from_mem(buf, sizeof(rodc_id)); + if (rs == NULL) + ret = krb5_enomem(context); + else + krb5_storage_set_flags(rs, KRB5_STORAGE_BYTEORDER_LE); + if (ret == 0) + ret = krb5_store_uint16(rs, rodc_id); + krb5_storage_free(rs); + } if (ret == 0) ret = create_checksum(context, server_key, server_cksumtype, d.data, d.length, @@ -1717,22 +1835,15 @@ _krb5_pac_sign(krb5_context context, (char *)d.data + server_offset, server_size, (char *)d.data + priv_offset, priv_size); if (ret == 0 && rodc_id != 0) { - krb5_data rd; - krb5_storage *rs = krb5_storage_emem(); + void *buf = (char *)d.data + priv_offset + priv_size; + krb5_storage *rs = krb5_storage_from_mem(buf, sizeof(rodc_id)); if (rs == NULL) ret = krb5_enomem(context); else krb5_storage_set_flags(rs, KRB5_STORAGE_BYTEORDER_LE); if (ret == 0) ret = krb5_store_uint16(rs, rodc_id); - if (ret == 0) - ret = krb5_storage_to_data(rs, &rd); krb5_storage_free(rs); - if (ret) - goto out; - heim_assert(rd.length == sizeof(rodc_id), "invalid length"); - memcpy((char *)d.data + priv_offset + priv_size, rd.data, rd.length); - krb5_data_free(&rd); } if (ret) @@ -1975,6 +2086,7 @@ _krb5_kdc_pac_sign_ticket(krb5_context context, krb5_const_principal upn, krb5_const_principal canon_name, krb5_boolean add_ticket_sig, + krb5_boolean add_full_sig, EncTicketPart *tkt, uint64_t *pac_attributes) /* optional */ { @@ -2012,6 +2124,7 @@ _krb5_kdc_pac_sign_ticket(krb5_context context, ret = _krb5_pac_sign(context, pac, tkt->authtime, client, server_key, kdc_key, rodc_id, upn, canon_name, + add_full_sig, pac_attributes, &rspac); if (ret == 0) ret = _kdc_tkt_insert_pac(context, tkt, &rspac); |