From e195e1e8571980edcf6e5e4b8e6185f3ae92363b Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 15:39:19 +1200 Subject: [PATCH 001/159] krb5pac.idl: Add ticket checksum PAC buffer type Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit ff2f38fae79220e16765e17671972f9a55eb7cce) --- librpc/idl/krb5pac.idl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/librpc/idl/krb5pac.idl b/librpc/idl/krb5pac.idl index fb360c1257f..3239d7656b6 100644 --- a/librpc/idl/krb5pac.idl +++ b/librpc/idl/krb5pac.idl @@ -112,7 +112,8 @@ interface krb5pac PAC_TYPE_KDC_CHECKSUM = 7, PAC_TYPE_LOGON_NAME = 10, PAC_TYPE_CONSTRAINED_DELEGATION = 11, - PAC_TYPE_UPN_DNS_INFO = 12 + PAC_TYPE_UPN_DNS_INFO = 12, + PAC_TYPE_TICKET_CHECKSUM = 16 } PAC_TYPE; typedef struct { @@ -128,6 +129,7 @@ interface krb5pac [case(PAC_TYPE_CONSTRAINED_DELEGATION)][subcontext(0xFFFFFC01)] PAC_CONSTRAINED_DELEGATION_CTR constrained_delegation; [case(PAC_TYPE_UPN_DNS_INFO)] PAC_UPN_DNS_INFO upn_dns_info; + [case(PAC_TYPE_TICKET_CHECKSUM)] PAC_SIGNATURE_DATA ticket_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 -- 2.25.1 From ca85c935655c243292eaeba06e303a5d906df09d Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 15:40:59 +1200 Subject: [PATCH 002/159] security.idl: Add well-known SIDs for FAST Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 0092b4a3ed58b2c256d4dd9117cce927a3edde12) --- librpc/idl/security.idl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/librpc/idl/security.idl b/librpc/idl/security.idl index 06bf7449a70..3df96dedbdd 100644 --- a/librpc/idl/security.idl +++ b/librpc/idl/security.idl @@ -295,6 +295,9 @@ interface security const string SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY = "S-1-18-1"; const string SID_SERVICE_ASSERTED_IDENTITY = "S-1-18-2"; + const string SID_COMPOUNDED_AUTHENTICATION = "S-1-5-21-0-0-0-496"; + const string SID_CLAIMS_VALID = "S-1-5-21-0-0-0-497"; + /* * http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ -- 2.25.1 From 0b6008fdf60e3d77cad9eff00c45ddb153e720e4 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 15:46:42 +1200 Subject: [PATCH 003/159] tests/krb5: Calculate expected salt if not given explicitly Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit c6badf818e9db44461979a931c74fc5ab6e80132) --- python/samba/tests/krb5/as_req_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/as_req_tests.py b/python/samba/tests/krb5/as_req_tests.py index 82ff3f4845c..09160bf6814 100755 --- a/python/samba/tests/krb5/as_req_tests.py +++ b/python/samba/tests/krb5/as_req_tests.py @@ -74,7 +74,7 @@ class AsReqKerberosTests(KDCBaseTest): expected_cname = cname expected_srealm = realm expected_sname = sname - expected_salt = client_creds.get_forced_salt() + expected_salt = client_creds.get_salt() if any(etype in client_as_etypes and etype in initial_etypes for etype in (kcrypto.Enctype.AES256, @@ -142,7 +142,7 @@ class AsReqKerberosTests(KDCBaseTest): expected_cname = cname expected_srealm = realm expected_sname = sname - expected_salt = client_creds.get_forced_salt() + expected_salt = client_creds.get_salt() till = self.get_KerberosTime(offset=36000) -- 2.25.1 From 88e37a8ff8fcc2d2c48a284e42af72f91972150f Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 15:50:26 +1200 Subject: [PATCH 004/159] tests/krb5: Add methods to obtain the length of checksum types Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 9924dd976183ea62b08f116f8b8bacc698bb9b95) --- python/samba/tests/krb5/kcrypto.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/python/samba/tests/krb5/kcrypto.py b/python/samba/tests/krb5/kcrypto.py index ce7b00bda4c..4a4a12a66d4 100755 --- a/python/samba/tests/krb5/kcrypto.py +++ b/python/samba/tests/krb5/kcrypto.py @@ -478,6 +478,7 @@ class _ChecksumProfile(object): # define: # * checksum # * verify (if verification is not just checksum-and-compare) + # * checksum_len @classmethod def verify(cls, key, keyusage, text, cksum): expected = cls.checksum(key, keyusage, text) @@ -504,6 +505,10 @@ class _SimplifiedChecksum(_ChecksumProfile): raise ValueError('Wrong key type for checksum') super(_SimplifiedChecksum, cls).verify(key, keyusage, text, cksum) + @classmethod + def checksum_len(cls): + return cls.macsize + class _SHA1AES128(_SimplifiedChecksum): macsize = 12 @@ -533,6 +538,10 @@ class _HMACMD5(_ChecksumProfile): raise ValueError('Wrong key type for checksum') super(_HMACMD5, cls).verify(key, keyusage, text, cksum) + @classmethod + def checksum_len(cls): + return hashes.MD5.digest_size + class _MD5(_ChecksumProfile): @classmethod @@ -540,6 +549,10 @@ class _MD5(_ChecksumProfile): # This is unkeyed! return SIMPLE_HASH(text, hashes.MD5) + @classmethod + def checksum_len(cls): + return hashes.MD5.digest_size + class _SHA1(_ChecksumProfile): @classmethod @@ -547,6 +560,10 @@ class _SHA1(_ChecksumProfile): # This is unkeyed! return SIMPLE_HASH(text, hashes.SHA1) + @classmethod + def checksum_len(cls): + return hashes.SHA1.digest_size + class _CRC32(_ChecksumProfile): @classmethod @@ -555,6 +572,10 @@ class _CRC32(_ChecksumProfile): cksum = (~crc32(text, 0xffffffff)) & 0xffffffff return pack(' Date: Wed, 1 Sep 2021 15:57:26 +1200 Subject: [PATCH 005/159] tests/krb5: Use signed integers to represent key version numbers in ASN.1 As specified in 'MS-KILE 3.1.5.8: Key Version Numbers', Windows uses signed 32-bit integers to represent key version numbers. This makes a difference for an RODC with a msDS-SecondaryKrbTgtNumber greater than 32767, where the kvno should be encoded in four bytes rather than five. Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 448b661bf8815a05f534926d8ee8d6f57d123c2c) --- python/samba/tests/krb5/raw_testcase.py | 2 +- python/samba/tests/krb5/rfc4120.asn1 | 2 +- python/samba/tests/krb5/rfc4120_pyasn1.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 6db17f2a118..c5ee5eb6083 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -995,7 +995,7 @@ class RawKerberosTest(TestCaseInTempDir): def EncryptedData_create(self, key, usage, plaintext): # EncryptedData ::= SEQUENCE { # etype [0] Int32 -- EncryptionType --, - # kvno [1] UInt32 OPTIONAL, + # kvno [1] Int32 OPTIONAL, # cipher [2] OCTET STRING -- ciphertext # } ciphertext = key.encrypt(usage, plaintext) diff --git a/python/samba/tests/krb5/rfc4120.asn1 b/python/samba/tests/krb5/rfc4120.asn1 index f47c1d00202..a37011ae932 100644 --- a/python/samba/tests/krb5/rfc4120.asn1 +++ b/python/samba/tests/krb5/rfc4120.asn1 @@ -124,7 +124,7 @@ KerberosFlags ::= BIT STRING (SIZE (1..32)) EncryptedData ::= SEQUENCE { etype [0] EncryptionType, --Int32 EncryptionType -- - kvno [1] UInt32 OPTIONAL, + kvno [1] Int32 OPTIONAL, cipher [2] OCTET STRING -- ciphertext } diff --git a/python/samba/tests/krb5/rfc4120_pyasn1.py b/python/samba/tests/krb5/rfc4120_pyasn1.py index 39ec8ed7982..a9e4bcbb18f 100644 --- a/python/samba/tests/krb5/rfc4120_pyasn1.py +++ b/python/samba/tests/krb5/rfc4120_pyasn1.py @@ -120,7 +120,7 @@ class EncryptedData(univ.Sequence): EncryptedData.componentType = namedtype.NamedTypes( namedtype.NamedType('etype', EncryptionType().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), - namedtype.OptionalNamedType('kvno', UInt32().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('kvno', Int32().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), namedtype.NamedType('cipher', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) ) -- 2.25.1 From ed0c2617186d4f2bd1ef709b0f11c0005d112413 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 16:05:39 +1200 Subject: [PATCH 006/159] tests/krb5: Add KDCOptions flag for constrained delegation Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 08086c43987abecc588ebd32ec846ff7e27a83b6) --- python/samba/tests/krb5/rfc4120.asn1 | 1 + python/samba/tests/krb5/rfc4120_pyasn1.py | 1 + 2 files changed, 2 insertions(+) diff --git a/python/samba/tests/krb5/rfc4120.asn1 b/python/samba/tests/krb5/rfc4120.asn1 index a37011ae932..e0831e1f86f 100644 --- a/python/samba/tests/krb5/rfc4120.asn1 +++ b/python/samba/tests/krb5/rfc4120.asn1 @@ -632,6 +632,7 @@ KDCOptionsValues ::= BIT STRING { -- KerberosFlags opt-hardware-auth(11), unused12(12), unused13(13), + cname-in-addl-tkt(14), -- Canonicalize is used by RFC 6806 canonicalize(15), -- 26 was unused in 1510 diff --git a/python/samba/tests/krb5/rfc4120_pyasn1.py b/python/samba/tests/krb5/rfc4120_pyasn1.py index a9e4bcbb18f..348dd8c63fb 100644 --- a/python/samba/tests/krb5/rfc4120_pyasn1.py +++ b/python/samba/tests/krb5/rfc4120_pyasn1.py @@ -649,6 +649,7 @@ KDCOptionsValues.namedValues = namedval.NamedValues( ('opt-hardware-auth', 11), ('unused12', 12), ('unused13', 13), + ('cname-in-addl-tkt', 14), ('canonicalize', 15), ('disable-transited-check', 26), ('renewable-ok', 27), -- 2.25.1 From 9b856ae2c84fb642ac0c0676175e6067c20d08d9 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 16:21:55 +1200 Subject: [PATCH 007/159] tests/krb5: Use more compact dict lookup Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 3fd73b65a3db405db5a0a82cca6c808763d4f437) --- python/samba/tests/krb5/raw_testcase.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index c5ee5eb6083..0ec0f65c6d6 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -305,7 +305,7 @@ class KerberosCredentials(Credentials): def get_forced_key(self, etype): etype = int(etype) - return self.forced_keys.get(etype, None) + return self.forced_keys.get(etype) def set_forced_salt(self, salt): self.forced_salt = bytes(salt) @@ -830,7 +830,7 @@ class RawKerberosTest(TestCaseInTempDir): self.assertIsNotNone(value) def getElementValue(self, obj, elem): - return obj.get(elem, None) + return obj.get(elem) def assertElementMissing(self, obj, elem): v = self.getElementValue(obj, elem) @@ -942,7 +942,7 @@ class RawKerberosTest(TestCaseInTempDir): def PasswordKey_from_etype_info2(self, creds, etype_info2, kvno=None): e = etype_info2['etype'] - salt = etype_info2.get('salt', None) + salt = etype_info2.get('salt') if e == kcrypto.Enctype.RC4: nthash = creds.get_nt_hash() -- 2.25.1 From 0d1703f344daf86e6914728014ba6b9c5fb4040b Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 16:31:56 +1200 Subject: [PATCH 008/159] tests/krb5: Replace expected_cname_private with expected_anon parameter This is used in the case where the KDC returns 'WELLKNOWN/ANONYMOUS' as the cname, and makes the reply checking logic easier to follow. This also removes the need to fetch the client credentials in the test methods. Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit bf55786fcd9a96daa9002661d6f5d9b3502ed8a7) --- python/samba/tests/krb5/fast_tests.py | 33 +++++------------------- python/samba/tests/krb5/raw_testcase.py | 34 ++++++++++++------------- 2 files changed, 24 insertions(+), 43 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 2d4b69f8590..b371ab617aa 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -49,7 +49,6 @@ from samba.tests.krb5.rfc4120_constants import ( KU_TICKET, NT_PRINCIPAL, NT_SRV_INST, - NT_WELLKNOWN, PADATA_FX_COOKIE, PADATA_FX_FAST, PADATA_PAC_OPTIONS @@ -985,14 +984,6 @@ class FAST_Tests(KDCBaseTest): ]) def test_fast_hide_client_names(self): - user_creds = self.get_client_creds() - user_name = user_creds.get_username() - user_cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, - names=[user_name]) - - expected_cname = self.PrincipalName_create( - name_type=NT_WELLKNOWN, names=['WELLKNOWN', 'ANONYMOUS']) - self._run_test_sequence([ { 'rep_type': KRB_AS_REP, @@ -1001,7 +992,7 @@ class FAST_Tests(KDCBaseTest): 'fast_armor': FX_FAST_ARMOR_AP_REQUEST, 'gen_armor_tgt_fn': self.get_mach_tgt, 'fast_options': '01', # hide client names - 'expected_cname': expected_cname + 'expected_anon': True }, { 'rep_type': KRB_AS_REP, @@ -1011,20 +1002,11 @@ class FAST_Tests(KDCBaseTest): 'fast_armor': FX_FAST_ARMOR_AP_REQUEST, 'gen_armor_tgt_fn': self.get_mach_tgt, 'fast_options': '01', # hide client names - 'expected_cname': expected_cname, - 'expected_cname_private': user_cname + 'expected_anon': True } ]) def test_fast_tgs_hide_client_names(self): - user_creds = self.get_client_creds() - user_name = user_creds.get_username() - user_cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, - names=[user_name]) - - expected_cname = self.PrincipalName_create( - name_type=NT_WELLKNOWN, names=['WELLKNOWN', 'ANONYMOUS']) - self._run_test_sequence([ { 'rep_type': KRB_TGS_REP, @@ -1033,8 +1015,7 @@ class FAST_Tests(KDCBaseTest): 'gen_tgt_fn': self.get_user_tgt, 'fast_armor': None, 'fast_options': '01', # hide client names - 'expected_cname': expected_cname, - 'expected_cname_private': user_cname + 'expected_anon': True } ]) @@ -1216,8 +1197,8 @@ class FAST_Tests(KDCBaseTest): srealm = target_realm expected_cname = kdc_dict.pop('expected_cname', client_cname) - expected_cname_private = kdc_dict.pop('expected_cname_private', - None) + expected_anon = kdc_dict.pop('expected_anon', + False) expected_crealm = kdc_dict.pop('expected_crealm', client_realm) expected_sname = kdc_dict.pop('expected_sname', sname) expected_srealm = kdc_dict.pop('expected_srealm', srealm) @@ -1341,7 +1322,7 @@ class FAST_Tests(KDCBaseTest): kdc_exchange_dict = self.as_exchange_dict( expected_crealm=expected_crealm, expected_cname=expected_cname, - expected_cname_private=expected_cname_private, + expected_anon=expected_anon, expected_srealm=expected_srealm, expected_sname=expected_sname, ticket_decryption_key=krbtgt_decryption_key, @@ -1370,7 +1351,7 @@ class FAST_Tests(KDCBaseTest): kdc_exchange_dict = self.tgs_exchange_dict( expected_crealm=expected_crealm, expected_cname=expected_cname, - expected_cname_private=expected_cname_private, + expected_anon=expected_anon, expected_srealm=expected_srealm, expected_sname=expected_sname, ticket_decryption_key=target_decryption_key, diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 0ec0f65c6d6..e4dbb10d135 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1721,7 +1721,7 @@ class RawKerberosTest(TestCaseInTempDir): def as_exchange_dict(self, expected_crealm=None, expected_cname=None, - expected_cname_private=None, + expected_anon=False, expected_srealm=None, expected_sname=None, ticket_decryption_key=None, @@ -1759,6 +1759,7 @@ class RawKerberosTest(TestCaseInTempDir): 'rep_encpart_asn1Spec': krb5_asn1.EncASRepPart, 'expected_crealm': expected_crealm, 'expected_cname': expected_cname, + 'expected_anon': expected_anon, 'expected_srealm': expected_srealm, 'expected_sname': expected_sname, 'ticket_decryption_key': ticket_decryption_key, @@ -1784,10 +1785,6 @@ class RawKerberosTest(TestCaseInTempDir): 'inner_req': inner_req, 'outer_req': outer_req } - if expected_cname_private is not None: - kdc_exchange_dict['expected_cname_private'] = ( - expected_cname_private) - if callback_dict is None: callback_dict = {} @@ -1796,7 +1793,7 @@ class RawKerberosTest(TestCaseInTempDir): def tgs_exchange_dict(self, expected_crealm=None, expected_cname=None, - expected_cname_private=None, + expected_anon=False, expected_srealm=None, expected_sname=None, ticket_decryption_key=None, @@ -1834,6 +1831,7 @@ class RawKerberosTest(TestCaseInTempDir): 'rep_encpart_asn1Spec': krb5_asn1.EncTGSRepPart, 'expected_crealm': expected_crealm, 'expected_cname': expected_cname, + 'expected_anon': expected_anon, 'expected_srealm': expected_srealm, 'expected_sname': expected_sname, 'ticket_decryption_key': ticket_decryption_key, @@ -1859,10 +1857,6 @@ class RawKerberosTest(TestCaseInTempDir): 'inner_req': inner_req, 'outer_req': outer_req } - if expected_cname_private is not None: - kdc_exchange_dict['expected_cname_private'] = ( - expected_cname_private) - if callback_dict is None: callback_dict = {} @@ -1874,7 +1868,7 @@ class RawKerberosTest(TestCaseInTempDir): rep): expected_crealm = kdc_exchange_dict['expected_crealm'] - expected_cname = kdc_exchange_dict['expected_cname'] + expected_anon = kdc_exchange_dict['expected_anon'] expected_srealm = kdc_exchange_dict['expected_srealm'] expected_sname = kdc_exchange_dict['expected_sname'] ticket_decryption_key = kdc_exchange_dict['ticket_decryption_key'] @@ -1888,6 +1882,12 @@ class RawKerberosTest(TestCaseInTempDir): padata = self.getElementValue(rep, 'padata') if self.strict_checking: self.assertElementEqualUTF8(rep, 'crealm', expected_crealm) + if expected_anon: + expected_cname = self.PrincipalName_create( + name_type=NT_WELLKNOWN, + names=['WELLKNOWN', 'ANONYMOUS']) + else: + expected_cname = kdc_exchange_dict['expected_cname'] self.assertElementEqualPrincipal(rep, 'cname', expected_cname) self.assertElementPresent(rep, 'ticket') ticket = self.getElementValue(rep, 'ticket') @@ -2042,14 +2042,11 @@ class RawKerberosTest(TestCaseInTempDir): and kdc_options[canon_pos] == '1') expected_crealm = kdc_exchange_dict['expected_crealm'] + expected_cname = kdc_exchange_dict['expected_cname'] expected_srealm = kdc_exchange_dict['expected_srealm'] expected_sname = kdc_exchange_dict['expected_sname'] ticket_decryption_key = kdc_exchange_dict['ticket_decryption_key'] - try: - expected_cname = kdc_exchange_dict['expected_cname_private'] - except KeyError: - expected_cname = kdc_exchange_dict['expected_cname'] ticket = self.getElementValue(rep, 'ticket') @@ -2182,7 +2179,7 @@ class RawKerberosTest(TestCaseInTempDir): rep_msg_type = kdc_exchange_dict['rep_msg_type'] - expected_cname = kdc_exchange_dict['expected_cname'] + expected_anon = kdc_exchange_dict['expected_anon'] expected_srealm = kdc_exchange_dict['expected_srealm'] expected_sname = kdc_exchange_dict['expected_sname'] expected_error_mode = kdc_exchange_dict['expected_error_mode'] @@ -2203,7 +2200,10 @@ class RawKerberosTest(TestCaseInTempDir): # error-code checked above if self.strict_checking: self.assertElementMissing(rep, 'crealm') - if expected_cname['name-type'] == NT_WELLKNOWN and not inner: + if expected_anon and not inner: + expected_cname = self.PrincipalName_create( + name_type=NT_WELLKNOWN, + names=['WELLKNOWN', 'ANONYMOUS']) self.assertElementEqualPrincipal(rep, 'cname', expected_cname) else: self.assertElementMissing(rep, 'cname') -- 2.25.1 From 0b1c12cac5d6ec0cd87e8d04843ebc717cb33870 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 16:34:02 +1200 Subject: [PATCH 009/159] tests/krb5: Allow specifying an OU to create accounts in Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 7aae0e9b100b8cb7d1da78b8cb9a4a5c20acffbd) --- python/samba/tests/krb5/kdc_base_test.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index f5c1eba9151..efe11da8468 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -151,12 +151,16 @@ class KDCBaseTest(RawKerberosTest): return default_enctypes def create_account(self, ldb, name, machine_account=False, - spn=None, upn=None, additional_details=None): + spn=None, upn=None, additional_details=None, + ou=None): '''Create an account for testing. The dn of the created account is added to self.accounts, which is used by tearDownClass to clean up the created accounts. ''' - dn = "cn=%s,%s" % (name, ldb.domain_dn()) + if ou is None: + ou = ldb.domain_dn() + + dn = "CN=%s,%s" % (name, ou) # remove the account if it exists, this will happen if a previous test # run failed -- 2.25.1 From 452a870aba4aaf5fb4eb32faa9b1ba129910e85e Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 16:34:46 +1200 Subject: [PATCH 010/159] tests/krb5: Allow specifying additional User Account Control flags for account Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 9aa900857441ea7e1c2d6c60bfa1ddeb142bf3e3) --- python/samba/tests/krb5/kdc_base_test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index efe11da8468..bd5bacfaca1 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -152,7 +152,7 @@ class KDCBaseTest(RawKerberosTest): def create_account(self, ldb, name, machine_account=False, spn=None, upn=None, additional_details=None, - ou=None): + ou=None, account_control=0): '''Create an account for testing. The dn of the created account is added to self.accounts, which is used by tearDownClass to clean up the created accounts. @@ -168,11 +168,11 @@ class KDCBaseTest(RawKerberosTest): if machine_account: object_class = "computer" account_name = "%s$" % name - account_control = str(UF_WORKSTATION_TRUST_ACCOUNT) + account_control |= UF_WORKSTATION_TRUST_ACCOUNT else: object_class = "user" account_name = name - account_control = str(UF_NORMAL_ACCOUNT) + account_control |= UF_NORMAL_ACCOUNT password = generate_random_password(32, 32) utf16pw = ('"%s"' % password).encode('utf-16-le') @@ -181,7 +181,7 @@ class KDCBaseTest(RawKerberosTest): "dn": dn, "objectclass": object_class, "sAMAccountName": account_name, - "userAccountControl": account_control, + "userAccountControl": str(account_control), "unicodePwd": utf16pw} if spn is not None: details["servicePrincipalName"] = spn -- 2.25.1 From 3c2388566f21a640aebb9cf662026b1b1f70505f Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 16:35:58 +1200 Subject: [PATCH 011/159] tests/krb5: Keep track of account DN in credentials object Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 9973b51e48a5d5f3e33c6e0da46e6231a42bd77a) --- python/samba/tests/krb5/kdc_base_test.py | 2 ++ python/samba/tests/krb5/raw_testcase.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index bd5bacfaca1..b52452358cc 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -201,6 +201,7 @@ class KDCBaseTest(RawKerberosTest): creds.set_workstation(name) else: creds.set_workstation('') + creds.set_dn(dn) # # Save the account name so it can be deleted in tearDownClass self.accounts.add(dn) @@ -441,6 +442,7 @@ class KDCBaseTest(RawKerberosTest): kvno = int(res[0]['msDS-KeyVersionNumber'][0]) creds.set_kvno(kvno) + creds.set_dn(dn) keys = self.get_keys(samdb, dn) self.creds_set_keys(creds, keys) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index e4dbb10d135..e62fad3d187 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -261,6 +261,8 @@ class KerberosCredentials(Credentials): self.forced_salt = None + self.dn = None + def set_as_supported_enctypes(self, value): self.as_supported_enctypes = int(value) @@ -327,6 +329,12 @@ class KerberosCredentials(Credentials): return salt_string.encode('utf-8') + def set_dn(self, dn): + self.dn = dn + + def get_dn(self): + return self.dn + class KerberosTicketCreds: def __init__(self, ticket, session_key, -- 2.25.1 From 08f7c62550d3fd45e3e067437cf35f67a32ea6c6 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 2 Sep 2021 14:27:00 +1200 Subject: [PATCH 012/159] tests/krb5: Move padata generation methods to base class This allows them to be used directly from RawKerberosTest. Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 1f23b16ef3a900a1bda01bf2a5a3a3847e2e79d1) --- python/samba/tests/krb5/fast_tests.py | 14 -------------- python/samba/tests/krb5/raw_testcase.py | 13 +++++++++++++ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index b371ab617aa..4fc297c1e34 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -51,7 +51,6 @@ from samba.tests.krb5.rfc4120_constants import ( NT_SRV_INST, PADATA_FX_COOKIE, PADATA_FX_FAST, - PADATA_PAC_OPTIONS ) import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1 import samba.tests.krb5.kcrypto as kcrypto @@ -1466,19 +1465,6 @@ class FAST_Tests(KDCBaseTest): return self.PA_DATA_create(PADATA_FX_COOKIE, cookie) - def get_pa_pac_request(self, request_pac=True): - pac_request = self.KERB_PA_PAC_REQUEST_create(request_pac) - - return pac_request - - def get_pa_pac_options(self, options): - pac_options = self.PA_PAC_OPTIONS_create(options) - pac_options = self.der_encode(pac_options, - asn1Spec=krb5_asn1.PA_PAC_OPTIONS()) - pac_options = self.PA_DATA_create(PADATA_PAC_OPTIONS, pac_options) - - return pac_options - def check_kdc_fast_support(self): # Check that the KDC supports FAST diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index e62fad3d187..b724baf5cf8 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1151,6 +1151,19 @@ class RawKerberosTest(TestCaseInTempDir): pa_data = self.PA_DATA_create(PADATA_PAC_REQUEST, pa_pac) return pa_data + def get_pa_pac_request(self, request_pac=True): + pac_request = self.KERB_PA_PAC_REQUEST_create(request_pac) + + return pac_request + + def get_pa_pac_options(self, options): + pac_options = self.PA_PAC_OPTIONS_create(options) + pac_options = self.der_encode(pac_options, + asn1Spec=krb5_asn1.PA_PAC_OPTIONS()) + pac_options = self.PA_DATA_create(PADATA_PAC_OPTIONS, pac_options) + + return pac_options + def KDC_REQ_BODY_create(self, kdc_options, cname, -- 2.25.1 From 78407dac18d71204f1b2d3b1a734dfff92be24cc Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 2 Sep 2021 14:36:42 +1200 Subject: [PATCH 013/159] tests/krb5: add options to kdc_exchange_dict to specify including PAC-REQUEST or PAC-OPTIONS Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit c0db1ba54d238d4b2da8895215d8314b068ce09c) --- python/samba/tests/krb5/raw_testcase.py | 40 +++++++++++++++++++++---- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index b724baf5cf8..58f246606d7 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1574,6 +1574,9 @@ class RawKerberosTest(TestCaseInTempDir): expected_error_mode = kdc_exchange_dict['expected_error_mode'] kdc_options = kdc_exchange_dict['kdc_options'] + pac_request = kdc_exchange_dict['pac_request'] + pac_options = kdc_exchange_dict['pac_options'] + # Parameters specific to the inner request body inner_req = kdc_exchange_dict['inner_req'] @@ -1619,6 +1622,14 @@ class RawKerberosTest(TestCaseInTempDir): else: del req_body[key] + additional_padata = [] + if pac_request is not None: + pa_pac_request = self.KERB_PA_PAC_REQUEST_create(pac_request) + additional_padata.append(pa_pac_request) + if pac_options is not None: + pa_pac_options = self.get_pa_pac_options(pac_options) + additional_padata.append(pa_pac_options) + if req_msg_type == KRB_AS_REQ: tgs_req = None tgs_req_padata = None @@ -1637,6 +1648,8 @@ class RawKerberosTest(TestCaseInTempDir): fast_padata, req_body = generate_fast_padata_fn(kdc_exchange_dict, callback_dict, req_body) + + fast_padata += additional_padata else: fast_padata = [] @@ -1701,6 +1714,9 @@ class RawKerberosTest(TestCaseInTempDir): if outer_padata is not None: padata += outer_padata + if fast is None: + padata += additional_padata + if not padata: padata = None @@ -1766,7 +1782,9 @@ class RawKerberosTest(TestCaseInTempDir): auth_data=None, kdc_options='', inner_req=None, - outer_req=None): + outer_req=None, + pac_request=None, + pac_options=None): if expected_error_mode == 0: expected_error_mode = () elif not isinstance(expected_error_mode, collections.abc.Container): @@ -1804,7 +1822,9 @@ class RawKerberosTest(TestCaseInTempDir): 'auth_data': auth_data, 'kdc_options': kdc_options, 'inner_req': inner_req, - 'outer_req': outer_req + 'outer_req': outer_req, + 'pac_request': pac_request, + 'pac_options': pac_options } if callback_dict is None: callback_dict = {} @@ -1838,7 +1858,9 @@ class RawKerberosTest(TestCaseInTempDir): body_checksum_type=None, kdc_options='', inner_req=None, - outer_req=None): + outer_req=None, + pac_request=None, + pac_options=None): if expected_error_mode == 0: expected_error_mode = () elif not isinstance(expected_error_mode, collections.abc.Container): @@ -1876,7 +1898,9 @@ class RawKerberosTest(TestCaseInTempDir): 'authenticator_subkey': authenticator_subkey, 'kdc_options': kdc_options, 'inner_req': inner_req, - 'outer_req': outer_req + 'outer_req': outer_req, + 'pac_request': pac_request, + 'pac_options': pac_options } if callback_dict is None: callback_dict = {} @@ -2820,7 +2844,9 @@ class RawKerberosTest(TestCaseInTempDir): padata, kdc_options, preauth_key=None, - ticket_decryption_key=None): + ticket_decryption_key=None, + pac_request=None, + pac_options=None): def _generate_padata_copy(_kdc_exchange_dict, _callback_dict, @@ -2860,7 +2886,9 @@ class RawKerberosTest(TestCaseInTempDir): expected_error_mode=expected_error_mode, client_as_etypes=client_as_etypes, expected_salt=expected_salt, - kdc_options=str(kdc_options)) + kdc_options=str(kdc_options), + pac_request=pac_request, + pac_options=pac_options) rep = self._generic_kdc_exchange(kdc_exchange_dict, cname=cname, -- 2.25.1 From 9af5f1bb152b716f786a26948970d794988ff1a4 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 2 Sep 2021 14:37:27 +1200 Subject: [PATCH 014/159] tests/krb5: Don't create PAC request manually in as_req_tests Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit bc21ba2592093c765751ed3e8083dcd3512997f8) --- python/samba/tests/krb5/as_req_tests.py | 35 ++++++++----------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/python/samba/tests/krb5/as_req_tests.py b/python/samba/tests/krb5/as_req_tests.py index 09160bf6814..35f88a0c920 100755 --- a/python/samba/tests/krb5/as_req_tests.py +++ b/python/samba/tests/krb5/as_req_tests.py @@ -56,7 +56,7 @@ class AsReqKerberosTests(KDCBaseTest): def _test_as_req_nopreauth(self, initial_etypes, - initial_padata=None, + pac=None, initial_kdc_options=None): client_creds = self.get_client_creds() client_account = client_creds.get_username() @@ -84,27 +84,19 @@ class AsReqKerberosTests(KDCBaseTest): else: expected_error_mode = KDC_ERR_ETYPE_NOSUPP - def _generate_padata_copy(_kdc_exchange_dict, - _callback_dict, - req_body): - return initial_padata, req_body - - generate_padata_fn = (_generate_padata_copy - if initial_padata is not None - else None) - kdc_exchange_dict = self.as_exchange_dict( expected_crealm=expected_crealm, expected_cname=expected_cname, expected_srealm=expected_srealm, expected_sname=expected_sname, - generate_padata_fn=generate_padata_fn, + generate_padata_fn=None, check_error_fn=self.generic_check_kdc_error, check_rep_fn=None, expected_error_mode=expected_error_mode, client_as_etypes=client_as_etypes, expected_salt=expected_salt, - kdc_options=str(initial_kdc_options)) + kdc_options=str(initial_kdc_options), + pac_request=pac) self._generic_kdc_exchange(kdc_exchange_dict, cname=cname, @@ -114,13 +106,8 @@ class AsReqKerberosTests(KDCBaseTest): def _test_as_req_no_preauth_with_args(self, etype_idx, pac): name, etypes = self.etype_test_permutation_by_idx(etype_idx) - if pac is None: - padata = None - else: - pa_pac = self.KERB_PA_PAC_REQUEST_create(pac) - padata = [pa_pac] self._test_as_req_nopreauth( - initial_padata=padata, + pac=pac, initial_etypes=etypes, initial_kdc_options=krb5_asn1.KDCOptions('forwardable')) @@ -146,8 +133,6 @@ class AsReqKerberosTests(KDCBaseTest): till = self.get_KerberosTime(offset=36000) - pa_pac = self.KERB_PA_PAC_REQUEST_create(True) - initial_padata = [pa_pac] initial_etypes = client_as_etypes initial_kdc_options = krb5_asn1.KDCOptions('forwardable') initial_error_mode = KDC_ERR_PREAUTH_REQUIRED @@ -164,8 +149,9 @@ class AsReqKerberosTests(KDCBaseTest): expected_sname, expected_salt, initial_etypes, - initial_padata, - initial_kdc_options) + None, + initial_kdc_options, + pac_request=True) etype_info2 = kdc_exchange_dict['preauth_etype_info2'] self.assertIsNotNone(etype_info2) @@ -183,7 +169,7 @@ class AsReqKerberosTests(KDCBaseTest): pa_ts = self.PA_DATA_create(PADATA_ENC_TIMESTAMP, pa_ts) - preauth_padata = [pa_ts, pa_pac] + preauth_padata = [pa_ts] preauth_etypes = client_as_etypes preauth_kdc_options = krb5_asn1.KDCOptions('forwardable') preauth_error_mode = 0 # AS-REP @@ -207,7 +193,8 @@ class AsReqKerberosTests(KDCBaseTest): preauth_padata, preauth_kdc_options, preauth_key=preauth_key, - ticket_decryption_key=krbtgt_decryption_key) + ticket_decryption_key=krbtgt_decryption_key, + pac_request=True) self.assertIsNotNone(as_rep) if __name__ == "__main__": -- 2.25.1 From d4e20807271e4d220576ecb9c8f9181f5239f03f Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 2 Sep 2021 14:38:33 +1200 Subject: [PATCH 015/159] tests/krb5: Don't create PAC request or options manually in fast_tests Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 7556a4dfa64650939aef14a2fc4d10b9ed3d29f7) --- python/samba/tests/krb5/fast_tests.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 4fc297c1e34..e10db90a57e 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1093,8 +1093,6 @@ class FAST_Tests(KDCBaseTest): 'canonicalize,' 'renewable-ok')) - pac_request = self.get_pa_pac_request() - client_creds = self.get_client_creds() target_creds = self.get_service_creds() krbtgt_creds = self.get_krbtgt_creds() @@ -1250,7 +1248,7 @@ class FAST_Tests(KDCBaseTest): _callback_dict, req_body, padata): - return padata, req_body + return list(padata), req_body def _check_padata_preauth_key(_kdc_exchange_dict, _callback_dict, @@ -1260,15 +1258,9 @@ class FAST_Tests(KDCBaseTest): return preauth_key, as_rep_usage pac_options = kdc_dict.pop('pac_options', '1') # claims support - pac_options = self.get_pa_pac_options(pac_options) kdc_options = kdc_dict.pop('kdc_options', kdc_options_default) - if rep_type == KRB_AS_REP: - padata = [pac_request, pac_options] - else: - padata = [pac_options] - gen_padata_fn = kdc_dict.pop('gen_padata_fn', None) if gen_padata_fn is not None: self.assertEqual(KRB_AS_REP, rep_type) @@ -1278,10 +1270,10 @@ class FAST_Tests(KDCBaseTest): client_creds, preauth_etype_info2[0], client_creds.get_kvno()) - gen_padata = gen_padata_fn(preauth_key, armor_key) - padata.insert(0, gen_padata) + padata = [gen_padata_fn(preauth_key, armor_key)] else: preauth_key = None + padata = [] if rep_type == KRB_AS_REP: check_padata_fn = _check_padata_preauth_key @@ -1345,7 +1337,9 @@ class FAST_Tests(KDCBaseTest): armor_subkey=armor_subkey, kdc_options=kdc_options, inner_req=inner_req, - outer_req=outer_req) + outer_req=outer_req, + pac_request=True, + pac_options=pac_options) else: # KRB_TGS_REP kdc_exchange_dict = self.tgs_exchange_dict( expected_crealm=expected_crealm, @@ -1374,7 +1368,9 @@ class FAST_Tests(KDCBaseTest): body_checksum_type=None, kdc_options=kdc_options, inner_req=inner_req, - outer_req=outer_req) + outer_req=outer_req, + pac_request=None, + pac_options=pac_options) repeat = kdc_dict.pop('repeat', 1) for _ in range(repeat): -- 2.25.1 From 1c4fe313794c2714fb6c788d669540a666fc7415 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 17:46:02 +1200 Subject: [PATCH 016/159] tests/krb5: Remove magic constants Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 571265257f335ba7f6f1b46daa0d657b8a8dff2b) --- python/samba/tests/krb5/fast_tests.py | 2 +- python/samba/tests/krb5/kdc_base_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index e10db90a57e..29a666aad5e 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1466,7 +1466,7 @@ class FAST_Tests(KDCBaseTest): samdb = self.get_samdb() - krbtgt_rid = 502 + krbtgt_rid = security.DOMAIN_RID_KRBTGT krbtgt_sid = '%s-%d' % (samdb.get_domain_sid(), krbtgt_rid) res = samdb.search(base='' % krbtgt_sid, diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index b52452358cc..ac43b2eae1a 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -425,7 +425,7 @@ class KDCBaseTest(RawKerberosTest): def download_krbtgt_creds(): samdb = self.get_samdb() - krbtgt_rid = 502 + krbtgt_rid = security.DOMAIN_RID_KRBTGT krbtgt_sid = '%s-%d' % (samdb.get_domain_sid(), krbtgt_rid) res = samdb.search(base='' % krbtgt_sid, -- 2.25.1 From c4b81188694c96d94ab1a8454a2cb4b145b6da8a Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 19:13:11 +1200 Subject: [PATCH 017/159] tests/krb5: Allow specifying ticket flags expected to be set or reset Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 85ddfc1afcf21797dab15431a5f375444c4d316e) --- python/samba/tests/krb5/fast_tests.py | 11 +++++++ python/samba/tests/krb5/raw_testcase.py | 40 +++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 29a666aad5e..687f7532a64 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1309,6 +1309,13 @@ class FAST_Tests(KDCBaseTest): inner_req = kdc_dict.pop('inner_req', None) outer_req = kdc_dict.pop('outer_req', None) + expected_flags = kdc_dict.pop('expected_flags', None) + if expected_flags is not None: + expected_flags = krb5_asn1.KDCOptions(expected_flags) + unexpected_flags = kdc_dict.pop('unexpected_flags', None) + if unexpected_flags is not None: + unexpected_flags = krb5_asn1.KDCOptions(unexpected_flags) + if rep_type == KRB_AS_REP: kdc_exchange_dict = self.as_exchange_dict( expected_crealm=expected_crealm, @@ -1316,6 +1323,8 @@ class FAST_Tests(KDCBaseTest): expected_anon=expected_anon, expected_srealm=expected_srealm, expected_sname=expected_sname, + expected_flags=expected_flags, + unexpected_flags=unexpected_flags, ticket_decryption_key=krbtgt_decryption_key, generate_fast_fn=generate_fast_fn, generate_fast_armor_fn=generate_fast_armor_fn, @@ -1347,6 +1356,8 @@ class FAST_Tests(KDCBaseTest): expected_anon=expected_anon, expected_srealm=expected_srealm, expected_sname=expected_sname, + expected_flags=expected_flags, + unexpected_flags=unexpected_flags, ticket_decryption_key=target_decryption_key, generate_fast_fn=generate_fast_fn, generate_fast_armor_fn=generate_fast_armor_fn, diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 58f246606d7..268f6ccc6bb 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -896,6 +896,24 @@ class RawKerberosTest(TestCaseInTempDir): else: self.assertIsNone(v) + def assertElementFlags(self, obj, elem, expected, unexpected): + v = self.getElementValue(obj, elem) + self.assertIsNotNone(v) + if expected is not None: + self.assertIsInstance(expected, krb5_asn1.KDCOptions) + for i, flag in enumerate(expected): + if flag == 1: + self.assertEqual('1', v[i], + f"'{expected.namedValues[i]}' " + f"expected in {v}") + if unexpected is not None: + self.assertIsInstance(unexpected, krb5_asn1.KDCOptions) + for i, flag in enumerate(unexpected): + if flag == 1: + self.assertEqual('0', v[i], + f"'{unexpected.namedValues[i]}' " + f"unexpected in {v}") + def get_KerberosTimeWithUsec(self, epoch=None, offset=None): if epoch is None: epoch = time.time() @@ -1761,6 +1779,8 @@ class RawKerberosTest(TestCaseInTempDir): expected_anon=False, expected_srealm=None, expected_sname=None, + expected_flags=None, + unexpected_flags=None, ticket_decryption_key=None, generate_fast_fn=None, generate_fast_armor_fn=None, @@ -1801,6 +1821,8 @@ class RawKerberosTest(TestCaseInTempDir): 'expected_anon': expected_anon, 'expected_srealm': expected_srealm, 'expected_sname': expected_sname, + 'expected_flags': expected_flags, + 'unexpected_flags': unexpected_flags, 'ticket_decryption_key': ticket_decryption_key, 'generate_fast_fn': generate_fast_fn, 'generate_fast_armor_fn': generate_fast_armor_fn, @@ -1837,6 +1859,8 @@ class RawKerberosTest(TestCaseInTempDir): expected_anon=False, expected_srealm=None, expected_sname=None, + expected_flags=None, + unexpected_flags=None, ticket_decryption_key=None, generate_fast_fn=None, generate_fast_armor_fn=None, @@ -1877,6 +1901,8 @@ class RawKerberosTest(TestCaseInTempDir): 'expected_anon': expected_anon, 'expected_srealm': expected_srealm, 'expected_sname': expected_sname, + 'expected_flags': expected_flags, + 'unexpected_flags': unexpected_flags, 'ticket_decryption_key': ticket_decryption_key, 'generate_fast_fn': generate_fast_fn, 'generate_fast_armor_fn': generate_fast_armor_fn, @@ -2092,6 +2118,8 @@ class RawKerberosTest(TestCaseInTempDir): expected_sname = kdc_exchange_dict['expected_sname'] ticket_decryption_key = kdc_exchange_dict['ticket_decryption_key'] + expected_flags = kdc_exchange_dict.get('expected_flags') + unexpected_flags = kdc_exchange_dict.get('unexpected_flags') ticket = self.getElementValue(rep, 'ticket') @@ -2101,7 +2129,9 @@ class RawKerberosTest(TestCaseInTempDir): ticket_session_key = None if ticket_private is not None: - self.assertElementPresent(ticket_private, 'flags') + self.assertElementFlags(ticket_private, 'flags', + expected_flags, + unexpected_flags) self.assertElementPresent(ticket_private, 'key') ticket_key = self.getElementValue(ticket_private, 'key') self.assertIsNotNone(ticket_key) @@ -2137,7 +2167,9 @@ class RawKerberosTest(TestCaseInTempDir): kdc_exchange_dict['nonce']) # TODO self.assertElementPresent(encpart_private, # 'key-expiration') - self.assertElementPresent(encpart_private, 'flags') + self.assertElementFlags(ticket_private, 'flags', + expected_flags, + unexpected_flags) self.assertElementPresent(encpart_private, 'authtime') if self.strict_checking: self.assertElementPresent(encpart_private, 'starttime') @@ -2843,6 +2875,8 @@ class RawKerberosTest(TestCaseInTempDir): etypes, padata, kdc_options, + expected_flags=None, + unexpected_flags=None, preauth_key=None, ticket_decryption_key=None, pac_request=None, @@ -2886,6 +2920,8 @@ class RawKerberosTest(TestCaseInTempDir): expected_error_mode=expected_error_mode, client_as_etypes=client_as_etypes, expected_salt=expected_salt, + expected_flags=expected_flags, + unexpected_flags=unexpected_flags, kdc_options=str(kdc_options), pac_request=pac_request, pac_options=pac_options) -- 2.25.1 From 4cf6730703b4cf98366b24fbf109e2b9eedd23ea Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 19:15:17 +1200 Subject: [PATCH 018/159] tests/krb5: Make time assertion less strict This assertion could fail if there was a time difference between the KDC and the client. Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 1974b872fb5a7da052305d01e2f1efc8d0637078) --- python/samba/tests/krb5/raw_testcase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 268f6ccc6bb..5ae8fe4ba41 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2559,7 +2559,7 @@ class RawKerberosTest(TestCaseInTempDir): current_time = time.time() self.assertLess(current_time - 300, rep_time) - self.assertLess(rep_time, current_time) + self.assertLess(rep_time, current_time + 300) if all(etype not in client_as_etypes or etype not in proposed_etypes for etype in (kcrypto.Enctype.AES256, -- 2.25.1 From a5cfaf79ee4344bd20544a8298cb5aba9cbdcfe6 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 19:34:20 +1200 Subject: [PATCH 019/159] tests/krb5: Allow Kerberos requests to be sent to DC or RODC If run inside the 'rodc' testing environment, 'DC_SERVER' and 'SERVER' refer to the hostnames of the DC and RODC respectively, and this commit allows either one of them to be used as the KDC for Kerberos exchanges. Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 0afb548a0a3221730c4a81d51bc31e99ec90e334) --- python/samba/tests/krb5/kdc_base_test.py | 2 +- python/samba/tests/krb5/raw_testcase.py | 39 +++++++++++++++--------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index ac43b2eae1a..0755040a87a 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -116,7 +116,7 @@ class KDCBaseTest(RawKerberosTest): lp = self.get_lp() session = system_session() - type(self)._ldb = SamDB(url="ldap://%s" % self.host, + type(self)._ldb = SamDB(url="ldap://%s" % self.dc_host, session_info=session, credentials=creds, lp=lp) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 5ae8fe4ba41..c03600f765b 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -418,6 +418,7 @@ class RawKerberosTest(TestCaseInTempDir): super().setUpClass() cls.host = samba.tests.env_get_var_value('SERVER') + cls.dc_host = samba.tests.env_get_var_value('DC_SERVER') # A dictionary containing credentials that have already been # obtained. @@ -452,10 +453,10 @@ class RawKerberosTest(TestCaseInTempDir): if self.do_hexdump: sys.stderr.write("disconnect[%s]\n" % reason) - def _connect_tcp(self): + def _connect_tcp(self, host): tcp_port = 88 try: - self.a = socket.getaddrinfo(self.host, tcp_port, socket.AF_UNSPEC, + self.a = socket.getaddrinfo(host, tcp_port, socket.AF_UNSPEC, socket.SOCK_STREAM, socket.SOL_TCP, 0) self.s = socket.socket(self.a[0][0], self.a[0][1], self.a[0][2]) @@ -468,11 +469,11 @@ class RawKerberosTest(TestCaseInTempDir): self.s.close() raise - def connect(self): + def connect(self, host): self.assertNotConnected() - self._connect_tcp() + self._connect_tcp(host) if self.do_hexdump: - sys.stderr.write("connected[%s]\n" % self.host) + sys.stderr.write("connected[%s]\n" % host) def env_get_var(self, varname, prefix, fallback_default=True, @@ -819,8 +820,10 @@ class RawKerberosTest(TestCaseInTempDir): req, asn1_print=None, hexdump=None, - timeout=None): - self.connect() + timeout=None, + to_rodc=False): + host = self.host if to_rodc else self.dc_host + self.connect(host) try: self.send_pdu(req, asn1_print=asn1_print, hexdump=hexdump) rep = self.recv_pdu( @@ -1747,7 +1750,9 @@ class RawKerberosTest(TestCaseInTempDir): req_body=req_body, asn1Spec=req_asn1Spec()) - rep = self.send_recv_transaction(req_decoded) + to_rodc = kdc_exchange_dict['to_rodc'] + + rep = self.send_recv_transaction(req_decoded, to_rodc=to_rodc) self.assertIsNotNone(rep) msg_type = self.getElementValue(rep, 'msg-type') @@ -1804,7 +1809,8 @@ class RawKerberosTest(TestCaseInTempDir): inner_req=None, outer_req=None, pac_request=None, - pac_options=None): + pac_options=None, + to_rodc=False): if expected_error_mode == 0: expected_error_mode = () elif not isinstance(expected_error_mode, collections.abc.Container): @@ -1846,7 +1852,8 @@ class RawKerberosTest(TestCaseInTempDir): 'inner_req': inner_req, 'outer_req': outer_req, 'pac_request': pac_request, - 'pac_options': pac_options + 'pac_options': pac_options, + 'to_rodc': to_rodc } if callback_dict is None: callback_dict = {} @@ -1884,7 +1891,8 @@ class RawKerberosTest(TestCaseInTempDir): inner_req=None, outer_req=None, pac_request=None, - pac_options=None): + pac_options=None, + to_rodc=False): if expected_error_mode == 0: expected_error_mode = () elif not isinstance(expected_error_mode, collections.abc.Container): @@ -1926,7 +1934,8 @@ class RawKerberosTest(TestCaseInTempDir): 'inner_req': inner_req, 'outer_req': outer_req, 'pac_request': pac_request, - 'pac_options': pac_options + 'pac_options': pac_options, + 'to_rodc': to_rodc } if callback_dict is None: callback_dict = {} @@ -2880,7 +2889,8 @@ class RawKerberosTest(TestCaseInTempDir): preauth_key=None, ticket_decryption_key=None, pac_request=None, - pac_options=None): + pac_options=None, + to_rodc=False): def _generate_padata_copy(_kdc_exchange_dict, _callback_dict, @@ -2924,7 +2934,8 @@ class RawKerberosTest(TestCaseInTempDir): unexpected_flags=unexpected_flags, kdc_options=str(kdc_options), pac_request=pac_request, - pac_options=pac_options) + pac_options=pac_options, + to_rodc=to_rodc) rep = self._generic_kdc_exchange(kdc_exchange_dict, cname=cname, -- 2.25.1 From d39c7bc563486e4cdb0b93cba16be3037fa916f4 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 19:43:41 +1200 Subject: [PATCH 020/159] tests/krb5: Check for presence of 'renew-till' element Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 9cba5f9a1b098e49315e2e3d4c0b626884c04a64) --- python/samba/tests/krb5/raw_testcase.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index c03600f765b..45ce3c092ad 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2120,6 +2120,9 @@ class RawKerberosTest(TestCaseInTempDir): canon_pos = len(tuple(krb5_asn1.KDCOptions('canonicalize'))) - 1 canonicalize = (canon_pos < len(kdc_options) and kdc_options[canon_pos] == '1') + renewable_pos = len(tuple(krb5_asn1.KDCOptions('renewable'))) - 1 + renewable = (renewable_pos < len(kdc_options) + and kdc_options[renewable_pos] == '1') expected_crealm = kdc_exchange_dict['expected_crealm'] expected_cname = kdc_exchange_dict['expected_cname'] @@ -2158,7 +2161,11 @@ class RawKerberosTest(TestCaseInTempDir): if self.strict_checking: self.assertElementPresent(ticket_private, 'starttime') self.assertElementPresent(ticket_private, 'endtime') - # TODO self.assertElementPresent(ticket_private, 'renew-till') + if renewable: + if self.strict_checking: + self.assertElementPresent(ticket_private, 'renew-till') + else: + self.assertElementMissing(ticket_private, 'renew-till') # TODO self.assertElementMissing(ticket_private, 'caddr') self.assertElementPresent(ticket_private, 'authorization-data') @@ -2183,7 +2190,11 @@ class RawKerberosTest(TestCaseInTempDir): if self.strict_checking: self.assertElementPresent(encpart_private, 'starttime') self.assertElementPresent(encpart_private, 'endtime') - # TODO self.assertElementPresent(encpart_private, 'renew-till') + if renewable: + if self.strict_checking: + self.assertElementPresent(encpart_private, 'renew-till') + else: + self.assertElementMissing(encpart_private, 'renew-till') self.assertElementEqualUTF8(encpart_private, 'srealm', expected_srealm) self.assertElementEqualPrincipal(encpart_private, 'sname', -- 2.25.1 From c20a5a1c9a2470946db2f76aff10775b07a8d2a7 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 19:45:57 +1200 Subject: [PATCH 021/159] tests/krb5: Check 'caddr' element Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit d3106a8d35225e826d548d3bea0d42edc3998c38) --- python/samba/tests/krb5/raw_testcase.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 45ce3c092ad..9e47897dd76 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2166,7 +2166,8 @@ class RawKerberosTest(TestCaseInTempDir): self.assertElementPresent(ticket_private, 'renew-till') else: self.assertElementMissing(ticket_private, 'renew-till') - # TODO self.assertElementMissing(ticket_private, 'caddr') + if self.strict_checking: + self.assertElementEqual(ticket_private, 'caddr', []) self.assertElementPresent(ticket_private, 'authorization-data') encpart_session_key = None @@ -2199,7 +2200,8 @@ class RawKerberosTest(TestCaseInTempDir): expected_srealm) self.assertElementEqualPrincipal(encpart_private, 'sname', expected_sname) - # TODO self.assertElementMissing(encpart_private, 'caddr') + if self.strict_checking: + self.assertElementEqual(encpart_private, 'caddr', []) sent_claims = self.sent_claims(kdc_exchange_dict) -- 2.25.1 From 10cf7df564cd14ae98cc6d357ffae783ad732d6a Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 19:47:27 +1200 Subject: [PATCH 022/159] tests/krb5: Check for presence of 'key-expiration' element Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit c3b746290278f7b5c1dea676e3fa28b9f15bcf94) --- python/samba/tests/krb5/raw_testcase.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 9e47897dd76..e754794e48b 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2130,6 +2130,8 @@ class RawKerberosTest(TestCaseInTempDir): expected_sname = kdc_exchange_dict['expected_sname'] ticket_decryption_key = kdc_exchange_dict['ticket_decryption_key'] + rep_msg_type = kdc_exchange_dict['rep_msg_type'] + expected_flags = kdc_exchange_dict.get('expected_flags') unexpected_flags = kdc_exchange_dict.get('unexpected_flags') @@ -2182,8 +2184,13 @@ class RawKerberosTest(TestCaseInTempDir): self.assertElementPresent(encpart_private, 'last-req') self.assertElementEqual(encpart_private, 'nonce', kdc_exchange_dict['nonce']) - # TODO self.assertElementPresent(encpart_private, - # 'key-expiration') + if rep_msg_type == KRB_AS_REP: + if self.strict_checking: + self.assertElementPresent(encpart_private, + 'key-expiration') + else: + self.assertElementMissing(encpart_private, + 'key-expiration') self.assertElementFlags(ticket_private, 'flags', expected_flags, unexpected_flags) -- 2.25.1 From 97931026fa0bd052e243eb7b2415cd09985c50b5 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 3 Sep 2021 09:18:32 +1200 Subject: [PATCH 023/159] tests/krb5: Create testing accounts in appropriate containers Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Isaac Boukris BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Tue Sep 14 00:01:44 UTC 2021 on sn-devel-184 (cherry picked from commit 01378a52a1cf0b6855492673455013d5719be45b) --- python/samba/tests/krb5/kdc_base_test.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 0755040a87a..49a3227c26e 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -34,6 +34,8 @@ from samba.drs_utils import drsuapi_connect from samba.dsdb import ( DS_DOMAIN_FUNCTION_2000, DS_DOMAIN_FUNCTION_2008, + DS_GUID_COMPUTERS_CONTAINER, + DS_GUID_USERS_CONTAINER, UF_WORKSTATION_TRUST_ACCOUNT, UF_NORMAL_ACCOUNT ) @@ -158,7 +160,10 @@ class KDCBaseTest(RawKerberosTest): which is used by tearDownClass to clean up the created accounts. ''' if ou is None: - ou = ldb.domain_dn() + guid = (DS_GUID_COMPUTERS_CONTAINER if machine_account + else DS_GUID_USERS_CONTAINER) + + ou = ldb.get_wellknown_dn(ldb.get_default_basedn(), guid) dn = "CN=%s,%s" % (name, ou) -- 2.25.1 From 2f177f9c43f77595221f80e3b023d7e0e8caabf7 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 19:26:43 +1200 Subject: [PATCH 024/159] tests/krb5: Allow specifying status code to be checked This allows us to check the status code that may be sent in an error reply to a TGS-REQ message. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall (cherry picked from commit 4ba5e82ae53410ec9a0bc7d47b181a88c15d9387) --- python/samba/tests/krb5/raw_testcase.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index e754794e48b..f65811243ba 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1798,6 +1798,7 @@ class RawKerberosTest(TestCaseInTempDir): check_kdc_private_fn=None, callback_dict=None, expected_error_mode=0, + expected_status=None, client_as_etypes=None, expected_salt=None, authenticator_subkey=None, @@ -1841,6 +1842,7 @@ class RawKerberosTest(TestCaseInTempDir): 'check_kdc_private_fn': check_kdc_private_fn, 'callback_dict': callback_dict, 'expected_error_mode': expected_error_mode, + 'expected_status': expected_status, 'client_as_etypes': client_as_etypes, 'expected_salt': expected_salt, 'authenticator_subkey': authenticator_subkey, @@ -1879,6 +1881,7 @@ class RawKerberosTest(TestCaseInTempDir): check_padata_fn=None, check_kdc_private_fn=None, expected_error_mode=0, + expected_status=None, callback_dict=None, tgt=None, armor_key=None, @@ -1923,6 +1926,7 @@ class RawKerberosTest(TestCaseInTempDir): 'check_kdc_private_fn': check_kdc_private_fn, 'callback_dict': callback_dict, 'expected_error_mode': expected_error_mode, + 'expected_status': expected_status, 'tgt': tgt, 'body_checksum_type': body_checksum_type, 'armor_key': armor_key, @@ -2540,7 +2544,12 @@ class RawKerberosTest(TestCaseInTempDir): status = int.from_bytes(pw_salt[:4], 'little') flags = int.from_bytes(pw_salt[8:], 'little') + expected_status = kdc_exchange_dict['expected_status'] + self.assertEqual(expected_status, status) + self.assertEqual(3, flags) + else: + self.assertIsNone(kdc_exchange_dict.get('expected_status')) if enc_challenge is not None: if not sent_enc_challenge: -- 2.25.1 From 4365b3ed5f800f16ffaf3060cf2f74a6c561d7a2 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 3 Sep 2021 09:40:02 +1200 Subject: [PATCH 025/159] tests/krb5: Get expected cname from TGT for TGS-REQ messages BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall (cherry picked from commit a5186f92803009c81eca2957e1bf2eb0ff7b6dff) --- python/samba/tests/krb5/fast_tests.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 687f7532a64..e1ba4628994 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -179,18 +179,12 @@ class FAST_Tests(KDCBaseTest): ]) def test_simple_tgs_wrong_principal(self): - mach_creds = self.get_mach_creds() - mach_name = mach_creds.get_username() - expected_cname = self.PrincipalName_create( - name_type=NT_PRINCIPAL, names=[mach_name]) - self._run_test_sequence([ { 'rep_type': KRB_TGS_REP, 'expected_error_mode': 0, 'use_fast': False, - 'gen_tgt_fn': self.get_mach_tgt, - 'expected_cname': expected_cname + 'gen_tgt_fn': self.get_mach_tgt } ]) @@ -1193,7 +1187,12 @@ class FAST_Tests(KDCBaseTest): else: # KRB_TGS_REP srealm = target_realm - expected_cname = kdc_dict.pop('expected_cname', client_cname) + if rep_type == KRB_TGS_REP: + tgt_cname = tgt.cname + else: + tgt_cname = client_cname + + expected_cname = kdc_dict.pop('expected_cname', tgt_cname) expected_anon = kdc_dict.pop('expected_anon', False) expected_crealm = kdc_dict.pop('expected_crealm', client_realm) -- 2.25.1 From a69fe59d1ca93f35743c332ce3c18e1f86a82bce Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 3 Sep 2021 09:55:10 +1200 Subject: [PATCH 026/159] tests/krb5: Get encpart decryption key from kdc_exchange_dict Instead of using check_padata_fn to get the encpart decryption key, we can get the key from the AS-REQ preauth phase or from the TGT, depending on whether the message is an AS-REQ or a TGS-REQ. This allows removal of check_padata_fn and some duplicated code. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall (cherry picked from commit 0e99382d73f44eed7e19e83e430938d587e762d0) --- python/samba/tests/krb5/fast_tests.py | 18 +--- python/samba/tests/krb5/raw_testcase.py | 122 ++++++++++-------------- 2 files changed, 54 insertions(+), 86 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index e1ba4628994..12235cd439e 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -45,7 +45,6 @@ from samba.tests.krb5.rfc4120_constants import ( KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS, KRB_AS_REP, KRB_TGS_REP, - KU_AS_REP_ENC_PART, KU_TICKET, NT_PRINCIPAL, NT_SRV_INST, @@ -1114,8 +1113,6 @@ class FAST_Tests(KDCBaseTest): fast_cookie = None preauth_etype_info2 = None - preauth_key = None - for kdc_dict in test_sequence: rep_type = kdc_dict.pop('rep_type') self.assertIn(rep_type, (KRB_AS_REP, KRB_TGS_REP)) @@ -1249,13 +1246,6 @@ class FAST_Tests(KDCBaseTest): padata): return list(padata), req_body - def _check_padata_preauth_key(_kdc_exchange_dict, - _callback_dict, - _rep, - _padata): - as_rep_usage = KU_AS_REP_ENC_PART - return preauth_key, as_rep_usage - pac_options = kdc_dict.pop('pac_options', '1') # claims support kdc_options = kdc_dict.pop('kdc_options', kdc_options_default) @@ -1274,11 +1264,6 @@ class FAST_Tests(KDCBaseTest): preauth_key = None padata = [] - if rep_type == KRB_AS_REP: - check_padata_fn = _check_padata_preauth_key - else: - check_padata_fn = self.check_simple_tgs_padata - if use_fast: inner_padata = padata outer_padata = [] @@ -1332,13 +1317,13 @@ class FAST_Tests(KDCBaseTest): generate_padata_fn=generate_padata_fn, check_error_fn=check_error_fn, check_rep_fn=check_rep_fn, - check_padata_fn=check_padata_fn, check_kdc_private_fn=self.generic_check_kdc_private, callback_dict={}, expected_error_mode=expected_error_mode, client_as_etypes=etypes, expected_salt=expected_salt, authenticator_subkey=authenticator_subkey, + preauth_key=preauth_key, auth_data=auth_data, armor_key=armor_key, armor_tgt=armor_tgt, @@ -1365,7 +1350,6 @@ class FAST_Tests(KDCBaseTest): generate_padata_fn=generate_padata_fn, check_error_fn=check_error_fn, check_rep_fn=check_rep_fn, - check_padata_fn=check_padata_fn, check_kdc_private_fn=self.generic_check_kdc_private, expected_error_mode=expected_error_mode, callback_dict={}, diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index f65811243ba..164d06b9788 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1794,7 +1794,6 @@ class RawKerberosTest(TestCaseInTempDir): generate_padata_fn=None, check_error_fn=None, check_rep_fn=None, - check_padata_fn=None, check_kdc_private_fn=None, callback_dict=None, expected_error_mode=0, @@ -1802,6 +1801,7 @@ class RawKerberosTest(TestCaseInTempDir): client_as_etypes=None, expected_salt=None, authenticator_subkey=None, + preauth_key=None, armor_key=None, armor_tgt=None, armor_subkey=None, @@ -1838,7 +1838,6 @@ class RawKerberosTest(TestCaseInTempDir): 'generate_padata_fn': generate_padata_fn, 'check_error_fn': check_error_fn, 'check_rep_fn': check_rep_fn, - 'check_padata_fn': check_padata_fn, 'check_kdc_private_fn': check_kdc_private_fn, 'callback_dict': callback_dict, 'expected_error_mode': expected_error_mode, @@ -1846,6 +1845,7 @@ class RawKerberosTest(TestCaseInTempDir): 'client_as_etypes': client_as_etypes, 'expected_salt': expected_salt, 'authenticator_subkey': authenticator_subkey, + 'preauth_key': preauth_key, 'armor_key': armor_key, 'armor_tgt': armor_tgt, 'armor_subkey': armor_subkey, @@ -1878,7 +1878,6 @@ class RawKerberosTest(TestCaseInTempDir): generate_padata_fn=None, check_error_fn=None, check_rep_fn=None, - check_padata_fn=None, check_kdc_private_fn=None, expected_error_mode=0, expected_status=None, @@ -1922,7 +1921,6 @@ class RawKerberosTest(TestCaseInTempDir): 'generate_padata_fn': generate_padata_fn, 'check_error_fn': check_error_fn, 'check_rep_fn': check_rep_fn, - 'check_padata_fn': check_padata_fn, 'check_kdc_private_fn': check_kdc_private_fn, 'callback_dict': callback_dict, 'expected_error_mode': expected_error_mode, @@ -1956,7 +1954,6 @@ class RawKerberosTest(TestCaseInTempDir): expected_srealm = kdc_exchange_dict['expected_srealm'] expected_sname = kdc_exchange_dict['expected_sname'] ticket_decryption_key = kdc_exchange_dict['ticket_decryption_key'] - check_padata_fn = kdc_exchange_dict['check_padata_fn'] check_kdc_private_fn = kdc_exchange_dict['check_kdc_private_fn'] rep_encpart_asn1Spec = kdc_exchange_dict['rep_encpart_asn1Spec'] msg_type = kdc_exchange_dict['rep_msg_type'] @@ -2004,41 +2001,37 @@ class RawKerberosTest(TestCaseInTempDir): ticket_checksum = None - encpart_decryption_key = None - self.assertIsNotNone(check_padata_fn) - if check_padata_fn is not None: - # See if we can get the decryption key from the preauth phase - encpart_decryption_key, encpart_decryption_usage = ( - check_padata_fn(kdc_exchange_dict, callback_dict, - rep, padata)) - - if armor_key is not None: - pa_dict = self.get_pa_dict(padata) - - if PADATA_FX_FAST in pa_dict: - fx_fast_data = pa_dict[PADATA_FX_FAST] - fast_response = self.check_fx_fast_data(kdc_exchange_dict, - fx_fast_data, - armor_key, - finished=True) - - if 'strengthen-key' in fast_response: - strengthen_key = self.EncryptionKey_import( - fast_response['strengthen-key']) - encpart_decryption_key = ( - self.generate_strengthen_reply_key( - strengthen_key, - encpart_decryption_key)) - - fast_finished = fast_response.get('finished', None) - if fast_finished is not None: - ticket_checksum = fast_finished['ticket-checksum'] - - self.check_rep_padata(kdc_exchange_dict, - callback_dict, - rep, - fast_response['padata'], - error_code=0) + # Get the decryption key for the encrypted part + encpart_decryption_key, encpart_decryption_usage = ( + self.get_preauth_key(kdc_exchange_dict)) + + if armor_key is not None: + pa_dict = self.get_pa_dict(padata) + + if PADATA_FX_FAST in pa_dict: + fx_fast_data = pa_dict[PADATA_FX_FAST] + fast_response = self.check_fx_fast_data(kdc_exchange_dict, + fx_fast_data, + armor_key, + finished=True) + + if 'strengthen-key' in fast_response: + strengthen_key = self.EncryptionKey_import( + fast_response['strengthen-key']) + encpart_decryption_key = ( + self.generate_strengthen_reply_key( + strengthen_key, + encpart_decryption_key)) + + fast_finished = fast_response.get('finished') + if fast_finished is not None: + ticket_checksum = fast_finished['ticket-checksum'] + + self.check_rep_padata(kdc_exchange_dict, + callback_dict, + rep, + fast_response['padata'], + error_code=0) ticket_private = None self.assertIsNotNone(ticket_decryption_key) @@ -2558,13 +2551,7 @@ class RawKerberosTest(TestCaseInTempDir): armor_key = kdc_exchange_dict['armor_key'] self.assertIsNotNone(armor_key) - check_padata_fn = kdc_exchange_dict['check_padata_fn'] - padata = self.getElementValue(rep, 'padata') - self.assertIsNotNone(check_padata_fn) - preauth_key, _ = check_padata_fn(kdc_exchange_dict, - callback_dict, - rep, - padata) + preauth_key, _ = self.get_preauth_key(kdc_exchange_dict) kdc_challenge_key = self.generate_kdc_challenge_key( armor_key, preauth_key) @@ -2790,21 +2777,25 @@ class RawKerberosTest(TestCaseInTempDir): return padata, req_body - def check_simple_tgs_padata(self, - kdc_exchange_dict, - callback_dict, - rep, - padata): - tgt = kdc_exchange_dict['tgt'] - authenticator_subkey = kdc_exchange_dict['authenticator_subkey'] - if authenticator_subkey is not None: - subkey = authenticator_subkey - subkey_usage = KU_TGS_REP_ENC_PART_SUB_KEY - else: - subkey = tgt.session_key - subkey_usage = KU_TGS_REP_ENC_PART_SESSION + def get_preauth_key(self, kdc_exchange_dict): + msg_type = kdc_exchange_dict['rep_msg_type'] + + if msg_type == KRB_AS_REP: + key = kdc_exchange_dict['preauth_key'] + usage = KU_AS_REP_ENC_PART + else: # KRB_TGS_REP + authenticator_subkey = kdc_exchange_dict['authenticator_subkey'] + if authenticator_subkey is not None: + key = authenticator_subkey + usage = KU_TGS_REP_ENC_PART_SUB_KEY + else: + tgt = kdc_exchange_dict['tgt'] + key = tgt.session_key + usage = KU_TGS_REP_ENC_PART_SESSION + + self.assertIsNotNone(key) - return subkey, subkey_usage + return key, usage def generate_armor_key(self, subkey, session_key): armor_key = kcrypto.cf2(subkey.key, @@ -2926,13 +2917,6 @@ class RawKerberosTest(TestCaseInTempDir): req_body): return padata, req_body - def _check_padata_preauth_key(_kdc_exchange_dict, - _callback_dict, - rep, - padata): - as_rep_usage = KU_AS_REP_ENC_PART - return preauth_key, as_rep_usage - if not expected_error_mode: check_error_fn = None check_rep_fn = self.generic_check_kdc_rep @@ -2954,13 +2938,13 @@ class RawKerberosTest(TestCaseInTempDir): generate_padata_fn=generate_padata_fn, check_error_fn=check_error_fn, check_rep_fn=check_rep_fn, - check_padata_fn=_check_padata_preauth_key, check_kdc_private_fn=self.generic_check_kdc_private, expected_error_mode=expected_error_mode, client_as_etypes=client_as_etypes, expected_salt=expected_salt, expected_flags=expected_flags, unexpected_flags=unexpected_flags, + preauth_key=preauth_key, kdc_options=str(kdc_options), pac_request=pac_request, pac_options=pac_options, -- 2.25.1 From ecfa294660f93327cadce74b1061299631a43b8a Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 3 Sep 2021 15:36:24 +1200 Subject: [PATCH 027/159] tests/krb5: Add get_cached_creds() method to create persistent accounts for testing BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall (cherry picked from commit c9fd8ffd8927ef42fd555e690f966f65aa01332e) --- python/samba/tests/krb5/fast_tests.py | 2 +- python/samba/tests/krb5/kdc_base_test.py | 191 +++++++++++++++-------- 2 files changed, 125 insertions(+), 68 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 12235cd439e..106b9b1fb78 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1108,7 +1108,7 @@ class FAST_Tests(KDCBaseTest): target_sname = self.PrincipalName_create( name_type=NT_SRV_INST, names=[target_service, target_username]) target_decryption_key = self.TicketDecryptionKey_from_creds( - target_creds, etype=kcrypto.Enctype.RC4) + target_creds) fast_cookie = None preauth_etype_info2 = None diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 49a3227c26e..b2b9d87c3af 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -22,6 +22,7 @@ from datetime import datetime, timezone import tempfile import binascii import collections +import secrets from collections import namedtuple import ldb @@ -37,7 +38,10 @@ from samba.dsdb import ( DS_GUID_COMPUTERS_CONTAINER, DS_GUID_USERS_CONTAINER, UF_WORKSTATION_TRUST_ACCOUNT, - UF_NORMAL_ACCOUNT + UF_NO_AUTH_DATA_REQUIRED, + UF_NORMAL_ACCOUNT, + UF_NOT_DELEGATED, + UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION ) from samba.ndr import ndr_pack, ndr_unpack from samba import net @@ -88,9 +92,17 @@ class KDCBaseTest(RawKerberosTest): cls._functional_level = None + # An identifier to ensure created accounts have unique names. Windows + # caches accounts based on usernames, so account names being different + # across test runs avoids previous test runs affecting the results. + cls.account_base = f'krb5_{secrets.token_hex(5)}_' + cls.account_id = 0 + # A set containing DNs of accounts created as part of testing. cls.accounts = set() + cls.account_cache = {} + @classmethod def tearDownClass(cls): # Clean up any accounts created by create_account. This is @@ -322,24 +334,113 @@ class KDCBaseTest(RawKerberosTest): creds.set_tgs_supported_enctypes(supported_enctypes) creds.set_ap_supported_enctypes(supported_enctypes) - def get_client_creds(self, - allow_missing_password=False, - allow_missing_keys=True): - def create_client_account(): - samdb = self.get_samdb() + def get_cached_creds(self, *, + machine_account, + opts=None): + if opts is None: + opts = {} + + opts_default = { + 'no_auth_data_required': False, + 'supported_enctypes': None, + 'not_delegated': False, + 'allowed_to_delegate_to': None, + 'trusted_to_auth_for_delegation': False, + 'fast_support': False + } + + account_opts = { + 'machine_account': machine_account, + **opts_default, + **opts + } + + cache_key = tuple(sorted(account_opts.items())) + + creds = self.account_cache.get(cache_key) + if creds is None: + creds = self.create_account_opts(**account_opts) + self.account_cache[cache_key] = creds + + return creds + + def create_account_opts(self, *, + machine_account, + no_auth_data_required, + supported_enctypes, + not_delegated, + allowed_to_delegate_to, + trusted_to_auth_for_delegation, + fast_support): + if machine_account: + self.assertFalse(not_delegated) + else: + self.assertIsNone(allowed_to_delegate_to) + self.assertFalse(trusted_to_auth_for_delegation) - creds, dn = self.create_account(samdb, 'kdctestclient') + samdb = self.get_samdb() - res = samdb.search(base=dn, - scope=ldb.SCOPE_BASE, - attrs=['msDS-KeyVersionNumber']) - kvno = int(res[0]['msDS-KeyVersionNumber'][0]) - creds.set_kvno(kvno) + user_name = self.account_base + str(self.account_id) + type(self).account_id += 1 - keys = self.get_keys(samdb, dn) - self.creds_set_keys(creds, keys) + user_account_control = 0 + if trusted_to_auth_for_delegation: + user_account_control |= UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION + if not_delegated: + user_account_control |= UF_NOT_DELEGATED + if no_auth_data_required: + user_account_control |= UF_NO_AUTH_DATA_REQUIRED - return creds + details = {} + + enctypes = supported_enctypes + if fast_support: + fast_bits = (security.KERB_ENCTYPE_FAST_SUPPORTED | + security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED | + security.KERB_ENCTYPE_CLAIMS_SUPPORTED) + enctypes = (enctypes or 0) | fast_bits + + if enctypes is not None: + details['msDS-SupportedEncryptionTypes'] = str(enctypes) + + if allowed_to_delegate_to: + details['msDS-AllowedToDelegateTo'] = allowed_to_delegate_to + + if machine_account: + spn = 'host/' + user_name + else: + spn = None + + creds, dn = self.create_account(samdb, user_name, + machine_account=machine_account, + spn=spn, + additional_details=details, + account_control=user_account_control) + + res = samdb.search(base=dn, + scope=ldb.SCOPE_BASE, + attrs=['msDS-KeyVersionNumber']) + kvno = int(res[0]['msDS-KeyVersionNumber'][0]) + creds.set_kvno(kvno) + + keys = self.get_keys(samdb, dn) + self.creds_set_keys(creds, keys) + + if machine_account: + if supported_enctypes is not None: + tgs_enctypes = supported_enctypes + else: + tgs_enctypes = security.KERB_ENCTYPE_RC4_HMAC_MD5 + + creds.set_tgs_supported_enctypes(tgs_enctypes) + + return creds + + def get_client_creds(self, + allow_missing_password=False, + allow_missing_keys=True): + def create_client_account(): + return self.get_cached_creds(machine_account=False) c = self._get_krb5_creds(prefix='CLIENT', allow_missing_password=allow_missing_password, @@ -351,32 +452,8 @@ class KDCBaseTest(RawKerberosTest): allow_missing_password=False, allow_missing_keys=True): def create_mach_account(): - samdb = self.get_samdb() - - mach_name = 'kdctestmac' - details = { - 'msDS-SupportedEncryptionTypes': str( - security.KERB_ENCTYPE_FAST_SUPPORTED | - security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED | - security.KERB_ENCTYPE_CLAIMS_SUPPORTED - ) - } - - creds, dn = self.create_account(samdb, mach_name, - machine_account=True, - spn='host/' + mach_name, - additional_details=details) - - res = samdb.search(base=dn, - scope=ldb.SCOPE_BASE, - attrs=['msDS-KeyVersionNumber']) - kvno = int(res[0]['msDS-KeyVersionNumber'][0]) - creds.set_kvno(kvno) - - keys = self.get_keys(samdb, dn) - self.creds_set_keys(creds, keys) - - return creds + return self.get_cached_creds(machine_account=True, + opts={'fast_support': True}) c = self._get_krb5_creds(prefix='MAC', allow_missing_password=allow_missing_password, @@ -388,32 +465,12 @@ class KDCBaseTest(RawKerberosTest): allow_missing_password=False, allow_missing_keys=True): def create_service_account(): - samdb = self.get_samdb() - - mach_name = 'kdctestservice' - details = { - 'msDS-SupportedEncryptionTypes': str( - security.KERB_ENCTYPE_FAST_SUPPORTED | - security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED | - security.KERB_ENCTYPE_CLAIMS_SUPPORTED - ) - } - - creds, dn = self.create_account(samdb, mach_name, - machine_account=True, - spn='host/' + mach_name, - additional_details=details) - - res = samdb.search(base=dn, - scope=ldb.SCOPE_BASE, - attrs=['msDS-KeyVersionNumber']) - kvno = int(res[0]['msDS-KeyVersionNumber'][0]) - creds.set_kvno(kvno) - - keys = self.get_keys(samdb, dn) - self.creds_set_keys(creds, keys) - - return creds + return self.get_cached_creds( + machine_account=True, + opts={ + 'trusted_to_auth_for_delegation': True, + 'fast_support': True + }) c = self._get_krb5_creds(prefix='SERVICE', allow_missing_password=allow_missing_password, -- 2.25.1 From 9451573c242bb6fbc28ad128332de5f3467db34b Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 8 Sep 2021 11:28:52 +1200 Subject: [PATCH 028/159] tests/krb5: Generate padata for FAST tests This gives us access to parameters of kdc_exchange_dict and enables us to simplify the logic. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall (cherry picked from commit 943079fd94fec66cdc2ba4ea1b2beb2971473004) --- python/samba/tests/krb5/fast_tests.py | 101 ++++++++++++++++---------- 1 file changed, 61 insertions(+), 40 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 106b9b1fb78..8024b92f445 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1017,19 +1017,6 @@ class FAST_Tests(KDCBaseTest): # challenge is only considered a replay if the ciphertext is identical # to a previous challenge. Windows does not perform this check. - class GenerateEncChallengePadataReplay: - def __init__(replay): - replay._padata = None - - def __call__(replay, key, armor_key): - if replay._padata is None: - client_challenge_key = ( - self.generate_client_challenge_key(armor_key, key)) - replay._padata = self.get_challenge_pa_data( - client_challenge_key) - - return replay._padata - self._run_test_sequence([ { 'rep_type': KRB_AS_REP, @@ -1042,28 +1029,72 @@ class FAST_Tests(KDCBaseTest): 'rep_type': KRB_AS_REP, 'expected_error_mode': 0, 'use_fast': True, - 'gen_padata_fn': GenerateEncChallengePadataReplay(), + 'gen_padata_fn': self.generate_enc_challenge_padata_replay, 'fast_armor': FX_FAST_ARMOR_AP_REQUEST, 'gen_armor_tgt_fn': self.get_mach_tgt, 'repeat': 2 } ]) - def generate_enc_timestamp_padata(self, key, _armor_key): - return self.get_enc_timestamp_pa_data_from_key(key) + def generate_enc_timestamp_padata(self, + kdc_exchange_dict, + callback_dict, + req_body): + key = kdc_exchange_dict['preauth_key'] + + padata = self.get_enc_timestamp_pa_data_from_key(key) + return [padata], req_body + + def generate_enc_challenge_padata(self, + kdc_exchange_dict, + callback_dict, + req_body, + skew=0): + armor_key = kdc_exchange_dict['armor_key'] + key = kdc_exchange_dict['preauth_key'] - def generate_enc_challenge_padata(self, key, armor_key, skew=0): client_challenge_key = ( self.generate_client_challenge_key(armor_key, key)) - return self.get_challenge_pa_data(client_challenge_key, skew=skew) + padata = self.get_challenge_pa_data(client_challenge_key, skew=skew) + return [padata], req_body + + def generate_enc_challenge_padata_wrong_key_kdc(self, + kdc_exchange_dict, + callback_dict, + req_body): + armor_key = kdc_exchange_dict['armor_key'] + key = kdc_exchange_dict['preauth_key'] - def generate_enc_challenge_padata_wrong_key_kdc(self, key, armor_key): kdc_challenge_key = ( self.generate_kdc_challenge_key(armor_key, key)) - return self.get_challenge_pa_data(kdc_challenge_key) + padata = self.get_challenge_pa_data(kdc_challenge_key) + return [padata], req_body - def generate_enc_challenge_padata_wrong_key(self, key, _armor_key): - return self.get_challenge_pa_data(key) + def generate_enc_challenge_padata_wrong_key(self, + kdc_exchange_dict, + callback_dict, + req_body): + key = kdc_exchange_dict['preauth_key'] + + padata = self.get_challenge_pa_data(key) + return [padata], req_body + + def generate_enc_challenge_padata_replay(self, + kdc_exchange_dict, + callback_dict, + req_body): + padata = callback_dict.get('replay_padata') + + if padata is None: + armor_key = kdc_exchange_dict['armor_key'] + key = kdc_exchange_dict['preauth_key'] + + client_challenge_key = ( + self.generate_client_challenge_key(armor_key, key)) + padata = self.get_challenge_pa_data(client_challenge_key) + callback_dict['replay_padata'] = padata + + return [padata], req_body def generate_empty_fast(self, _kdc_exchange_dict, @@ -1251,35 +1282,25 @@ class FAST_Tests(KDCBaseTest): kdc_options = kdc_dict.pop('kdc_options', kdc_options_default) gen_padata_fn = kdc_dict.pop('gen_padata_fn', None) - if gen_padata_fn is not None: - self.assertEqual(KRB_AS_REP, rep_type) + + if rep_type == KRB_AS_REP and gen_padata_fn is not None: self.assertIsNotNone(preauth_etype_info2) preauth_key = self.PasswordKey_from_etype_info2( client_creds, preauth_etype_info2[0], client_creds.get_kvno()) - padata = [gen_padata_fn(preauth_key, armor_key)] else: preauth_key = None - padata = [] if use_fast: - inner_padata = padata - outer_padata = [] + generate_fast_padata_fn = gen_padata_fn + generate_padata_fn = (functools.partial(_generate_padata_copy, + padata=[fast_cookie]) + if fast_cookie is not None else None) else: - inner_padata = [] - outer_padata = padata - - if use_fast and fast_cookie is not None: - outer_padata.append(fast_cookie) - - generate_fast_padata_fn = (functools.partial(_generate_padata_copy, - padata=inner_padata) - if inner_padata else None) - generate_padata_fn = (functools.partial(_generate_padata_copy, - padata=outer_padata) - if outer_padata else None) + generate_fast_padata_fn = None + generate_padata_fn = gen_padata_fn gen_authdata_fn = kdc_dict.pop('gen_authdata_fn', None) if gen_authdata_fn is not None: -- 2.25.1 From b69f9a907388949473c8f6161e797781a8b6ed02 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 13 Sep 2021 21:14:18 +1200 Subject: [PATCH 029/159] tests/krb5: Sign-extend kvno from 32-bit integer This helps to avoid problems with RODC kvnos that have the high bit set. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall (cherry picked from commit 7bc52cecb442c4bcbd39372a8b98bb033e4d1540) --- python/samba/tests/krb5/raw_testcase.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 164d06b9788..cca38fb9480 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -294,6 +294,9 @@ class KerberosCredentials(Credentials): return self._get_krb5_etypes(self.ap_supported_enctypes) def set_kvno(self, kvno): + # Sign-extend from 32 bits. + if kvno & 1 << 31: + kvno |= -1 << 31 self.kvno = kvno def get_kvno(self): -- 2.25.1 From 56a8d5cc38e842b5f1070c0baf44dfb8b755136c Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 13 Sep 2021 20:20:23 +1200 Subject: [PATCH 030/159] tests/krb5: Add method to get RODC krbtgt credentials BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall (cherry picked from commit a5bf7aad54b7053417a24ae0918ee42ceed7bf21) --- python/samba/tests/krb5/kdc_base_test.py | 74 ++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index b2b9d87c3af..fd2e4d08cd3 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -89,6 +89,7 @@ class KDCBaseTest(RawKerberosTest): cls._lp = None cls._ldb = None + cls._rodc_ldb = None cls._functional_level = None @@ -137,6 +138,30 @@ class KDCBaseTest(RawKerberosTest): return self._ldb + def get_rodc_samdb(self): + if self._rodc_ldb is None: + creds = self.get_admin_creds() + lp = self.get_lp() + + session = system_session() + type(self)._rodc_ldb = SamDB(url="ldap://%s" % self.host, + session_info=session, + credentials=creds, + lp=lp, + am_rodc=True) + + return self._rodc_ldb + + def get_server_dn(self, samdb): + server = samdb.get_serverName() + + res = samdb.search(base=server, + scope=ldb.SCOPE_BASE, + attrs=['serverReference']) + dn = ldb.Dn(samdb, res[0]['serverReference'][0].decode('utf8')) + + return dn + def get_domain_functional_level(self, ldb): if self._functional_level is None: res = ldb.search(base='', @@ -478,6 +503,55 @@ class KDCBaseTest(RawKerberosTest): fallback_creds_fn=create_service_account) return c + def get_rodc_krbtgt_creds(self, + require_keys=True, + require_strongest_key=False): + if require_strongest_key: + self.assertTrue(require_keys) + + def download_rodc_krbtgt_creds(): + samdb = self.get_samdb() + rodc_samdb = self.get_rodc_samdb() + + rodc_dn = self.get_server_dn(rodc_samdb) + + res = samdb.search(rodc_dn, + scope=ldb.SCOPE_BASE, + attrs=['msDS-KrbTgtLink']) + krbtgt_dn = res[0]['msDS-KrbTgtLink'][0] + + res = samdb.search(krbtgt_dn, + scope=ldb.SCOPE_BASE, + attrs=['sAMAccountName', + 'msDS-KeyVersionNumber', + 'msDS-SecondaryKrbTgtNumber']) + krbtgt_dn = res[0].dn + username = str(res[0]['sAMAccountName']) + + creds = KerberosCredentials() + creds.set_domain(self.env_get_var('DOMAIN', 'RODC_KRBTGT')) + creds.set_realm(self.env_get_var('REALM', 'RODC_KRBTGT')) + creds.set_username(username) + + kvno = int(res[0]['msDS-KeyVersionNumber'][0]) + krbtgt_number = int(res[0]['msDS-SecondaryKrbTgtNumber'][0]) + + rodc_kvno = krbtgt_number << 16 | kvno + creds.set_kvno(rodc_kvno) + creds.set_dn(krbtgt_dn) + + keys = self.get_keys(samdb, krbtgt_dn) + self.creds_set_keys(creds, keys) + + return creds + + c = self._get_krb5_creds(prefix='RODC_KRBTGT', + allow_missing_password=True, + allow_missing_keys=not require_keys, + require_strongest_key=require_strongest_key, + fallback_creds_fn=download_rodc_krbtgt_creds) + return c + def get_krbtgt_creds(self, require_keys=True, require_strongest_key=False): -- 2.25.1 From accdbf65bc4d5b593cd47573e04bac93952c7d1a Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 13 Sep 2021 20:58:01 +1200 Subject: [PATCH 031/159] tests/krb5: Add get_secrets() method to get the secret attributes of a DN BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall (cherry picked from commit af633992e31e839cdd7f77740c1f25d129be2f79) --- python/samba/tests/krb5/kdc_base_test.py | 26 +++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index fd2e4d08cd3..3681d26bb83 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -250,7 +250,9 @@ class KDCBaseTest(RawKerberosTest): return (creds, dn) - def get_keys(self, samdb, dn): + def get_secrets(self, samdb, dn, + destination_dsa_guid, + source_dsa_invocation_id): admin_creds = self.get_admin_creds() dns_hostname = samdb.host_dns_name() @@ -258,15 +260,13 @@ class KDCBaseTest(RawKerberosTest): self.get_lp(), admin_creds) - destination_dsa_guid = misc.GUID(samdb.get_ntds_GUID()) - req = drsuapi.DsGetNCChangesRequest8() req.destination_dsa_guid = destination_dsa_guid - req.source_dsa_invocation_id = misc.GUID() + req.source_dsa_invocation_id = source_dsa_invocation_id naming_context = drsuapi.DsReplicaObjectIdentifier() - naming_context.dn = str(dn) + naming_context.dn = dn req.naming_context = naming_context @@ -299,9 +299,25 @@ class KDCBaseTest(RawKerberosTest): req.mapping_ctr.mappings = None _, ctr = bind.DsGetNCChanges(handle, 8, req) + + self.assertEqual(1, ctr.object_count) + identifier = ctr.first_object.object.identifier attributes = ctr.first_object.object.attribute_ctr.attributes + self.assertEqual(dn, identifier.dn) + + return bind, identifier, attributes + + def get_keys(self, samdb, dn): + admin_creds = self.get_admin_creds() + + bind, identifier, attributes = self.get_secrets( + samdb, + str(dn), + destination_dsa_guid=misc.GUID(samdb.get_ntds_GUID()), + source_dsa_invocation_id=misc.GUID()) + rid = identifier.sid.split()[1] net_ctx = net.Net(admin_creds) -- 2.25.1 From dc54b53b9e88101ea682092567578ddeefe847c9 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 13 Sep 2021 22:13:24 +1200 Subject: [PATCH 032/159] tests/krb5: Allow replicating accounts to the RODC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall (cherry picked from commit 3cc9e77f38f6698aa01abca4285a520c7c0cd2ac) --- python/samba/tests/krb5/kdc_base_test.py | 141 ++++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 3681d26bb83..56102cfc4ea 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -31,8 +31,9 @@ from samba import generate_random_password from samba.auth import system_session from samba.credentials import Credentials, SPECIFIED, MUST_USE_KERBEROS from samba.dcerpc import drsblobs, drsuapi, misc, krb5pac, krb5ccache, security -from samba.drs_utils import drsuapi_connect +from samba.drs_utils import drs_Replicate, drsuapi_connect from samba.dsdb import ( + DSDB_SYNTAX_BINARY_DN, DS_DOMAIN_FUNCTION_2000, DS_DOMAIN_FUNCTION_2008, DS_GUID_COMPUTERS_CONTAINER, @@ -45,7 +46,7 @@ from samba.dsdb import ( ) from samba.ndr import ndr_pack, ndr_unpack from samba import net -from samba.samdb import SamDB +from samba.samdb import SamDB, dsdb_Dn from samba.tests import delete_force import samba.tests.krb5.kcrypto as kcrypto @@ -104,12 +105,20 @@ class KDCBaseTest(RawKerberosTest): cls.account_cache = {} + cls.ldb_cleanups = [] + @classmethod def tearDownClass(cls): # Clean up any accounts created by create_account. This is # done in tearDownClass() rather than tearDown(), so that # accounts need only be created once for permutation tests. if cls._ldb is not None: + for cleanup in reversed(cls.ldb_cleanups): + try: + cls._ldb.modify(cleanup) + except ldb.LdbError: + pass + for dn in cls.accounts: delete_force(cls._ldb, dn) super().tearDownClass() @@ -250,6 +259,76 @@ class KDCBaseTest(RawKerberosTest): return (creds, dn) + def replicate_account_to_rodc(self, dn): + samdb = self.get_samdb() + rodc_samdb = self.get_rodc_samdb() + + repl_val = f'{samdb.get_dsServiceName()}:{dn}:SECRETS_ONLY' + + msg = ldb.Message() + msg.dn = ldb.Dn(rodc_samdb, '') + msg['replicateSingleObject'] = ldb.MessageElement( + repl_val, + ldb.FLAG_MOD_REPLACE, + 'replicateSingleObject') + + try: + # Try replication using the replicateSingleObject rootDSE + # operation. + rodc_samdb.modify(msg) + except ldb.LdbError as err: + enum, estr = err.args + self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM) + self.assertIn('rootdse_modify: unknown attribute to change!', + estr) + + # If that method wasn't supported, we may be in the rodc:local test + # environment, where we can try replicating to the local database. + + lp = self.get_lp() + + rodc_creds = Credentials() + rodc_creds.guess(lp) + rodc_creds.set_machine_account(lp) + + local_samdb = SamDB(url=None, session_info=system_session(), + credentials=rodc_creds, lp=lp) + + destination_dsa_guid = misc.GUID(local_samdb.get_ntds_GUID()) + + repl = drs_Replicate(f'ncacn_ip_tcp:{self.dc_host}[seal]', + lp, rodc_creds, + local_samdb, destination_dsa_guid) + + source_dsa_invocation_id = misc.GUID(samdb.invocation_id) + + repl.replicate(dn, + source_dsa_invocation_id, + destination_dsa_guid, + exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, + rodc=True) + + def check_revealed(self, dn, rodc_dn, revealed=True): + samdb = self.get_samdb() + + res = samdb.search(base=rodc_dn, + scope=ldb.SCOPE_BASE, + attrs=['msDS-RevealedUsers']) + + revealed_users = res[0].get('msDS-RevealedUsers') + if revealed_users is None: + self.assertFalse(revealed) + return + + revealed_dns = set(str(dsdb_Dn(samdb, str(user), + syntax_oid=DSDB_SYNTAX_BINARY_DN).dn) + for user in revealed_users) + + if revealed: + self.assertIn(str(dn), revealed_dns) + else: + self.assertNotIn(str(dn), revealed_dns) + def get_secrets(self, samdb, dn, destination_dsa_guid, source_dsa_invocation_id): @@ -375,6 +454,29 @@ class KDCBaseTest(RawKerberosTest): creds.set_tgs_supported_enctypes(supported_enctypes) creds.set_ap_supported_enctypes(supported_enctypes) + def add_to_group(self, account_dn, group_dn, group_attr): + samdb = self.get_samdb() + + res = samdb.search(base=group_dn, + scope=ldb.SCOPE_BASE, + attrs=[group_attr]) + orig_msg = res[0] + + members = list(orig_msg[group_attr]) + members.append(account_dn) + + msg = ldb.Message() + msg.dn = group_dn + msg[group_attr] = ldb.MessageElement(members, + ldb.FLAG_MOD_REPLACE, + group_attr) + + cleanup = samdb.msg_diff(msg, orig_msg) + self.ldb_cleanups.append(cleanup) + samdb.modify(msg) + + return cleanup + def get_cached_creds(self, *, machine_account, opts=None): @@ -382,6 +484,9 @@ class KDCBaseTest(RawKerberosTest): opts = {} opts_default = { + 'allowed_replication': False, + 'denied_replication': False, + 'revealed_to_rodc': False, 'no_auth_data_required': False, 'supported_enctypes': None, 'not_delegated': False, @@ -407,6 +512,9 @@ class KDCBaseTest(RawKerberosTest): def create_account_opts(self, *, machine_account, + allowed_replication, + denied_replication, + revealed_to_rodc, no_auth_data_required, supported_enctypes, not_delegated, @@ -420,6 +528,9 @@ class KDCBaseTest(RawKerberosTest): self.assertFalse(trusted_to_auth_for_delegation) samdb = self.get_samdb() + rodc_samdb = self.get_rodc_samdb() + + rodc_dn = self.get_server_dn(rodc_samdb) user_name = self.account_base + str(self.account_id) type(self).account_id += 1 @@ -475,6 +586,32 @@ class KDCBaseTest(RawKerberosTest): creds.set_tgs_supported_enctypes(tgs_enctypes) + # Handle secret replication to the RODC. + + if allowed_replication or revealed_to_rodc: + # Allow replicating this account's secrets if requested, or allow + # it only temporarily if we're about to replicate them. + allowed_cleanup = self.add_to_group( + dn, rodc_dn, + 'msDS-RevealOnDemandGroup') + + if revealed_to_rodc: + # Replicate this account's secrets to the RODC. + self.replicate_account_to_rodc(dn) + + if not allowed_replication: + # If we don't want replicating secrets to be allowed for this + # account, disable it again. + samdb.modify(allowed_cleanup) + + self.check_revealed(dn, + rodc_dn, + revealed=revealed_to_rodc) + + if denied_replication: + # Deny replicating this account's secrets to the RODC. + self.add_to_group(dn, rodc_dn, 'msDS-NeverRevealGroup') + return creds def get_client_creds(self, -- 2.25.1 From 168c655af8cad05e036e72dd7d577d87922d0ffd Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 13 Sep 2021 21:24:05 +1200 Subject: [PATCH 033/159] tests/krb5: Create RODC account for testing BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall (cherry picked from commit ef5666bc51ca80e1acdadd525a9c61762756c8e3) --- python/samba/tests/krb5/kdc_base_test.py | 114 +++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 56102cfc4ea..892d3aaf41f 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -42,8 +42,10 @@ from samba.dsdb import ( UF_NO_AUTH_DATA_REQUIRED, UF_NORMAL_ACCOUNT, UF_NOT_DELEGATED, + UF_PARTIAL_SECRETS_ACCOUNT, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION ) +from samba.join import DCJoinContext from samba.ndr import ndr_pack, ndr_unpack from samba import net from samba.samdb import SamDB, dsdb_Dn @@ -105,6 +107,8 @@ class KDCBaseTest(RawKerberosTest): cls.account_cache = {} + cls._rodc_ctx = None + cls.ldb_cleanups = [] @classmethod @@ -121,6 +125,10 @@ class KDCBaseTest(RawKerberosTest): for dn in cls.accounts: delete_force(cls._ldb, dn) + + if cls._rodc_ctx is not None: + cls._rodc_ctx.cleanup_old_join(force=True) + super().tearDownClass() def setUp(self): @@ -171,6 +179,25 @@ class KDCBaseTest(RawKerberosTest): return dn + def get_mock_rodc_ctx(self): + if self._rodc_ctx is None: + admin_creds = self.get_admin_creds() + lp = self.get_lp() + + rodc_name = 'KRB5RODC' + site_name = 'Default-First-Site-Name' + + type(self)._rodc_ctx = DCJoinContext(server=self.dc_host, + creds=admin_creds, + lp=lp, + site=site_name, + netbios_name=rodc_name, + targetdir=None, + domain=None) + self.create_rodc(self._rodc_ctx) + + return self._rodc_ctx + def get_domain_functional_level(self, ldb): if self._functional_level is None: res = ldb.search(base='', @@ -259,6 +286,49 @@ class KDCBaseTest(RawKerberosTest): return (creds, dn) + def create_rodc(self, ctx): + ctx.nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn] + ctx.full_nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn] + ctx.krbtgt_dn = f'CN=krbtgt_{ctx.myname},CN=Users,{ctx.base_dn}' + + ctx.never_reveal_sid = [f'', + f'', + f'', + f'', + f''] + ctx.reveal_sid = f'' + + mysid = ctx.get_mysid() + admin_dn = f'' + ctx.managedby = admin_dn + + ctx.userAccountControl = (UF_WORKSTATION_TRUST_ACCOUNT | + UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION | + UF_PARTIAL_SECRETS_ACCOUNT) + + ctx.connection_dn = f'CN=RODC Connection (FRS),{ctx.ntds_dn}' + ctx.secure_channel_type = misc.SEC_CHAN_RODC + ctx.RODC = True + ctx.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC | + drsuapi.DRSUAPI_DRS_PER_SYNC | + drsuapi.DRSUAPI_DRS_GET_ANC | + drsuapi.DRSUAPI_DRS_NEVER_SYNCED | + drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) + ctx.domain_replica_flags = ctx.replica_flags | drsuapi.DRSUAPI_DRS_CRITICAL_ONLY + + ctx.build_nc_lists() + + ctx.cleanup_old_join() + + try: + ctx.join_add_objects() + except Exception: + # cleanup the failed join (checking we still have a live LDB + # connection to the remote DC first) + ctx.refresh_ldb_connection() + ctx.cleanup_old_join() + raise + def replicate_account_to_rodc(self, dn): samdb = self.get_samdb() rodc_samdb = self.get_rodc_samdb() @@ -705,6 +775,50 @@ class KDCBaseTest(RawKerberosTest): fallback_creds_fn=download_rodc_krbtgt_creds) return c + def get_mock_rodc_krbtgt_creds(self, + require_keys=True, + require_strongest_key=False): + if require_strongest_key: + self.assertTrue(require_keys) + + def create_rodc_krbtgt_account(): + samdb = self.get_samdb() + + rodc_ctx = self.get_mock_rodc_ctx() + + krbtgt_dn = rodc_ctx.new_krbtgt_dn + + res = samdb.search(base=ldb.Dn(samdb, krbtgt_dn), + scope=ldb.SCOPE_BASE, + attrs=['msDS-KeyVersionNumber', + 'msDS-SecondaryKrbTgtNumber']) + dn = res[0].dn + username = str(rodc_ctx.krbtgt_name) + + creds = KerberosCredentials() + creds.set_domain(self.env_get_var('DOMAIN', 'RODC_KRBTGT')) + creds.set_realm(self.env_get_var('REALM', 'RODC_KRBTGT')) + creds.set_username(username) + + kvno = int(res[0]['msDS-KeyVersionNumber'][0]) + krbtgt_number = int(res[0]['msDS-SecondaryKrbTgtNumber'][0]) + + rodc_kvno = krbtgt_number << 16 | kvno + creds.set_kvno(rodc_kvno) + creds.set_dn(dn) + + keys = self.get_keys(samdb, dn) + self.creds_set_keys(creds, keys) + + return creds + + c = self._get_krb5_creds(prefix='MOCK_RODC_KRBTGT', + allow_missing_password=True, + allow_missing_keys=not require_keys, + require_strongest_key=require_strongest_key, + fallback_creds_fn=create_rodc_krbtgt_account) + return c + def get_krbtgt_creds(self, require_keys=True, require_strongest_key=False): -- 2.25.1 From df0700ab8f6745474d8185451b7cab9815632de8 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 13 Sep 2021 21:24:31 +1200 Subject: [PATCH 034/159] tests/krb5: Allow replicating accounts to the created RODC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall (cherry picked from commit 35292bd32225b39ad7a03c3aa53027458f0671eb) --- python/samba/tests/krb5/kdc_base_test.py | 50 ++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 892d3aaf41f..0e138352b06 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -378,6 +378,16 @@ class KDCBaseTest(RawKerberosTest): exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) + def reveal_account_to_mock_rodc(self, dn): + samdb = self.get_samdb() + rodc_ctx = self.get_mock_rodc_ctx() + + self.get_secrets( + samdb, + dn, + destination_dsa_guid=rodc_ctx.ntds_guid, + source_dsa_invocation_id=misc.GUID(samdb.invocation_id)) + def check_revealed(self, dn, rodc_dn, revealed=True): samdb = self.get_samdb() @@ -555,8 +565,11 @@ class KDCBaseTest(RawKerberosTest): opts_default = { 'allowed_replication': False, + 'allowed_replication_mock': False, 'denied_replication': False, + 'denied_replication_mock': False, 'revealed_to_rodc': False, + 'revealed_to_mock_rodc': False, 'no_auth_data_required': False, 'supported_enctypes': None, 'not_delegated': False, @@ -583,8 +596,11 @@ class KDCBaseTest(RawKerberosTest): def create_account_opts(self, *, machine_account, allowed_replication, + allowed_replication_mock, denied_replication, + denied_replication_mock, revealed_to_rodc, + revealed_to_mock_rodc, no_auth_data_required, supported_enctypes, not_delegated, @@ -682,6 +698,40 @@ class KDCBaseTest(RawKerberosTest): # Deny replicating this account's secrets to the RODC. self.add_to_group(dn, rodc_dn, 'msDS-NeverRevealGroup') + # Handle secret replication to the mock RODC. + + if allowed_replication_mock or revealed_to_mock_rodc: + # Allow replicating this account's secrets if requested, or allow + # it only temporarily if we want to add the account to the mock + # RODC's msDS-RevealedUsers. + rodc_ctx = self.get_mock_rodc_ctx() + mock_rodc_dn = ldb.Dn(samdb, rodc_ctx.acct_dn) + + allowed_mock_cleanup = self.add_to_group( + dn, mock_rodc_dn, + 'msDS-RevealOnDemandGroup') + + if revealed_to_mock_rodc: + # Request replicating this account's secrets to the mock RODC, + # which updates msDS-RevealedUsers. + self.reveal_account_to_mock_rodc(dn) + + if not allowed_replication_mock: + # If we don't want replicating secrets to be allowed for this + # account, disable it again. + samdb.modify(allowed_mock_cleanup) + + self.check_revealed(dn, + mock_rodc_dn, + revealed=revealed_to_mock_rodc) + + if denied_replication_mock: + # Deny replicating this account's secrets to the mock RODC. + rodc_ctx = self.get_mock_rodc_ctx() + mock_rodc_dn = ldb.Dn(samdb, rodc_ctx.acct_dn) + + self.add_to_group(dn, mock_rodc_dn, 'msDS-NeverRevealGroup') + return creds def get_client_creds(self, -- 2.25.1 From 01afea2d97b889a7acf1cc20ab1ffddf8423c4f8 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 1 Sep 2021 15:42:28 +1200 Subject: [PATCH 035/159] python: Don't leak file handles Signed-off-by: Joseph Sutton Reviewed-by: Noel Power Reviewed-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit cde38d36b98f1d40e7b58cd4c4b4bedfab76c390) --- python/samba/__init__.py | 12 +++++++----- python/samba/ms_schema.py | 6 +++--- python/samba/schema.py | 9 +++++++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/python/samba/__init__.py b/python/samba/__init__.py index fa047a813e2..b4d141e95c1 100644 --- a/python/samba/__init__.py +++ b/python/samba/__init__.py @@ -217,7 +217,8 @@ class Ldb(_Ldb): :param ldif_path: Path to LDIF file. """ - self.add_ldif(open(ldif_path, 'r').read()) + with open(ldif_path, 'r') as ldif_file: + self.add_ldif(ldif_file.read()) def add_ldif(self, ldif, controls=None): """Add data based on a LDIF string. @@ -279,10 +280,11 @@ def read_and_sub_file(file_name, subst_vars): :param file_name: File to be read (typically from setup directory) param subst_vars: Optional variables to subsitute in the file. """ - data = open(file_name, 'r', encoding="utf-8").read() - if subst_vars is not None: - data = substitute_var(data, subst_vars) - check_all_substituted(data) + with open(file_name, 'r', encoding="utf-8") as data_file: + data = data_file.read() + if subst_vars is not None: + data = substitute_var(data, subst_vars) + check_all_substituted(data) return data diff --git a/python/samba/ms_schema.py b/python/samba/ms_schema.py index 4b5c4a5df83..b26bbfd4fbd 100644 --- a/python/samba/ms_schema.py +++ b/python/samba/ms_schema.py @@ -295,9 +295,9 @@ def __parse_schema_file(filename, objectClass): out = [] from io import open - f = open(filename, "r", encoding='latin-1') - for entry in __read_raw_entries(f): - out.append(__write_ldif_one(__transform_entry(entry, objectClass))) + with open(filename, "r", encoding='latin-1') as f: + for entry in __read_raw_entries(f): + out.append(__write_ldif_one(__transform_entry(entry, objectClass))) return "\n\n".join(out) diff --git a/python/samba/schema.py b/python/samba/schema.py index 54fc9fc3125..a3adc162fa3 100644 --- a/python/samba/schema.py +++ b/python/samba/schema.py @@ -110,8 +110,13 @@ class Schema(object): setup_path('ad-schema/%s' % Schema.base_schemas[base_schema][0]), setup_path('ad-schema/%s' % Schema.base_schemas[base_schema][1])) + def read_file(file): + with open(file, 'rb') as data_file: + return data_file.read() + if files is not None: - self.schema_data = "".join(get_string(open(file, 'rb').read()) for file in files) + self.schema_data = "".join(get_string(read_file(file)) + for file in files) self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn}) @@ -130,7 +135,7 @@ class Schema(object): if override_prefixmap is not None: self.prefixmap_data = override_prefixmap else: - self.prefixmap_data = open(setup_path("prefixMap.txt"), 'rb').read() + self.prefixmap_data = read_file(setup_path("prefixMap.txt")) if additional_prefixmap is not None: self.prefixmap_data += "".join("%s\n" % map for map in additional_prefixmap) -- 2.25.1 From 441d25f8b9b476219da132462f60829599d088da Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 10 Sep 2021 14:02:22 +1200 Subject: [PATCH 036/159] python/join: Check for correct msDS-KrbTgtLink attribute Previously, the wrong case was used when checking for this attribute, which meant krbtgt accounts were not being cleaned up. Signed-off-by: Joseph Sutton Reviewed-by: Noel Power Reviewed-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 21a7717359082feaddfdf42788648c3d7574c28e) --- python/samba/join.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python/samba/join.py b/python/samba/join.py index 59de000a401..79030cdfd29 100644 --- a/python/samba/join.py +++ b/python/samba/join.py @@ -257,8 +257,9 @@ class DCJoinContext(object): ctx.del_noerror(res[0].dn, recursive=True) - if "msDS-Krbtgtlink" in res[0]: - ctx.new_krbtgt_dn = res[0]["msDS-Krbtgtlink"][0] + krbtgt_dn = res[0].get('msDS-KrbTgtLink', idx=0) + if krbtgt_dn is not None: + ctx.new_krbtgt_dn = krbtgt_dn ctx.del_noerror(ctx.new_krbtgt_dn) res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(), @@ -337,7 +338,7 @@ class DCJoinContext(object): attrs=["msDS-krbTgtLink", "userAccountControl", "serverReferenceBL", "rIDSetReferences"]) if len(res) == 0: raise Exception("Could not find domain member account '%s' to promote to a DC, use 'samba-tool domain join' instead'" % ctx.samname) - if "msDS-krbTgtLink" in res[0] or "serverReferenceBL" in res[0] or "rIDSetReferences" in res[0]: + if "msDS-KrbTgtLink" in res[0] or "serverReferenceBL" in res[0] or "rIDSetReferences" in res[0]: raise Exception("Account '%s' appears to be an active DC, use 'samba-tool domain join' if you must re-create this account" % ctx.samname) if (int(res[0]["userAccountControl"][0]) & (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT | samba.dsdb.UF_SERVER_TRUST_ACCOUNT) == 0): -- 2.25.1 From cd40da7187413f65243d9ce55a1f5528e1db0acd Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 15 Sep 2021 20:56:28 +1200 Subject: [PATCH 037/159] tests/krb5: Add helper method for modifying PACs This method can remove or replace a PAC in an authorization-data container, while additionally returning the original PAC. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit a281ae09bcf35277c830c4112567c72233fd66b8) --- python/samba/tests/krb5/raw_testcase.py | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index cca38fb9480..b7df1ac0879 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -42,6 +42,8 @@ from samba.tests import TestCaseInTempDir import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1 from samba.tests.krb5.rfc4120_constants import ( + AD_IF_RELEVANT, + AD_WIN2K_PAC, FX_FAST_ARMOR_AP_REQUEST, KDC_ERR_GENERIC, KDC_ERR_PREAUTH_FAILED, @@ -2848,6 +2850,49 @@ class RawKerberosTest(TestCaseInTempDir): ticket_blob) self.assertEqual(expected_checksum, checksum) + def replace_pac(self, auth_data, new_pac, expect_pac=True): + if new_pac is not None: + self.assertElementEqual(new_pac, 'ad-type', AD_WIN2K_PAC) + self.assertElementPresent(new_pac, 'ad-data') + + new_auth_data = [] + + ad_relevant = None + old_pac = None + + for authdata_elem in auth_data: + if authdata_elem['ad-type'] == AD_IF_RELEVANT: + ad_relevant = self.der_decode( + authdata_elem['ad-data'], + asn1Spec=krb5_asn1.AD_IF_RELEVANT()) + + relevant_elems = [] + for relevant_elem in ad_relevant: + if relevant_elem['ad-type'] == AD_WIN2K_PAC: + self.assertIsNone(old_pac, 'Multiple PACs detected') + old_pac = relevant_elem['ad-data'] + + if new_pac is not None: + relevant_elems.append(new_pac) + else: + relevant_elems.append(relevant_elem) + if expect_pac: + self.assertIsNotNone(old_pac, 'Expected PAC') + + ad_relevant = self.der_encode( + relevant_elems, + asn1Spec=krb5_asn1.AD_IF_RELEVANT()) + + authdata_elem = self.AuthorizationData_create(AD_IF_RELEVANT, + ad_relevant) + + new_auth_data.append(authdata_elem) + + if expect_pac: + self.assertIsNotNone(ad_relevant, 'Expected AD-RELEVANT') + + return new_auth_data, old_pac + def get_outer_pa_dict(self, kdc_exchange_dict): return self.get_pa_dict(kdc_exchange_dict['req_padata']) -- 2.25.1 From c3bf406346995b1f192caf7fa0b0aa1c90ccf1b4 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 11:22:28 +1200 Subject: [PATCH 038/159] tests/krb5: Check correct flags element BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 0061fa2c2a26d990ed2e47441bca8797fc9be356) --- python/samba/tests/krb5/raw_testcase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index b7df1ac0879..632f69794e6 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2193,7 +2193,7 @@ class RawKerberosTest(TestCaseInTempDir): else: self.assertElementMissing(encpart_private, 'key-expiration') - self.assertElementFlags(ticket_private, 'flags', + self.assertElementFlags(encpart_private, 'flags', expected_flags, unexpected_flags) self.assertElementPresent(encpart_private, 'authtime') -- 2.25.1 From cf6b4d369bf9479e6c7586559b489e8b73cdaa6f Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 11:13:09 +1200 Subject: [PATCH 039/159] tests/krb5: Refactor tgs_req() to use _generic_kdc_exchange BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 2a4d53dc12aa785f696e53ae3376f67375ce455f) --- python/samba/tests/krb5/kdc_base_test.py | 78 +++++++++++++----------- python/samba/tests/krb5/kdc_tgs_tests.py | 3 +- python/samba/tests/krb5/raw_testcase.py | 1 - source4/selftest/tests.py | 18 ++++-- 4 files changed, 58 insertions(+), 42 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 0e138352b06..6a370d3036e 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -52,7 +52,11 @@ from samba.samdb import SamDB, dsdb_Dn from samba.tests import delete_force import samba.tests.krb5.kcrypto as kcrypto -from samba.tests.krb5.raw_testcase import KerberosCredentials, RawKerberosTest +from samba.tests.krb5.raw_testcase import ( + KerberosCredentials, + KerberosTicketCreds, + RawKerberosTest +) import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1 from samba.tests.krb5.rfc4120_constants import ( AD_IF_RELEVANT, @@ -66,7 +70,6 @@ from samba.tests.krb5.rfc4120_constants import ( KU_AS_REP_ENC_PART, KU_ENC_CHALLENGE_CLIENT, KU_PA_ENC_TIMESTAMP, - KU_TGS_REP_ENC_PART_SUB_KEY, KU_TICKET, NT_PRINCIPAL, NT_SRV_HST, @@ -1063,49 +1066,56 @@ class KDCBaseTest(RawKerberosTest): else: self.assertEqual(rep['error-code'], expected, "rep = {%s}" % rep) - def tgs_req(self, cname, sname, realm, ticket, key, etypes): + def tgs_req(self, cname, sname, realm, ticket, key, etypes, + expected_error_mode=0): '''Send a TGS-REQ, returns the response and the decrypted and decoded enc-part ''' kdc_options = "0" - till = self.get_KerberosTime(offset=36000) - padata = [] subkey = self.RandomKey(key.etype) (ctime, cusec) = self.get_KerberosTimeWithUsec() - req = self.TGS_REQ_create(padata=padata, - cusec=cusec, - ctime=ctime, - ticket=ticket, - kdc_options=str(kdc_options), - cname=cname, - realm=realm, - sname=sname, - from_time=None, - till_time=till, - renew_time=None, - nonce=0x7ffffffe, - etypes=etypes, - addresses=None, - EncAuthorizationData=None, - EncAuthorizationData_key=None, - additional_tickets=None, - ticket_session_key=key, - authenticator_subkey=subkey) - rep = self.send_recv_transaction(req) - self.assertIsNotNone(rep) + tgt = KerberosTicketCreds(ticket, + key, + crealm=realm, + cname=cname) - msg_type = rep['msg-type'] - enc_part = None - if msg_type == KRB_TGS_REP: - enc_part = subkey.decrypt( - KU_TGS_REP_ENC_PART_SUB_KEY, rep['enc-part']['cipher']) - enc_part = self.der_decode( - enc_part, asn1Spec=krb5_asn1.EncTGSRepPart()) - return (rep, enc_part) + if not expected_error_mode: + check_error_fn = None + check_rep_fn = self.generic_check_kdc_rep + else: + check_error_fn = self.generic_check_kdc_error + check_rep_fn = None + + kdc_exchange_dict = self.tgs_exchange_dict( + expected_crealm=realm, + expected_cname=cname, + expected_srealm=realm, + expected_sname=sname, + expected_error_mode=expected_error_mode, + check_error_fn=check_error_fn, + check_rep_fn=check_rep_fn, + check_kdc_private_fn=self.generic_check_kdc_private, + tgt=tgt, + authenticator_subkey=subkey, + kdc_options=str(kdc_options)) + + rep = self._generic_kdc_exchange(kdc_exchange_dict, + cname=None, + realm=realm, + sname=sname, + etypes=etypes) + + if expected_error_mode: + enc_part = None + else: + ticket_creds = kdc_exchange_dict['rep_ticket_creds'] + enc_part = ticket_creds.encpart_private + + return rep, enc_part # Named tuple to contain values of interest when the PAC is decoded. PacData = namedtuple( diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py index 97f9dd41339..dad9e6b88df 100755 --- a/python/samba/tests/krb5/kdc_tgs_tests.py +++ b/python/samba/tests/krb5/kdc_tgs_tests.py @@ -84,7 +84,8 @@ class KdcTgsTests(KDCBaseTest): name_type=NT_PRINCIPAL, names=["host", samdb.host_dns_name()]) - (rep, enc_part) = self.tgs_req(cname, sname, realm, ticket, key, etype) + (rep, enc_part) = self.tgs_req(cname, sname, realm, ticket, key, etype, + expected_error_mode=KDC_ERR_BADMATCH) self.assertIsNone( enc_part, diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 632f69794e6..7eba62b4022 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2039,7 +2039,6 @@ class RawKerberosTest(TestCaseInTempDir): error_code=0) ticket_private = None - self.assertIsNotNone(ticket_decryption_key) if ticket_decryption_key is not None: self.assertElementEqual(ticket_encpart, 'etype', ticket_decryption_key.etype) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index a3f9478059f..c83136d6421 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -822,22 +822,26 @@ planoldpythontestsuite("fl2008r2dc:local", "samba.tests.krb5.xrealm_tests") planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ccache", environ={ 'ADMIN_USERNAME': '$USERNAME', - 'ADMIN_PASSWORD': '$PASSWORD' + 'ADMIN_PASSWORD': '$PASSWORD', + 'STRICT_CHECKING': '0' }) planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ldap", environ={ 'ADMIN_USERNAME': '$USERNAME', - 'ADMIN_PASSWORD': '$PASSWORD' + 'ADMIN_PASSWORD': '$PASSWORD', + 'STRICT_CHECKING': '0' }) planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_rpc", environ={ 'ADMIN_USERNAME': '$USERNAME', - 'ADMIN_PASSWORD': '$PASSWORD' + 'ADMIN_PASSWORD': '$PASSWORD', + 'STRICT_CHECKING': '0' }) planoldpythontestsuite("ad_dc_smb1", "samba.tests.krb5.test_smb", environ={ 'ADMIN_USERNAME': '$USERNAME', - 'ADMIN_PASSWORD': '$PASSWORD' + 'ADMIN_PASSWORD': '$PASSWORD', + 'STRICT_CHECKING': '0' }) for env in ["ad_dc", smbv1_disabled_testenv]: @@ -1427,7 +1431,8 @@ planpythontestsuite( "samba.tests.krb5.kdc_tgs_tests", environ={ 'ADMIN_USERNAME': '$USERNAME', - 'ADMIN_PASSWORD': '$PASSWORD' + 'ADMIN_PASSWORD': '$PASSWORD', + 'STRICT_CHECKING': '0' }) planpythontestsuite( "ad_dc", @@ -1442,7 +1447,8 @@ planpythontestsuite( "samba.tests.krb5.ms_kile_client_principal_lookup_tests", environ={ 'ADMIN_USERNAME': '$USERNAME', - 'ADMIN_PASSWORD': '$PASSWORD' + 'ADMIN_PASSWORD': '$PASSWORD', + 'STRICT_CHECKING': '0' }) for env in [ -- 2.25.1 From 1e9b10c884f112b694747fdb6cbb4584b6f7cf33 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 11:16:27 +1200 Subject: [PATCH 040/159] tests/krb5: Allow tgs_req() to send additional padata BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 1f0654b8facf3b9b2288d2569a573ff3a5ca4a82) --- python/samba/tests/krb5/kdc_base_test.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 6a370d3036e..57ef1bceb49 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1067,7 +1067,7 @@ class KDCBaseTest(RawKerberosTest): self.assertEqual(rep['error-code'], expected, "rep = {%s}" % rep) def tgs_req(self, cname, sname, realm, ticket, key, etypes, - expected_error_mode=0): + expected_error_mode=0, padata=None): '''Send a TGS-REQ, returns the response and the decrypted and decoded enc-part ''' @@ -1090,6 +1090,12 @@ class KDCBaseTest(RawKerberosTest): check_error_fn = self.generic_check_kdc_error check_rep_fn = None + def generate_padata(_kdc_exchange_dict, + _callback_dict, + req_body): + + return padata, req_body + kdc_exchange_dict = self.tgs_exchange_dict( expected_crealm=realm, expected_cname=cname, @@ -1099,6 +1105,7 @@ class KDCBaseTest(RawKerberosTest): check_error_fn=check_error_fn, check_rep_fn=check_rep_fn, check_kdc_private_fn=self.generic_check_kdc_private, + generate_padata_fn=generate_padata if padata is not None else None, tgt=tgt, authenticator_subkey=subkey, kdc_options=str(kdc_options)) -- 2.25.1 From 2de925001c1b64237512260da5fca88684428cd2 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 11:18:12 +1200 Subject: [PATCH 041/159] tests/krb5: Allow tgs_req() to specify different kdc-options BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 1a3426da54463c3e454c1b76c3df4e96882e6aa9) --- python/samba/tests/krb5/kdc_base_test.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 57ef1bceb49..c448f127c0c 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1067,13 +1067,11 @@ class KDCBaseTest(RawKerberosTest): self.assertEqual(rep['error-code'], expected, "rep = {%s}" % rep) def tgs_req(self, cname, sname, realm, ticket, key, etypes, - expected_error_mode=0, padata=None): + expected_error_mode=0, padata=None, kdc_options=0): '''Send a TGS-REQ, returns the response and the decrypted and decoded enc-part ''' - kdc_options = "0" - subkey = self.RandomKey(key.etype) (ctime, cusec) = self.get_KerberosTimeWithUsec() -- 2.25.1 From 1f80e9cfd2430da7f36b2b895e44640895e16ccd Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 11:25:01 +1200 Subject: [PATCH 042/159] tests/krb5: Allow tgs_req() to send requests to the RODC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 6403a09d94ab54f89d6e50601ae6b19ab7e6aae7) --- python/samba/tests/krb5/kdc_base_test.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index c448f127c0c..5ef08eb32fe 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1067,7 +1067,8 @@ class KDCBaseTest(RawKerberosTest): self.assertEqual(rep['error-code'], expected, "rep = {%s}" % rep) def tgs_req(self, cname, sname, realm, ticket, key, etypes, - expected_error_mode=0, padata=None, kdc_options=0): + expected_error_mode=0, padata=None, kdc_options=0, + to_rodc=False): '''Send a TGS-REQ, returns the response and the decrypted and decoded enc-part ''' @@ -1106,7 +1107,8 @@ class KDCBaseTest(RawKerberosTest): generate_padata_fn=generate_padata if padata is not None else None, tgt=tgt, authenticator_subkey=subkey, - kdc_options=str(kdc_options)) + kdc_options=str(kdc_options), + to_rodc=to_rodc) rep = self._generic_kdc_exchange(kdc_exchange_dict, cname=None, -- 2.25.1 From 1ffcbe53ad098d461b89ff97e32ad25f4f385e06 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 11:52:46 +1200 Subject: [PATCH 043/159] tests/krb5: Allow as_req() to specify different kdc-options BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit a5e62d681d81a422bac7bd89dc27ef2314d77457) --- python/samba/tests/krb5/kdc_base_test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 5ef08eb32fe..ae62c9d5fc6 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -913,12 +913,11 @@ class KDCBaseTest(RawKerberosTest): fallback_creds_fn=download_krbtgt_creds) return c - def as_req(self, cname, sname, realm, etypes, padata=None): + def as_req(self, cname, sname, realm, etypes, padata=None, kdc_options=0): '''Send a Kerberos AS_REQ, returns the undecoded response ''' till = self.get_KerberosTime(offset=36000) - kdc_options = 0 req = self.AS_REQ_create(padata=padata, kdc_options=str(kdc_options), -- 2.25.1 From 7ce1a2b4c2b02464c173e457d6b2db269c1aeee9 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 12:06:51 +1200 Subject: [PATCH 044/159] tests/krb5: Use PAC buffer type constants from krb5pac.idl BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 3504e99dc5bcc206ca2964012b7fdca541555416) --- python/samba/tests/krb5/kdc_base_test.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index ae62c9d5fc6..2cebd9ef0cf 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1127,13 +1127,6 @@ class KDCBaseTest(RawKerberosTest): PacData = namedtuple( "PacData", "account_name account_sid logon_name upn domain_name") - PAC_LOGON_INFO = 1 - PAC_CREDENTIAL_INFO = 2 - PAC_SRV_CHECKSUM = 6 - PAC_KDC_CHECKSUM = 7 - PAC_LOGON_NAME = 10 - PAC_CONSTRAINED_DELEGATION = 11 - PAC_UPN_DNS_INFO = 12 def get_pac_data(self, authorization_data): '''Decode the PAC element contained in the authorization-data element @@ -1154,15 +1147,15 @@ class KDCBaseTest(RawKerberosTest): for ad in (x for x in buf if x['ad-type'] == AD_WIN2K_PAC): pb = ndr_unpack(krb5pac.PAC_DATA, ad['ad-data']) for pac in pb.buffers: - if pac.type == self.PAC_LOGON_INFO: + if pac.type == krb5pac.PAC_TYPE_LOGON_INFO: account_name = ( pac.info.info.info3.base.account_name) user_sid = ( str(pac.info.info.info3.base.domain_sid) + "-" + str(pac.info.info.info3.base.rid)) - elif pac.type == self.PAC_LOGON_NAME: + elif pac.type == krb5pac.PAC_TYPE_LOGON_NAME: logon_name = pac.info.account_name - elif pac.type == self.PAC_UPN_DNS_INFO: + elif pac.type == krb5pac.PAC_TYPE_UPN_DNS_INFO: upn = pac.info.upn_name domain_name = pac.info.dns_domain_name -- 2.25.1 From fa0b1a18bfef07791dd99da888306d849efd7edf Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 12:13:51 +1200 Subject: [PATCH 045/159] tests/krb5: Don't manually create PAC request and options in fast_tests BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit c226029655ca361560d93298a6729a021f2f6b75) --- python/samba/tests/krb5/fast_tests.py | 17 +++++++++-------- python/samba/tests/krb5/raw_testcase.py | 5 ----- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 8024b92f445..dedf7a57a4b 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1553,10 +1553,7 @@ class FAST_Tests(KDCBaseTest): 'canonicalize,' 'renewable-ok')) - pac_request = self.get_pa_pac_request() - pac_options = self.get_pa_pac_options('1') # supports claims - - padata = [pac_request, pac_options] + pac_options = '1' # supports claims rep, kdc_exchange_dict = self._test_as_exchange( cname=cname, @@ -1571,10 +1568,12 @@ class FAST_Tests(KDCBaseTest): expected_sname=sname, expected_salt=salt, etypes=etype, - padata=padata, + padata=None, kdc_options=kdc_options, preauth_key=None, - ticket_decryption_key=ticket_decryption_key) + ticket_decryption_key=ticket_decryption_key, + pac_request=True, + pac_options=pac_options) self.check_pre_authentication(rep) etype_info2 = kdc_exchange_dict['preauth_etype_info2'] @@ -1585,7 +1584,7 @@ class FAST_Tests(KDCBaseTest): ts_enc_padata = self.get_enc_timestamp_pa_data(creds, rep) - padata = [ts_enc_padata, pac_request, pac_options] + padata = [ts_enc_padata] expected_realm = realm.upper() @@ -1608,7 +1607,9 @@ class FAST_Tests(KDCBaseTest): padata=padata, kdc_options=kdc_options, preauth_key=preauth_key, - ticket_decryption_key=ticket_decryption_key) + ticket_decryption_key=ticket_decryption_key, + pac_request=True, + pac_options=pac_options) self.check_as_reply(rep) tgt = rep['ticket'] diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 7eba62b4022..39821240941 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1177,11 +1177,6 @@ class RawKerberosTest(TestCaseInTempDir): pa_data = self.PA_DATA_create(PADATA_PAC_REQUEST, pa_pac) return pa_data - def get_pa_pac_request(self, request_pac=True): - pac_request = self.KERB_PA_PAC_REQUEST_create(request_pac) - - return pac_request - def get_pa_pac_options(self, options): pac_options = self.PA_PAC_OPTIONS_create(options) pac_options = self.der_encode(pac_options, -- 2.25.1 From 253a50feb5010763447379c75622c16869755b51 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 12:19:28 +1200 Subject: [PATCH 046/159] tests/krb5: Set DN of created accounts to ldb.Dn type BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 7645dfa5bedee7ef3f7debbf0fa7600bd1c4bd79) --- python/samba/tests/krb5/kdc_base_test.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 2cebd9ef0cf..e510ccbe46e 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -228,7 +228,7 @@ class KDCBaseTest(RawKerberosTest): return default_enctypes - def create_account(self, ldb, name, machine_account=False, + def create_account(self, samdb, name, machine_account=False, spn=None, upn=None, additional_details=None, ou=None, account_control=0): '''Create an account for testing. @@ -239,13 +239,13 @@ class KDCBaseTest(RawKerberosTest): guid = (DS_GUID_COMPUTERS_CONTAINER if machine_account else DS_GUID_USERS_CONTAINER) - ou = ldb.get_wellknown_dn(ldb.get_default_basedn(), guid) + ou = samdb.get_wellknown_dn(samdb.get_default_basedn(), guid) dn = "CN=%s,%s" % (name, ou) # remove the account if it exists, this will happen if a previous test # run failed - delete_force(ldb, dn) + delete_force(samdb, dn) if machine_account: object_class = "computer" account_name = "%s$" % name @@ -270,19 +270,19 @@ class KDCBaseTest(RawKerberosTest): details["userPrincipalName"] = upn if additional_details is not None: details.update(additional_details) - ldb.add(details) + samdb.add(details) creds = KerberosCredentials() creds.guess(self.get_lp()) - creds.set_realm(ldb.domain_dns_name().upper()) - creds.set_domain(ldb.domain_netbios_name().upper()) + creds.set_realm(samdb.domain_dns_name().upper()) + creds.set_domain(samdb.domain_netbios_name().upper()) creds.set_password(password) creds.set_username(account_name) if machine_account: creds.set_workstation(name) else: creds.set_workstation('') - creds.set_dn(dn) + creds.set_dn(ldb.Dn(samdb, dn)) # # Save the account name so it can be deleted in tearDownClass self.accounts.add(dn) -- 2.25.1 From 6bbb01604740dc47ba906d8dc45dbe297ea6e4c3 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 12:38:38 +1200 Subject: [PATCH 047/159] tests/krb5: Allow get_service_ticket() to get tickets from the RODC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 5d3a135c2326edc9ca8f56bea24d2f52320f4fd6) --- python/samba/tests/krb5/fast_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index dedf7a57a4b..74fe11c5a90 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1497,7 +1497,7 @@ class FAST_Tests(KDCBaseTest): self.assertTrue( security.KERB_ENCTYPE_CLAIMS_SUPPORTED & krbtgt_etypes) - def get_service_ticket(self, tgt, target_creds, service='host'): + def get_service_ticket(self, tgt, target_creds, service='host', to_rodc=False): etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) key = tgt.session_key @@ -1510,7 +1510,7 @@ class FAST_Tests(KDCBaseTest): sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, names=[service, target_name]) - rep, enc_part = self.tgs_req(cname, sname, realm, ticket, key, etype) + rep, enc_part = self.tgs_req(cname, sname, realm, ticket, key, etype, to_rodc=to_rodc) service_ticket = rep['ticket'] -- 2.25.1 From ffb969da6e58d03f667d60a93ae0764d3246a4c3 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 12:41:46 +1200 Subject: [PATCH 048/159] tests/krb5: Allow get_tgt() to get tickets from the RODC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 2d69805b1e3a8022f1418605e5f29ae0bbaa4a06) --- python/samba/tests/krb5/fast_tests.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 74fe11c5a90..9109a63d704 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1530,7 +1530,7 @@ class FAST_Tests(KDCBaseTest): return service_ticket_creds - def get_tgt(self, creds): + def get_tgt(self, creds, to_rodc=False): user_name = creds.get_username() realm = creds.get_realm() @@ -1544,7 +1544,10 @@ class FAST_Tests(KDCBaseTest): till = self.get_KerberosTime(offset=36000) - krbtgt_creds = self.get_krbtgt_creds() + if to_rodc: + krbtgt_creds = self.get_rodc_krbtgt_creds() + else: + krbtgt_creds = self.get_krbtgt_creds() ticket_decryption_key = ( self.TicketDecryptionKey_from_creds(krbtgt_creds)) @@ -1573,7 +1576,8 @@ class FAST_Tests(KDCBaseTest): preauth_key=None, ticket_decryption_key=ticket_decryption_key, pac_request=True, - pac_options=pac_options) + pac_options=pac_options, + to_rodc=to_rodc) self.check_pre_authentication(rep) etype_info2 = kdc_exchange_dict['preauth_etype_info2'] @@ -1609,7 +1613,8 @@ class FAST_Tests(KDCBaseTest): preauth_key=preauth_key, ticket_decryption_key=ticket_decryption_key, pac_request=True, - pac_options=pac_options) + pac_options=pac_options, + to_rodc=to_rodc) self.check_as_reply(rep) tgt = rep['ticket'] -- 2.25.1 From e1651b016c79b6f16ebb002a22e6327895fe206a Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 13:14:06 +1200 Subject: [PATCH 049/159] tests/krb5: Allow get_tgt() to specify different kdc-options BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 4ecfa82e71b0dd5b71aa97973033c5c72257a0c3) --- python/samba/tests/krb5/fast_tests.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 9109a63d704..826c3536fb9 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1530,7 +1530,7 @@ class FAST_Tests(KDCBaseTest): return service_ticket_creds - def get_tgt(self, creds, to_rodc=False): + def get_tgt(self, creds, to_rodc=False, kdc_options=None): user_name = creds.get_username() realm = creds.get_realm() @@ -1551,10 +1551,12 @@ class FAST_Tests(KDCBaseTest): ticket_decryption_key = ( self.TicketDecryptionKey_from_creds(krbtgt_creds)) - kdc_options = str(krb5_asn1.KDCOptions('forwardable,' + if kdc_options is None: + kdc_options = krb5_asn1.KDCOptions('forwardable,' 'renewable,' 'canonicalize,' - 'renewable-ok')) + 'renewable-ok') + kdc_options = str(kdc_options) pac_options = '1' # supports claims -- 2.25.1 From 2b9e63cca00a3cf1a8c568ba2c92f1554a459017 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 13:14:45 +1200 Subject: [PATCH 050/159] tests/krb5: Allow get_tgt() to specify expected and unexpected flags BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 035a8f198555ad1eedf8e2e6c565fbbbe4fbe7ce) --- python/samba/tests/krb5/fast_tests.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 826c3536fb9..fd96c2b9c1a 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1530,7 +1530,8 @@ class FAST_Tests(KDCBaseTest): return service_ticket_creds - def get_tgt(self, creds, to_rodc=False, kdc_options=None): + def get_tgt(self, creds, to_rodc=False, kdc_options=None, + expected_flags=None, unexpected_flags=None): user_name = creds.get_username() realm = creds.get_realm() @@ -1572,6 +1573,8 @@ class FAST_Tests(KDCBaseTest): expected_srealm=realm, expected_sname=sname, expected_salt=salt, + expected_flags=expected_flags, + unexpected_flags=unexpected_flags, etypes=etype, padata=None, kdc_options=kdc_options, -- 2.25.1 From 3c8751165de2de474496ff5d0cd462102ecbbe98 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 13:24:46 +1200 Subject: [PATCH 051/159] tests/krb5: Move get_tgt() and get_service_ticket() to kdc_base_test BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 59c1043be25b92db75ab5676601cb15426ef37a3) --- python/samba/tests/krb5/fast_tests.py | 141 ---------------------- python/samba/tests/krb5/kdc_base_test.py | 144 +++++++++++++++++++++++ 2 files changed, 144 insertions(+), 141 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index fd96c2b9c1a..a74dc2a3cd0 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1497,147 +1497,6 @@ class FAST_Tests(KDCBaseTest): self.assertTrue( security.KERB_ENCTYPE_CLAIMS_SUPPORTED & krbtgt_etypes) - def get_service_ticket(self, tgt, target_creds, service='host', to_rodc=False): - etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) - - key = tgt.session_key - ticket = tgt.ticket - - cname = tgt.cname - realm = tgt.crealm - - target_name = target_creds.get_username()[:-1] - sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, - names=[service, target_name]) - - rep, enc_part = self.tgs_req(cname, sname, realm, ticket, key, etype, to_rodc=to_rodc) - - service_ticket = rep['ticket'] - - ticket_etype = service_ticket['enc-part']['etype'] - target_key = self.TicketDecryptionKey_from_creds(target_creds, - etype=ticket_etype) - - session_key = self.EncryptionKey_import(enc_part['key']) - - service_ticket_creds = KerberosTicketCreds(service_ticket, - session_key, - crealm=realm, - cname=cname, - srealm=realm, - sname=sname, - decryption_key=target_key) - - return service_ticket_creds - - def get_tgt(self, creds, to_rodc=False, kdc_options=None, - expected_flags=None, unexpected_flags=None): - user_name = creds.get_username() - realm = creds.get_realm() - - salt = creds.get_salt() - - etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) - cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, - names=[user_name]) - sname = self.PrincipalName_create(name_type=NT_SRV_INST, - names=['krbtgt', realm]) - - till = self.get_KerberosTime(offset=36000) - - if to_rodc: - krbtgt_creds = self.get_rodc_krbtgt_creds() - else: - krbtgt_creds = self.get_krbtgt_creds() - ticket_decryption_key = ( - self.TicketDecryptionKey_from_creds(krbtgt_creds)) - - if kdc_options is None: - kdc_options = krb5_asn1.KDCOptions('forwardable,' - 'renewable,' - 'canonicalize,' - 'renewable-ok') - kdc_options = str(kdc_options) - - pac_options = '1' # supports claims - - rep, kdc_exchange_dict = self._test_as_exchange( - cname=cname, - realm=realm, - sname=sname, - till=till, - client_as_etypes=etype, - expected_error_mode=KDC_ERR_PREAUTH_REQUIRED, - expected_crealm=realm, - expected_cname=cname, - expected_srealm=realm, - expected_sname=sname, - expected_salt=salt, - expected_flags=expected_flags, - unexpected_flags=unexpected_flags, - etypes=etype, - padata=None, - kdc_options=kdc_options, - preauth_key=None, - ticket_decryption_key=ticket_decryption_key, - pac_request=True, - pac_options=pac_options, - to_rodc=to_rodc) - self.check_pre_authentication(rep) - - etype_info2 = kdc_exchange_dict['preauth_etype_info2'] - - preauth_key = self.PasswordKey_from_etype_info2(creds, - etype_info2[0], - creds.get_kvno()) - - ts_enc_padata = self.get_enc_timestamp_pa_data(creds, rep) - - padata = [ts_enc_padata] - - expected_realm = realm.upper() - - expected_sname = self.PrincipalName_create( - name_type=NT_SRV_INST, names=['krbtgt', realm.upper()]) - - rep, kdc_exchange_dict = self._test_as_exchange( - cname=cname, - realm=realm, - sname=sname, - till=till, - client_as_etypes=etype, - expected_error_mode=0, - expected_crealm=expected_realm, - expected_cname=cname, - expected_srealm=expected_realm, - expected_sname=expected_sname, - expected_salt=salt, - etypes=etype, - padata=padata, - kdc_options=kdc_options, - preauth_key=preauth_key, - ticket_decryption_key=ticket_decryption_key, - pac_request=True, - pac_options=pac_options, - to_rodc=to_rodc) - self.check_as_reply(rep) - - tgt = rep['ticket'] - - enc_part = self.get_as_rep_enc_data(preauth_key, rep) - session_key = self.EncryptionKey_import(enc_part['key']) - - ticket_creds = KerberosTicketCreds( - tgt, - session_key, - crealm=realm, - cname=cname, - srealm=realm, - sname=sname, - decryption_key=ticket_decryption_key) - - return ticket_creds, enc_part - def get_mach_tgt(self): if self.mach_tgt is None: mach_creds = self.get_mach_creds() diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index e510ccbe46e..fbcc89a9b31 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -73,6 +73,7 @@ from samba.tests.krb5.rfc4120_constants import ( KU_TICKET, NT_PRINCIPAL, NT_SRV_HST, + NT_SRV_INST, PADATA_ENCRYPTED_CHALLENGE, PADATA_ENC_TIMESTAMP, PADATA_ETYPE_INFO2, @@ -1123,6 +1124,149 @@ class KDCBaseTest(RawKerberosTest): return rep, enc_part + def get_service_ticket(self, tgt, target_creds, service='host', + to_rodc=False): + etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) + + key = tgt.session_key + ticket = tgt.ticket + + cname = tgt.cname + realm = tgt.crealm + + target_name = target_creds.get_username()[:-1] + sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, + names=[service, target_name]) + + rep, enc_part = self.tgs_req(cname, sname, realm, ticket, key, etype, + to_rodc=to_rodc) + + service_ticket = rep['ticket'] + + ticket_etype = service_ticket['enc-part']['etype'] + target_key = self.TicketDecryptionKey_from_creds(target_creds, + etype=ticket_etype) + + session_key = self.EncryptionKey_import(enc_part['key']) + + service_ticket_creds = KerberosTicketCreds(service_ticket, + session_key, + crealm=realm, + cname=cname, + srealm=realm, + sname=sname, + decryption_key=target_key) + + return service_ticket_creds + + def get_tgt(self, creds, to_rodc=False, kdc_options=None, + expected_flags=None, unexpected_flags=None): + user_name = creds.get_username() + realm = creds.get_realm() + + salt = creds.get_salt() + + etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) + cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, + names=[user_name]) + sname = self.PrincipalName_create(name_type=NT_SRV_INST, + names=['krbtgt', realm]) + + till = self.get_KerberosTime(offset=36000) + + if to_rodc: + krbtgt_creds = self.get_rodc_krbtgt_creds() + else: + krbtgt_creds = self.get_krbtgt_creds() + ticket_decryption_key = ( + self.TicketDecryptionKey_from_creds(krbtgt_creds)) + + if kdc_options is None: + kdc_options = krb5_asn1.KDCOptions('forwardable,' + 'renewable,' + 'canonicalize,' + 'renewable-ok') + kdc_options = str(kdc_options) + + pac_options = '1' # supports claims + + rep, kdc_exchange_dict = self._test_as_exchange( + cname=cname, + realm=realm, + sname=sname, + till=till, + client_as_etypes=etype, + expected_error_mode=KDC_ERR_PREAUTH_REQUIRED, + expected_crealm=realm, + expected_cname=cname, + expected_srealm=realm, + expected_sname=sname, + expected_salt=salt, + expected_flags=expected_flags, + unexpected_flags=unexpected_flags, + etypes=etype, + padata=None, + kdc_options=kdc_options, + preauth_key=None, + ticket_decryption_key=ticket_decryption_key, + pac_request=True, + pac_options=pac_options, + to_rodc=to_rodc) + self.check_pre_authentication(rep) + + etype_info2 = kdc_exchange_dict['preauth_etype_info2'] + + preauth_key = self.PasswordKey_from_etype_info2(creds, + etype_info2[0], + creds.get_kvno()) + + ts_enc_padata = self.get_enc_timestamp_pa_data(creds, rep) + + padata = [ts_enc_padata] + + expected_realm = realm.upper() + + expected_sname = self.PrincipalName_create( + name_type=NT_SRV_INST, names=['krbtgt', realm.upper()]) + + rep, kdc_exchange_dict = self._test_as_exchange( + cname=cname, + realm=realm, + sname=sname, + till=till, + client_as_etypes=etype, + expected_error_mode=0, + expected_crealm=expected_realm, + expected_cname=cname, + expected_srealm=expected_realm, + expected_sname=expected_sname, + expected_salt=salt, + etypes=etype, + padata=padata, + kdc_options=kdc_options, + preauth_key=preauth_key, + ticket_decryption_key=ticket_decryption_key, + pac_request=True, + pac_options=pac_options, + to_rodc=to_rodc) + self.check_as_reply(rep) + + tgt = rep['ticket'] + + enc_part = self.get_as_rep_enc_data(preauth_key, rep) + session_key = self.EncryptionKey_import(enc_part['key']) + + ticket_creds = KerberosTicketCreds( + tgt, + session_key, + crealm=realm, + cname=cname, + srealm=realm, + sname=sname, + decryption_key=ticket_decryption_key) + + return ticket_creds, enc_part + # Named tuple to contain values of interest when the PAC is decoded. PacData = namedtuple( "PacData", -- 2.25.1 From f07f057d16d462a6824f7eb7d0ec6a4cc23937ec Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 21 Sep 2021 11:51:05 +1200 Subject: [PATCH 052/159] tests/krb5: Return encpart from get_tgt() as part of KerberosTicketCreds The encpart is already contained in ticket_creds, so it no longer needs to be returned as a separate value. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 6193f7433b15579aa32b26a146287923c9d3844d) --- python/samba/tests/krb5/fast_tests.py | 8 ++------ python/samba/tests/krb5/kdc_base_test.py | 16 ++-------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index a74dc2a3cd0..42e75e7513c 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -67,11 +67,9 @@ class FAST_Tests(KDCBaseTest): super().setUpClass() cls.user_tgt = None - cls.user_enc_part = None cls.user_service_ticket = None cls.mach_tgt = None - cls.mach_enc_part = None cls.mach_service_ticket = None def setUp(self): @@ -1500,16 +1498,14 @@ class FAST_Tests(KDCBaseTest): def get_mach_tgt(self): if self.mach_tgt is None: mach_creds = self.get_mach_creds() - type(self).mach_tgt, type(self).mach_enc_part = ( - self.get_tgt(mach_creds)) + type(self).mach_tgt = self.get_tgt(mach_creds) return self.mach_tgt def get_user_tgt(self): if self.user_tgt is None: user_creds = self.get_client_creds() - type(self).user_tgt, type(self).user_enc_part = ( - self.get_tgt(user_creds)) + type(self).user_tgt = self.get_tgt(user_creds) return self.user_tgt diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index fbcc89a9b31..28d34210fce 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1251,21 +1251,9 @@ class KDCBaseTest(RawKerberosTest): to_rodc=to_rodc) self.check_as_reply(rep) - tgt = rep['ticket'] + ticket_creds = kdc_exchange_dict['rep_ticket_creds'] - enc_part = self.get_as_rep_enc_data(preauth_key, rep) - session_key = self.EncryptionKey_import(enc_part['key']) - - ticket_creds = KerberosTicketCreds( - tgt, - session_key, - crealm=realm, - cname=cname, - srealm=realm, - sname=sname, - decryption_key=ticket_decryption_key) - - return ticket_creds, enc_part + return ticket_creds # Named tuple to contain values of interest when the PAC is decoded. PacData = namedtuple( -- 2.25.1 From 97c1f50ab88bd80d009c8c4e92697d9ad0cb2d8e Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 21 Sep 2021 11:51:20 +1200 Subject: [PATCH 053/159] tests/krb5: Cache obtained tickets Now tickets obtained with get_tgt() and get_service_ticket() make use of a cache so they can be reused, unless the 'fresh' parameter is specified as true. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit 419e4061ced466ec7e5e23f815823b540ef4751c) --- python/samba/tests/krb5/kdc_base_test.py | 29 ++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 28d34210fce..59175c7bb2f 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -110,6 +110,7 @@ class KDCBaseTest(RawKerberosTest): cls.accounts = set() cls.account_cache = {} + cls.tkt_cache = {} cls._rodc_ctx = None @@ -1125,7 +1126,17 @@ class KDCBaseTest(RawKerberosTest): return rep, enc_part def get_service_ticket(self, tgt, target_creds, service='host', - to_rodc=False): + to_rodc=False, fresh=False): + user_name = tgt.cname['name-string'][0] + target_name = target_creds.get_username() + cache_key = (user_name, target_name, service, to_rodc) + + if not fresh: + ticket = self.tkt_cache.get(cache_key) + + if ticket is not None: + return ticket + etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) key = tgt.session_key @@ -1157,11 +1168,23 @@ class KDCBaseTest(RawKerberosTest): sname=sname, decryption_key=target_key) + self.tkt_cache[cache_key] = service_ticket_creds + return service_ticket_creds def get_tgt(self, creds, to_rodc=False, kdc_options=None, - expected_flags=None, unexpected_flags=None): + expected_flags=None, unexpected_flags=None, + fresh=False): user_name = creds.get_username() + cache_key = (user_name, to_rodc, kdc_options, + expected_flags, unexpected_flags) + + if not fresh: + tgt = self.tkt_cache.get(cache_key) + + if tgt is not None: + return tgt + realm = creds.get_realm() salt = creds.get_salt() @@ -1253,6 +1276,8 @@ class KDCBaseTest(RawKerberosTest): ticket_creds = kdc_exchange_dict['rep_ticket_creds'] + self.tkt_cache[cache_key] = ticket_creds + return ticket_creds # Named tuple to contain values of interest when the PAC is decoded. -- 2.25.1 From 9fd4feacc9e9b35b7f6fca69bc5206c2ee21ee3b Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 16:54:57 +1200 Subject: [PATCH 054/159] tests/krb5: Add methods for creating zeroed checksums and verifying checksums Creating a zeroed checksum is needed for signing a PAC. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett (cherry picked from commit a562882b15125902c5d89f094b8c9b1150f5d010) --- python/samba/tests/krb5/raw_testcase.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 39821240941..be49f16b1f7 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -232,12 +232,29 @@ class Krb5EncryptionKey: plaintext = kcrypto.decrypt(self.key, usage, ciphertext) return plaintext + def make_zeroed_checksum(self, ctype=None): + if ctype is None: + ctype = self.ctype + + checksum_len = kcrypto.checksum_len(ctype) + return bytes(checksum_len) + def make_checksum(self, usage, plaintext, ctype=None): if ctype is None: ctype = self.ctype cksum = kcrypto.make_checksum(ctype, self.key, usage, plaintext) return cksum + def verify_checksum(self, usage, plaintext, ctype, cksum): + if self.ctype != ctype: + raise AssertionError(f'{self.ctype} != {ctype}') + + kcrypto.verify_checksum(ctype, + self.key, + usage, + plaintext, + cksum) + def export_obj(self): EncryptionKey_obj = { 'keytype': self.etype, -- 2.25.1 From 58347c25364de6c3795b797c99aa5cb0960c69d1 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 16 Sep 2021 17:20:22 +1200 Subject: [PATCH 055/159] tests/krb5: Add RodcPacEncryptionKey type allowing for RODC PAC signatures Signatures created by an RODC have an RODCIdentifier appended to them identifying the RODC's krbtgt account. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Isaac Boukris Reviewed-by: Andrew Bartlett Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Tue Sep 21 23:55:39 UTC 2021 on sn-devel-184 (cherry picked from commit ec95b3042bf2649c0600cafb12818c27242b5098) --- python/samba/tests/krb5/raw_testcase.py | 45 +++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index be49f16b1f7..e213b5eef9b 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -263,6 +263,45 @@ class Krb5EncryptionKey: return EncryptionKey_obj +class RodcPacEncryptionKey(Krb5EncryptionKey): + def __init__(self, key, kvno, rodc_id=None): + super().__init__(key, kvno) + + if rodc_id is None: + kvno = self.kvno + if kvno is not None: + kvno >>= 16 + kvno &= (1 << 16) - 1 + + rodc_id = kvno or None + + if rodc_id is not None: + self.rodc_id = rodc_id.to_bytes(2, byteorder='little') + else: + self.rodc_id = b'' + + def make_zeroed_checksum(self, ctype=None): + checksum = super().make_zeroed_checksum(ctype) + return checksum + bytes(len(self.rodc_id)) + + def make_checksum(self, usage, plaintext, ctype=None): + checksum = super().make_checksum(usage, plaintext, ctype) + return checksum + self.rodc_id + + def verify_checksum(self, usage, plaintext, ctype, cksum): + if self.rodc_id: + cksum, cksum_rodc_id = cksum[:-2], cksum[-2:] + + if self.rodc_id != cksum_rodc_id: + raise AssertionError(f'{self.rodc_id.hex()} != ' + f'{cksum_rodc_id.hex()}') + + super().verify_checksum(usage, + plaintext, + ctype, + cksum) + + class KerberosCredentials(Credentials): def __init__(self): super(KerberosCredentials, self).__init__() @@ -325,7 +364,7 @@ class KerberosCredentials(Credentials): etype = int(etype) contents = binascii.a2b_hex(hexkey) key = kcrypto.Key(etype, contents) - self.forced_keys[etype] = Krb5EncryptionKey(key, self.kvno) + self.forced_keys[etype] = RodcPacEncryptionKey(key, self.kvno) def get_forced_key(self, etype): etype = int(etype) @@ -982,13 +1021,13 @@ class RawKerberosTest(TestCaseInTempDir): def SessionKey_create(self, etype, contents, kvno=None): key = kcrypto.Key(etype, contents) - return Krb5EncryptionKey(key, kvno) + return RodcPacEncryptionKey(key, kvno) def PasswordKey_create(self, etype=None, pwd=None, salt=None, kvno=None): self.assertIsNotNone(pwd) self.assertIsNotNone(salt) key = kcrypto.string_to_key(etype, pwd, salt) - return Krb5EncryptionKey(key, kvno) + return RodcPacEncryptionKey(key, kvno) def PasswordKey_from_etype_info2(self, creds, etype_info2, kvno=None): e = etype_info2['etype'] -- 2.25.1 From 5cfcba51d0251f4410e65440d7c28fae8b2a5270 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 17 Sep 2021 14:56:51 +1200 Subject: [PATCH 056/159] tests/krb5: Add method to verify ticket PAC checksums BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 12b5e72a35d632516980f6c051a5d83f913079e7) --- python/samba/tests/krb5/raw_testcase.py | 118 +++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index e213b5eef9b..265789cdb26 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -34,8 +34,9 @@ from pyasn1.codec.native.encoder import encode as pyasn1_native_encode from pyasn1.codec.ber.encoder import BitStringEncoder from samba.credentials import Credentials -from samba.dcerpc import security +from samba.dcerpc import krb5pac, security from samba.gensec import FEATURE_SEAL +from samba.ndr import ndr_pack, ndr_unpack import samba.tests from samba.tests import TestCaseInTempDir @@ -418,6 +419,10 @@ class KerberosTicketCreds: class RawKerberosTest(TestCaseInTempDir): """A raw Kerberos Test case.""" + pac_checksum_types = {krb5pac.PAC_TYPE_SRV_CHECKSUM, + krb5pac.PAC_TYPE_KDC_CHECKSUM, + krb5pac.PAC_TYPE_TICKET_CHECKSUM} + etypes_to_test = ( {"value": -1111, "name": "dummy", }, {"value": kcrypto.Enctype.AES256, "name": "aes128", }, @@ -2900,6 +2905,114 @@ class RawKerberosTest(TestCaseInTempDir): ticket_blob) self.assertEqual(expected_checksum, checksum) + def verify_ticket(self, ticket, krbtgt_key, expect_pac=True): + # Check if the ticket is a TGT. + sname = ticket.ticket['sname'] + is_tgt = sname['name-string'][0] == b'krbtgt' + + # Decrypt the ticket. + + key = ticket.decryption_key + enc_part = ticket.ticket['enc-part'] + + self.assertElementEqual(enc_part, 'etype', key.etype) + self.assertElementKVNO(enc_part, 'kvno', key.kvno) + + enc_part = key.decrypt(KU_TICKET, enc_part['cipher']) + enc_part = self.der_decode( + enc_part, asn1Spec=krb5_asn1.EncTicketPart()) + + # Fetch the authorization data from the ticket. + auth_data = enc_part.get('authorization-data') + if expect_pac: + self.assertIsNotNone(auth_data) + elif auth_data is None: + return + + # Get a copy of the authdata with an empty PAC, and the existing PAC + # (if present). + empty_pac = self.get_empty_pac() + auth_data, pac_data = self.replace_pac(auth_data, + empty_pac, + expect_pac=expect_pac) + if not expect_pac: + return + + # Unpack the PAC as both PAC_DATA and PAC_DATA_RAW types. We use the + # raw type to create a new PAC with zeroed signatures for + # verification. This is because on Windows, the resource_groups field + # is added to PAC_LOGON_INFO after the info3 field has been created, + # which results in a different ordering of pointer values than Samba + # (see commit 0e201ecdc53). Using the raw type avoids changing + # PAC_LOGON_INFO, so verification against Windows can work. We still + # need the PAC_DATA type to retrieve the actual checksums, because the + # signatures in the raw type may contain padding bytes. + pac = ndr_unpack(krb5pac.PAC_DATA, + pac_data) + raw_pac = ndr_unpack(krb5pac.PAC_DATA_RAW, + pac_data) + + checksums = {} + + 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: + self.assertNotIn(buffer_type, checksums, + f'Duplicate checksum type {buffer_type}') + + # Fetch the checksum and the checksum type from the PAC buffer. + checksum = pac_buffer.info.signature + ctype = pac_buffer.info.type + if ctype & 1 << 31: + ctype |= -1 << 31 + + checksums[buffer_type] = checksum, ctype + + if 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. + + signature = ndr_unpack( + krb5pac.PAC_SIGNATURE_DATA, + raw_pac_buffer.info.remaining) + signature.signature = bytes(len(checksum)) + raw_pac_buffer.info.remaining = ndr_pack( + signature) + + # Re-encode the PAC. + pac_data = ndr_pack(raw_pac) + + # Verify the signatures. + + server_checksum, server_ctype = checksums[ + krb5pac.PAC_TYPE_SRV_CHECKSUM] + Krb5EncryptionKey.verify_checksum(key, + KU_NON_KERB_CKSUM_SALT, + pac_data, + server_ctype, + server_checksum) + + kdc_checksum, kdc_ctype = checksums[ + krb5pac.PAC_TYPE_KDC_CHECKSUM] + krbtgt_key.verify_checksum(KU_NON_KERB_CKSUM_SALT, + server_checksum, + kdc_ctype, + kdc_checksum) + + if is_tgt: + self.assertNotIn(krb5pac.PAC_TYPE_TICKET_CHECKSUM, checksums) + else: + ticket_checksum, ticket_ctype = checksums[ + krb5pac.PAC_TYPE_TICKET_CHECKSUM] + enc_part['authorization-data'] = auth_data + enc_part = self.der_encode(enc_part, + asn1Spec=krb5_asn1.EncTicketPart()) + + krbtgt_key.verify_checksum(KU_NON_KERB_CKSUM_SALT, + enc_part, + ticket_ctype, + ticket_checksum) + def replace_pac(self, auth_data, new_pac, expect_pac=True): if new_pac is not None: self.assertElementEqual(new_pac, 'ad-type', AD_WIN2K_PAC) @@ -2943,6 +3056,9 @@ class RawKerberosTest(TestCaseInTempDir): return new_auth_data, old_pac + def get_empty_pac(self): + return self.AuthorizationData_create(AD_WIN2K_PAC, bytes(1)) + def get_outer_pa_dict(self, kdc_exchange_dict): return self.get_pa_dict(kdc_exchange_dict['req_padata']) -- 2.25.1 From 79800968075d3cd3517e4bbf5811b2e31b536950 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 17 Sep 2021 15:26:12 +1200 Subject: [PATCH 057/159] tests/krb5: Add method for modifying a ticket and creating PAC checksums BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 1fcde7cb6ce50e0a08097841e92476f320560664) --- python/samba/tests/krb5/raw_testcase.py | 234 ++++++++++++++++++++++++ 1 file changed, 234 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 265789cdb26..4ac7698ffab 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -3013,6 +3013,240 @@ class RawKerberosTest(TestCaseInTempDir): ticket_ctype, ticket_checksum) + def modified_ticket(self, + ticket, *, + new_ticket_key=None, + modify_fn=None, + modify_pac_fn=None, + exclude_pac=False, + update_pac_checksums=True, + checksum_keys=None, + include_checksums=None): + if checksum_keys is None: + # A dict containing a key for each checksum type to be created in + # the PAC. + checksum_keys = {} + + if include_checksums is None: + # A dict containing a value for each checksum type; True if the + # checksum type is to be included in the PAC, False if it is to be + # excluded, or None/not present if the checksum is to be included + # based on its presence in the original PAC. + include_checksums = {} + + # Check that the values passed in by the caller make sense. + + self.assertLessEqual(checksum_keys.keys(), self.pac_checksum_types) + self.assertLessEqual(include_checksums.keys(), self.pac_checksum_types) + + if exclude_pac: + self.assertIsNone(modify_pac_fn) + + update_pac_checksums = False + + if not update_pac_checksums: + self.assertFalse(checksum_keys) + self.assertFalse(include_checksums) + + expect_pac = update_pac_checksums or modify_pac_fn is not None + + key = ticket.decryption_key + + if new_ticket_key is None: + # Use the same key to re-encrypt the ticket. + new_ticket_key = key + + if krb5pac.PAC_TYPE_SRV_CHECKSUM not in checksum_keys: + # If the server signature key is not present, fall back to the key + # used to encrypt the ticket. + checksum_keys[krb5pac.PAC_TYPE_SRV_CHECKSUM] = new_ticket_key + + if krb5pac.PAC_TYPE_TICKET_CHECKSUM not in checksum_keys: + # If the ticket 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_TICKET_CHECKSUM] = ( + kdc_checksum_key) + + # Decrypt the ticket. + + enc_part = ticket.ticket['enc-part'] + + self.assertElementEqual(enc_part, 'etype', key.etype) + self.assertElementKVNO(enc_part, 'kvno', key.kvno) + + enc_part = key.decrypt(KU_TICKET, enc_part['cipher']) + enc_part = self.der_decode( + enc_part, asn1Spec=krb5_asn1.EncTicketPart()) + + # Modify the ticket here. + if modify_fn is not None: + enc_part = modify_fn(enc_part) + + auth_data = enc_part.get('authorization-data') + if expect_pac: + self.assertIsNotNone(auth_data) + if auth_data is not None: + new_pac = None + if not exclude_pac: + # Get a copy of the authdata with an empty PAC, and the + # existing PAC (if present). + empty_pac = self.get_empty_pac() + empty_pac_auth_data, pac_data = self.replace_pac(auth_data, + empty_pac) + + if expect_pac: + self.assertIsNotNone(pac_data) + if pac_data is not None: + pac = ndr_unpack(krb5pac.PAC_DATA, pac_data) + + # Modify the PAC here. + if modify_pac_fn is not None: + pac = modify_pac_fn(pac) + + if update_pac_checksums: + # Get the enc-part with an empty PAC, which is needed + # to create a ticket signature. + enc_part_to_sign = enc_part.copy() + enc_part_to_sign['authorization-data'] = ( + empty_pac_auth_data) + enc_part_to_sign = self.der_encode( + enc_part_to_sign, + asn1Spec=krb5_asn1.EncTicketPart()) + + self.update_pac_checksums(pac, + checksum_keys, + include_checksums, + enc_part_to_sign) + + # Re-encode the PAC. + pac_data = ndr_pack(pac) + new_pac = self.AuthorizationData_create(AD_WIN2K_PAC, + pac_data) + + # Replace the PAC in the authorization data and re-add it to the + # ticket enc-part. + auth_data, _ = self.replace_pac(auth_data, new_pac) + enc_part['authorization-data'] = auth_data + + # Re-encrypt the ticket enc-part with the new key. + enc_part_new = self.der_encode(enc_part, + asn1Spec=krb5_asn1.EncTicketPart()) + enc_part_new = self.EncryptedData_create(new_ticket_key, + KU_TICKET, + enc_part_new) + + # Create a copy of the ticket with the new enc-part. + new_ticket = ticket.ticket.copy() + new_ticket['enc-part'] = enc_part_new + + new_ticket_creds = KerberosTicketCreds( + new_ticket, + session_key=ticket.session_key, + crealm=ticket.crealm, + cname=ticket.cname, + srealm=ticket.srealm, + sname=ticket.sname, + decryption_key=new_ticket_key, + ticket_private=enc_part, + encpart_private=ticket.encpart_private) + + return new_ticket_creds + + def update_pac_checksums(self, + pac, + checksum_keys, + include_checksums, + enc_part=None): + pac_buffers = pac.buffers + checksum_buffers = {} + + # Find the relevant PAC checksum buffers. + for pac_buffer in pac_buffers: + buffer_type = pac_buffer.type + if buffer_type in self.pac_checksum_types: + self.assertNotIn(buffer_type, checksum_buffers, + f'Duplicate checksum type {buffer_type}') + + checksum_buffers[buffer_type] = pac_buffer + + # Create any additional buffers that were requested but not + # present. Conversely, remove any buffers that were requested to be + # removed. + for buffer_type in self.pac_checksum_types: + if buffer_type in checksum_buffers: + if include_checksums.get(buffer_type) is False: + checksum_buffer = checksum_buffers.pop(buffer_type) + + pac.num_buffers -= 1 + pac_buffers.remove(checksum_buffer) + + elif include_checksums.get(buffer_type) is True: + info = krb5pac.PAC_SIGNATURE_DATA() + + checksum_buffer = krb5pac.PAC_BUFFER() + checksum_buffer.type = buffer_type + checksum_buffer.info = info + + pac_buffers.append(checksum_buffer) + pac.num_buffers += 1 + + checksum_buffers[buffer_type] = checksum_buffer + + # Fill the relevant checksum buffers. + for buffer_type, checksum_buffer in checksum_buffers.items(): + checksum_key = checksum_keys[buffer_type] + ctype = checksum_key.ctype & ((1 << 32) - 1) + + if buffer_type == krb5pac.PAC_TYPE_TICKET_CHECKSUM: + self.assertIsNotNone(enc_part) + + signature = checksum_key.make_checksum( + KU_NON_KERB_CKSUM_SALT, + enc_part) + + elif buffer_type == krb5pac.PAC_TYPE_SRV_CHECKSUM: + signature = Krb5EncryptionKey.make_zeroed_checksum( + checksum_key) + + else: + signature = checksum_key.make_zeroed_checksum() + + checksum_buffer.info.signature = signature + checksum_buffer.info.type = ctype + + # Add the new checksum buffers to the PAC. + pac.buffers = pac_buffers + + # Calculate the server and KDC checksums and insert them into the PAC. + + server_checksum_buffer = checksum_buffers.get( + krb5pac.PAC_TYPE_SRV_CHECKSUM) + if server_checksum_buffer is not None: + server_checksum_key = checksum_keys[krb5pac.PAC_TYPE_SRV_CHECKSUM] + + pac_data = ndr_pack(pac) + server_checksum = Krb5EncryptionKey.make_checksum( + server_checksum_key, + KU_NON_KERB_CKSUM_SALT, + pac_data) + + server_checksum_buffer.info.signature = server_checksum + + kdc_checksum_buffer = checksum_buffers.get( + krb5pac.PAC_TYPE_KDC_CHECKSUM) + if kdc_checksum_buffer is not None: + self.assertIsNotNone(server_checksum_buffer) + + kdc_checksum_key = checksum_keys[krb5pac.PAC_TYPE_KDC_CHECKSUM] + + kdc_checksum = kdc_checksum_key.make_checksum( + KU_NON_KERB_CKSUM_SALT, + server_checksum) + + kdc_checksum_buffer.info.signature = kdc_checksum + def replace_pac(self, auth_data, new_pac, expect_pac=True): if new_pac is not None: self.assertElementEqual(new_pac, 'ad-type', AD_WIN2K_PAC) -- 2.25.1 From 717714f91b1eb1bb76f674fd005353ee2f35be39 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 21 Sep 2021 13:33:16 +1200 Subject: [PATCH 058/159] tests/krb5: Simplify adding authdata to ticket by using modified_ticket() BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 4c67a53cdca206a118e82b356db0faf0ddc011ab) --- python/samba/tests/krb5/fast_tests.py | 49 +++++-------------------- python/samba/tests/krb5/raw_testcase.py | 8 ++++ 2 files changed, 18 insertions(+), 39 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 42e75e7513c..d8ccfaee325 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -25,10 +25,7 @@ import collections import ldb from samba.dcerpc import security -from samba.tests.krb5.raw_testcase import ( - KerberosTicketCreds, - Krb5EncryptionKey -) +from samba.tests.krb5.raw_testcase import Krb5EncryptionKey from samba.tests.krb5.kdc_base_test import KDCBaseTest from samba.tests.krb5.rfc4120_constants import ( AD_FX_FAST_ARMOR, @@ -45,7 +42,6 @@ from samba.tests.krb5.rfc4120_constants import ( KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS, KRB_AS_REP, KRB_TGS_REP, - KU_TICKET, NT_PRINCIPAL, NT_SRV_INST, PADATA_FX_COOKIE, @@ -1428,44 +1424,19 @@ class FAST_Tests(KDCBaseTest): def gen_tgt_fast_armor_auth_data(self): user_tgt = self.get_user_tgt() - ticket_decryption_key = user_tgt.decryption_key + auth_data = self.generate_fast_armor_auth_data() + + def modify_fn(enc_part): + enc_part['authorization-data'].append(auth_data) - tgt_encpart = self.getElementValue(user_tgt.ticket, 'enc-part') - self.assertElementEqual(tgt_encpart, 'etype', - ticket_decryption_key.etype) - self.assertElementKVNO(tgt_encpart, 'kvno', - ticket_decryption_key.kvno) - tgt_cipher = self.getElementValue(tgt_encpart, 'cipher') - tgt_decpart = ticket_decryption_key.decrypt(KU_TICKET, tgt_cipher) - tgt_private = self.der_decode(tgt_decpart, - asn1Spec=krb5_asn1.EncTicketPart()) + return enc_part - auth_data = self.generate_fast_armor_auth_data() - tgt_private['authorization-data'].append(auth_data) - - # Re-encrypt the user TGT. - tgt_private_new = self.der_encode( - tgt_private, - asn1Spec=krb5_asn1.EncTicketPart()) - tgt_encpart = self.EncryptedData_create(ticket_decryption_key, - KU_TICKET, - tgt_private_new) - user_ticket = user_tgt.ticket.copy() - user_ticket['enc-part'] = tgt_encpart - - user_tgt = KerberosTicketCreds( - user_ticket, - session_key=user_tgt.session_key, - crealm=user_tgt.crealm, - cname=user_tgt.cname, - srealm=user_tgt.srealm, - sname=user_tgt.sname, - decryption_key=user_tgt.decryption_key, - ticket_private=tgt_private, - encpart_private=user_tgt.encpart_private) + checksum_keys = self.get_krbtgt_checksum_key() # Use our modifed TGT to replace the one in the request. - return user_tgt + return self.modified_ticket(user_tgt, + modify_fn=modify_fn, + checksum_keys=checksum_keys) def create_fast_cookie(self, cookie): self.assertIsNotNone(cookie) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 4ac7698ffab..57013caafb1 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -3290,6 +3290,14 @@ class RawKerberosTest(TestCaseInTempDir): return new_auth_data, old_pac + def get_krbtgt_checksum_key(self): + krbtgt_creds = self.get_krbtgt_creds() + krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) + + return { + krb5pac.PAC_TYPE_KDC_CHECKSUM: krbtgt_key + } + def get_empty_pac(self): return self.AuthorizationData_create(AD_WIN2K_PAC, bytes(1)) -- 2.25.1 From 55b266f2f571ec6455fe4739b5ff7bf5e59fccff Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 21 Sep 2021 17:01:12 +1200 Subject: [PATCH 059/159] tests/krb5: Make get_default_enctypes() return a set of enctype constants This is often more convenient than a bitfield. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 7cedd383bcc1b5652ea65817b464d6e0485c7b8b) --- python/samba/tests/krb5/kdc_base_test.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 59175c7bb2f..3d2d20cb65b 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -222,11 +222,11 @@ class KDCBaseTest(RawKerberosTest): functional_level = self.get_domain_functional_level(samdb) # RC4 should always be supported - default_enctypes = security.KERB_ENCTYPE_RC4_HMAC_MD5 + default_enctypes = {kcrypto.Enctype.RC4} if functional_level >= DS_DOMAIN_FUNCTION_2008: # AES is only supported at functional level 2008 or higher - default_enctypes |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 - default_enctypes |= security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 + default_enctypes.add(kcrypto.Enctype.AES256) + default_enctypes.add(kcrypto.Enctype.AES128) return default_enctypes @@ -513,12 +513,7 @@ class KDCBaseTest(RawKerberosTest): default_enctypes = self.get_default_enctypes() - if default_enctypes & security.KERB_ENCTYPE_RC4_HMAC_MD5: - self.assertIn(kcrypto.Enctype.RC4, keys) - if default_enctypes & security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96: - self.assertIn(kcrypto.Enctype.AES256, keys) - if default_enctypes & security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96: - self.assertIn(kcrypto.Enctype.AES128, keys) + self.assertCountEqual(default_enctypes, keys) return keys -- 2.25.1 From e3fa88c1c19beb1ff93ce76f46117d6b3b971d5d Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 21 Sep 2021 21:01:46 +1200 Subject: [PATCH 060/159] tests/krb5: Add methods to convert between enctypes and bitfields These methods are useful for converting a collection of encryption types into msDS-SupportedEncryptionTypes bit flags, and vice versa. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 432eba9e09849e74f4c0f2d7826d45cbd2b7ce42) --- python/samba/tests/krb5/kdc_base_test.py | 6 +-- python/samba/tests/krb5/raw_testcase.py | 51 +++++++++++++++++++----- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 3d2d20cb65b..10ad9e6961f 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -633,10 +633,8 @@ class KDCBaseTest(RawKerberosTest): enctypes = supported_enctypes if fast_support: - fast_bits = (security.KERB_ENCTYPE_FAST_SUPPORTED | - security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED | - security.KERB_ENCTYPE_CLAIMS_SUPPORTED) - enctypes = (enctypes or 0) | fast_bits + enctypes = enctypes or 0 + enctypes |= KerberosCredentials.fast_supported_bits if enctypes is not None: details['msDS-SupportedEncryptionTypes'] = str(enctypes) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 57013caafb1..57579126f8a 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -304,6 +304,11 @@ class RodcPacEncryptionKey(Krb5EncryptionKey): class KerberosCredentials(Credentials): + + fast_supported_bits = (security.KERB_ENCTYPE_FAST_SUPPORTED | + security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED | + security.KERB_ENCTYPE_CLAIMS_SUPPORTED) + def __init__(self): super(KerberosCredentials, self).__init__() all_enc_types = 0 @@ -331,26 +336,52 @@ class KerberosCredentials(Credentials): def set_ap_supported_enctypes(self, value): self.ap_supported_enctypes = int(value) - def _get_krb5_etypes(self, supported_enctypes): + etype_map = collections.OrderedDict([ + (kcrypto.Enctype.AES256, + security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96), + (kcrypto.Enctype.AES128, + security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96), + (kcrypto.Enctype.RC4, + security.KERB_ENCTYPE_RC4_HMAC_MD5), + (kcrypto.Enctype.DES_MD5, + security.KERB_ENCTYPE_DES_CBC_MD5), + (kcrypto.Enctype.DES_CRC, + security.KERB_ENCTYPE_DES_CBC_CRC) + ]) + + @classmethod + def etypes_to_bits(self, etypes): + bits = 0 + for etype in etypes: + bit = self.etype_map[etype] + if bits & bit: + raise ValueError(f'Got duplicate etype: {etype}') + bits |= bit + + return bits + + @classmethod + def bits_to_etypes(self, bits): etypes = () + for etype, bit in self.etype_map.items(): + if bit & bits: + bits &= ~bit + etypes += (etype,) - if supported_enctypes & security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96: - etypes += (kcrypto.Enctype.AES256,) - if supported_enctypes & security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96: - etypes += (kcrypto.Enctype.AES128,) - if supported_enctypes & security.KERB_ENCTYPE_RC4_HMAC_MD5: - etypes += (kcrypto.Enctype.RC4,) + bits &= ~self.fast_supported_bits + if bits != 0: + raise ValueError(f'Unsupported etype bits: {bits}') return etypes def get_as_krb5_etypes(self): - return self._get_krb5_etypes(self.as_supported_enctypes) + return self.bits_to_etypes(self.as_supported_enctypes) def get_tgs_krb5_etypes(self): - return self._get_krb5_etypes(self.tgs_supported_enctypes) + return self.bits_to_etypes(self.tgs_supported_enctypes) def get_ap_krb5_etypes(self): - return self._get_krb5_etypes(self.ap_supported_enctypes) + return self.bits_to_etypes(self.ap_supported_enctypes) def set_kvno(self, kvno): # Sign-extend from 32 bits. -- 2.25.1 From d4114c295fecd0fe07fca34213f945fd25a4b064 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 21 Sep 2021 17:10:49 +1200 Subject: [PATCH 061/159] tests/krb5: Get supported enctypes for credentials from database Look up the account's msDS-SupportedEncryptionTypes attribute to get the encryption types that it supports. Move the fallback to RC4 to when the ticket decryption key is obtained. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit b6eaf2cf44fb66d8f302d4cab050827a67de3ea4) --- python/samba/tests/krb5/as_req_tests.py | 4 +- python/samba/tests/krb5/kdc_base_test.py | 52 +++++++++++++++++------- python/samba/tests/krb5/raw_testcase.py | 5 ++- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/python/samba/tests/krb5/as_req_tests.py b/python/samba/tests/krb5/as_req_tests.py index 35f88a0c920..8d9b90fee69 100755 --- a/python/samba/tests/krb5/as_req_tests.py +++ b/python/samba/tests/krb5/as_req_tests.py @@ -60,7 +60,7 @@ class AsReqKerberosTests(KDCBaseTest): initial_kdc_options=None): client_creds = self.get_client_creds() client_account = client_creds.get_username() - client_as_etypes = client_creds.get_as_krb5_etypes() + client_as_etypes = self.get_default_enctypes() krbtgt_creds = self.get_krbtgt_creds(require_keys=False) krbtgt_account = krbtgt_creds.get_username() realm = krbtgt_creds.get_realm() @@ -114,7 +114,7 @@ class AsReqKerberosTests(KDCBaseTest): def test_as_req_enc_timestamp(self): client_creds = self.get_client_creds() client_account = client_creds.get_username() - client_as_etypes = client_creds.get_as_krb5_etypes() + client_as_etypes = self.get_default_enctypes() client_kvno = client_creds.get_kvno() krbtgt_creds = self.get_krbtgt_creds(require_strongest_key=True) krbtgt_account = krbtgt_creds.get_username() diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 10ad9e6961f..cdaeaf9f3e1 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -289,6 +289,8 @@ class KDCBaseTest(RawKerberosTest): # Save the account name so it can be deleted in tearDownClass self.accounts.add(dn) + self.creds_set_enctypes(creds) + return (creds, dn) def create_rodc(self, ctx): @@ -522,13 +524,28 @@ class KDCBaseTest(RawKerberosTest): for enctype, key in keys.items(): creds.set_forced_key(enctype, key) - supported_enctypes = 0 - if kcrypto.Enctype.AES256 in keys: - supported_enctypes |= security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 - if kcrypto.Enctype.AES128 in keys: - supported_enctypes |= security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 - if kcrypto.Enctype.RC4 in keys: - supported_enctypes |= security.KERB_ENCTYPE_RC4_HMAC_MD5 + def creds_set_enctypes(self, creds): + samdb = self.get_samdb() + + res = samdb.search(creds.get_dn(), + scope=ldb.SCOPE_BASE, + attrs=['msDS-SupportedEncryptionTypes']) + supported_enctypes = res[0].get('msDS-SupportedEncryptionTypes', idx=0) + + if supported_enctypes is None: + supported_enctypes = 0 + + creds.set_as_supported_enctypes(supported_enctypes) + creds.set_tgs_supported_enctypes(supported_enctypes) + creds.set_ap_supported_enctypes(supported_enctypes) + + def creds_set_default_enctypes(self, creds, fast_support=False): + default_enctypes = self.get_default_enctypes() + supported_enctypes = KerberosCredentials.etypes_to_bits( + default_enctypes) + + if fast_support: + supported_enctypes |= KerberosCredentials.fast_supported_bits creds.set_as_supported_enctypes(supported_enctypes) creds.set_tgs_supported_enctypes(supported_enctypes) @@ -662,14 +679,6 @@ class KDCBaseTest(RawKerberosTest): keys = self.get_keys(samdb, dn) self.creds_set_keys(creds, keys) - if machine_account: - if supported_enctypes is not None: - tgs_enctypes = supported_enctypes - else: - tgs_enctypes = security.KERB_ENCTYPE_RC4_HMAC_MD5 - - creds.set_tgs_supported_enctypes(tgs_enctypes) - # Handle secret replication to the RODC. if allowed_replication or revealed_to_rodc: @@ -814,6 +823,11 @@ class KDCBaseTest(RawKerberosTest): keys = self.get_keys(samdb, krbtgt_dn) self.creds_set_keys(creds, keys) + # The RODC krbtgt account should support the default enctypes, + # although it might not have the msDS-SupportedEncryptionTypes + # attribute. + self.creds_set_default_enctypes(creds) + return creds c = self._get_krb5_creds(prefix='RODC_KRBTGT', @@ -858,6 +872,8 @@ class KDCBaseTest(RawKerberosTest): keys = self.get_keys(samdb, dn) self.creds_set_keys(creds, keys) + self.creds_set_enctypes(creds) + return creds c = self._get_krb5_creds(prefix='MOCK_RODC_KRBTGT', @@ -898,6 +914,12 @@ class KDCBaseTest(RawKerberosTest): keys = self.get_keys(samdb, dn) self.creds_set_keys(creds, keys) + # The krbtgt account should support the default enctypes, although + # it might not (on Samba) have the msDS-SupportedEncryptionTypes + # attribute. + self.creds_set_default_enctypes(creds, + fast_support=self.kdc_fast_support) + return creds c = self._get_krb5_creds(prefix='KRBTGT', diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 57579126f8a..8d7778602f5 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1082,7 +1082,10 @@ class RawKerberosTest(TestCaseInTempDir): if etype is None: etypes = creds.get_tgs_krb5_etypes() - etype = etypes[0] + if etypes: + etype = etypes[0] + else: + etype = kcrypto.Enctype.RC4 forced_key = creds.get_forced_key(etype) if forced_key is not None: -- 2.25.1 From 838f74345f309efc0a68bf417dce28e0ac87f10a Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 21 Sep 2021 17:11:28 +1200 Subject: [PATCH 062/159] tests/krb5: Correctly check PA-SUPPORTED-ENCTYPES BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 14cd933a9d6af08deb680c9f688b166138d45ed9) --- python/samba/tests/krb5/fast_tests.py | 4 ++++ python/samba/tests/krb5/kdc_base_test.py | 3 +++ python/samba/tests/krb5/raw_testcase.py | 24 +++++++++++++++--------- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index d8ccfaee325..431b48f00d6 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1126,6 +1126,7 @@ class FAST_Tests(KDCBaseTest): name_type=NT_SRV_INST, names=[krbtgt_username, krbtgt_realm]) krbtgt_decryption_key = self.TicketDecryptionKey_from_creds( krbtgt_creds) + krbtgt_etypes = krbtgt_creds.tgs_supported_enctypes target_username = target_creds.get_username()[:-1] target_realm = target_creds.get_realm() @@ -1134,6 +1135,7 @@ class FAST_Tests(KDCBaseTest): name_type=NT_SRV_INST, names=[target_service, target_username]) target_decryption_key = self.TicketDecryptionKey_from_creds( target_creds) + target_etypes = target_creds.tgs_supported_enctypes fast_cookie = None preauth_etype_info2 = None @@ -1322,6 +1324,7 @@ class FAST_Tests(KDCBaseTest): expected_anon=expected_anon, expected_srealm=expected_srealm, expected_sname=expected_sname, + expected_supported_etypes=krbtgt_etypes, expected_flags=expected_flags, unexpected_flags=unexpected_flags, ticket_decryption_key=krbtgt_decryption_key, @@ -1355,6 +1358,7 @@ class FAST_Tests(KDCBaseTest): expected_anon=expected_anon, expected_srealm=expected_srealm, expected_sname=expected_sname, + expected_supported_etypes=target_etypes, expected_flags=expected_flags, unexpected_flags=unexpected_flags, ticket_decryption_key=target_decryption_key, diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index cdaeaf9f3e1..646859e85b3 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1267,6 +1267,8 @@ class KDCBaseTest(RawKerberosTest): expected_sname = self.PrincipalName_create( name_type=NT_SRV_INST, names=['krbtgt', realm.upper()]) + expected_etypes = krbtgt_creds.tgs_supported_enctypes + rep, kdc_exchange_dict = self._test_as_exchange( cname=cname, realm=realm, @@ -1279,6 +1281,7 @@ class KDCBaseTest(RawKerberosTest): expected_srealm=expected_realm, expected_sname=expected_sname, expected_salt=salt, + expected_supported_etypes=expected_etypes, etypes=etype, padata=padata, kdc_options=kdc_options, diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 8d7778602f5..c6bc3e553ad 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1879,6 +1879,7 @@ class RawKerberosTest(TestCaseInTempDir): expected_anon=False, expected_srealm=None, expected_sname=None, + expected_supported_etypes=None, expected_flags=None, unexpected_flags=None, ticket_decryption_key=None, @@ -1923,6 +1924,7 @@ class RawKerberosTest(TestCaseInTempDir): 'expected_anon': expected_anon, 'expected_srealm': expected_srealm, 'expected_sname': expected_sname, + 'expected_supported_etypes': expected_supported_etypes, 'expected_flags': expected_flags, 'unexpected_flags': unexpected_flags, 'ticket_decryption_key': ticket_decryption_key, @@ -1963,6 +1965,7 @@ class RawKerberosTest(TestCaseInTempDir): expected_anon=False, expected_srealm=None, expected_sname=None, + expected_supported_etypes=None, expected_flags=None, unexpected_flags=None, ticket_decryption_key=None, @@ -2006,6 +2009,7 @@ class RawKerberosTest(TestCaseInTempDir): 'expected_anon': expected_anon, 'expected_srealm': expected_srealm, 'expected_sname': expected_sname, + 'expected_supported_etypes': expected_supported_etypes, 'expected_flags': expected_flags, 'unexpected_flags': unexpected_flags, 'ticket_decryption_key': ticket_decryption_key, @@ -2312,19 +2316,19 @@ class RawKerberosTest(TestCaseInTempDir): if canonicalize: self.assertIn(PADATA_SUPPORTED_ETYPES, enc_pa_dict) + expected_supported_etypes = kdc_exchange_dict[ + 'expected_supported_etypes'] + expected_supported_etypes |= ( + security.KERB_ENCTYPE_DES_CBC_CRC | + security.KERB_ENCTYPE_DES_CBC_MD5 | + security.KERB_ENCTYPE_RC4_HMAC_MD5) + (supported_etypes,) = struct.unpack( ' Date: Mon, 20 Sep 2021 13:54:39 +1200 Subject: [PATCH 063/159] tests/krb5: Set key version number for all accounts created with create_account() BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 054ec1a8cc4ae42918c7c06ef9c66c8a81242655) --- python/samba/tests/krb5/kdc_base_test.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 646859e85b3..91034a10e15 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -291,6 +291,12 @@ class KDCBaseTest(RawKerberosTest): self.creds_set_enctypes(creds) + res = samdb.search(base=dn, + scope=ldb.SCOPE_BASE, + attrs=['msDS-KeyVersionNumber']) + kvno = int(res[0]['msDS-KeyVersionNumber'][0]) + creds.set_kvno(kvno) + return (creds, dn) def create_rodc(self, ctx): @@ -670,12 +676,6 @@ class KDCBaseTest(RawKerberosTest): additional_details=details, account_control=user_account_control) - res = samdb.search(base=dn, - scope=ldb.SCOPE_BASE, - attrs=['msDS-KeyVersionNumber']) - kvno = int(res[0]['msDS-KeyVersionNumber'][0]) - creds.set_kvno(kvno) - keys = self.get_keys(samdb, dn) self.creds_set_keys(creds, keys) -- 2.25.1 From 5c3a7f2dce8bf83d2ba7acd91e3acef6a7ed89bb Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 20 Sep 2021 13:59:24 +1200 Subject: [PATCH 064/159] tests/krb5: Allow tgs_req() to check the returned ticket enc-part BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 38b4b334caf1b32f1479db3ada48b2028946f5e6) --- python/samba/tests/krb5/kdc_base_test.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 91034a10e15..914112fdfc7 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1084,7 +1084,7 @@ class KDCBaseTest(RawKerberosTest): def tgs_req(self, cname, sname, realm, ticket, key, etypes, expected_error_mode=0, padata=None, kdc_options=0, - to_rodc=False): + to_rodc=False, service_creds=None): '''Send a TGS-REQ, returns the response and the decrypted and decoded enc-part ''' @@ -1098,6 +1098,12 @@ class KDCBaseTest(RawKerberosTest): crealm=realm, cname=cname) + if service_creds is not None: + decryption_key = self.TicketDecryptionKey_from_creds( + service_creds) + else: + decryption_key = None + if not expected_error_mode: check_error_fn = None check_rep_fn = self.generic_check_kdc_rep @@ -1120,6 +1126,7 @@ class KDCBaseTest(RawKerberosTest): check_error_fn=check_error_fn, check_rep_fn=check_rep_fn, check_kdc_private_fn=self.generic_check_kdc_private, + ticket_decryption_key=decryption_key, generate_padata_fn=generate_padata if padata is not None else None, tgt=tgt, authenticator_subkey=subkey, -- 2.25.1 From 42dee785c7fccd0172f131885f742c1d6465a73d Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 20 Sep 2021 13:58:09 +1200 Subject: [PATCH 065/159] tests/krb5: Add method to get DC credentials BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 9d01043042f1caac98a23cf4d9aa9a02a31a9239) --- python/samba/tests/krb5/kdc_base_test.py | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 914112fdfc7..5de9907d02b 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -930,6 +930,48 @@ class KDCBaseTest(RawKerberosTest): fallback_creds_fn=download_krbtgt_creds) return c + def get_dc_creds(self, + require_keys=True, + require_strongest_key=False): + if require_strongest_key: + self.assertTrue(require_keys) + + def download_dc_creds(): + samdb = self.get_samdb() + + dc_rid = 1000 + dc_sid = '%s-%d' % (samdb.get_domain_sid(), dc_rid) + + res = samdb.search(base='' % dc_sid, + scope=ldb.SCOPE_BASE, + attrs=['sAMAccountName', + 'msDS-KeyVersionNumber']) + dn = res[0].dn + username = str(res[0]['sAMAccountName']) + + creds = KerberosCredentials() + creds.set_domain(self.env_get_var('DOMAIN', 'DC')) + creds.set_realm(self.env_get_var('REALM', 'DC')) + creds.set_username(username) + + kvno = int(res[0]['msDS-KeyVersionNumber'][0]) + creds.set_kvno(kvno) + creds.set_dn(dn) + + keys = self.get_keys(samdb, dn) + self.creds_set_keys(creds, keys) + + self.creds_set_enctypes(creds) + + return creds + + c = self._get_krb5_creds(prefix='DC', + allow_missing_password=True, + allow_missing_keys=not require_keys, + require_strongest_key=require_strongest_key, + fallback_creds_fn=download_dc_creds) + return c + def as_req(self, cname, sname, realm, etypes, padata=None, kdc_options=0): '''Send a Kerberos AS_REQ, returns the undecoded response ''' -- 2.25.1 From 9ec0873fbc49c874f9b9075e0d0d2f8b34a9b640 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 20 Sep 2021 14:08:16 +1200 Subject: [PATCH 066/159] tests/krb5: Fix checking for presence of authorization data BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit f9284d8517edd9ffd96f0c24166a16366f97de8f) --- python/samba/tests/krb5/kdc_base_test.py | 3 ++- .../ms_kile_client_principal_lookup_tests.py | 6 ++++-- python/samba/tests/krb5/raw_testcase.py | 16 +++++++++++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 5de9907d02b..b4d3739aa11 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1126,7 +1126,7 @@ class KDCBaseTest(RawKerberosTest): def tgs_req(self, cname, sname, realm, ticket, key, etypes, expected_error_mode=0, padata=None, kdc_options=0, - to_rodc=False, service_creds=None): + to_rodc=False, service_creds=None, expect_pac=True): '''Send a TGS-REQ, returns the response and the decrypted and decoded enc-part ''' @@ -1173,6 +1173,7 @@ class KDCBaseTest(RawKerberosTest): tgt=tgt, authenticator_subkey=subkey, kdc_options=str(kdc_options), + expect_pac=expect_pac, to_rodc=to_rodc) rep = self._generic_kdc_exchange(kdc_exchange_dict, diff --git a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py b/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py index 99c842701ea..64ebe15ad70 100755 --- a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py +++ b/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py @@ -321,7 +321,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): names=[mc.get_username()]) (rep, enc_part) = self.tgs_req( - cname, sname, uc.get_realm(), ticket, key, etype) + cname, sname, uc.get_realm(), ticket, key, etype, + expect_pac=False) self.check_tgs_reply(rep) # Check the contents of the service ticket @@ -695,7 +696,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): names=[mc.get_username()]) (rep, enc_part) = self.tgs_req( - cname, sname, uc.get_realm(), ticket, key, etype) + cname, sname, uc.get_realm(), ticket, key, etype, + expect_pac=False) self.check_tgs_reply(rep) # Check the contents of the service ticket diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index c6bc3e553ad..b531e33041d 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -944,12 +944,15 @@ class RawKerberosTest(TestCaseInTempDir): v = self.getElementValue(obj, elem) self.assertIsNone(v) - def assertElementPresent(self, obj, elem): + def assertElementPresent(self, obj, elem, expect_empty=False): v = self.getElementValue(obj, elem) self.assertIsNotNone(v) if self.strict_checking: if isinstance(v, collections.abc.Container): - self.assertNotEqual(0, len(v)) + if expect_empty: + self.assertEqual(0, len(v)) + else: + self.assertNotEqual(0, len(v)) def assertElementEqual(self, obj, elem, value): v = self.getElementValue(obj, elem) @@ -1907,6 +1910,7 @@ class RawKerberosTest(TestCaseInTempDir): outer_req=None, pac_request=None, pac_options=None, + expect_pac=True, to_rodc=False): if expected_error_mode == 0: expected_error_mode = () @@ -1952,6 +1956,7 @@ class RawKerberosTest(TestCaseInTempDir): 'outer_req': outer_req, 'pac_request': pac_request, 'pac_options': pac_options, + 'expect_pac': expect_pac, 'to_rodc': to_rodc } if callback_dict is None: @@ -1992,6 +1997,7 @@ class RawKerberosTest(TestCaseInTempDir): outer_req=None, pac_request=None, pac_options=None, + expect_pac=True, to_rodc=False): if expected_error_mode == 0: expected_error_mode = () @@ -2036,6 +2042,7 @@ class RawKerberosTest(TestCaseInTempDir): 'outer_req': outer_req, 'pac_request': pac_request, 'pac_options': pac_options, + 'expect_pac': expect_pac, 'to_rodc': to_rodc } if callback_dict is None: @@ -2236,6 +2243,8 @@ class RawKerberosTest(TestCaseInTempDir): armor_key = kdc_exchange_dict['armor_key'] self.verify_ticket_checksum(ticket, ticket_checksum, armor_key) + expect_pac = kdc_exchange_dict['expect_pac'] + ticket_session_key = None if ticket_private is not None: self.assertElementFlags(ticket_private, 'flags', @@ -2265,7 +2274,8 @@ class RawKerberosTest(TestCaseInTempDir): self.assertElementMissing(ticket_private, 'renew-till') if self.strict_checking: self.assertElementEqual(ticket_private, 'caddr', []) - self.assertElementPresent(ticket_private, 'authorization-data') + self.assertElementPresent(ticket_private, 'authorization-data', + expect_empty=not expect_pac) encpart_session_key = None if encpart_private is not None: -- 2.25.1 From fcbbc7747f52d6f8a583b03b68ed77b240aaee40 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 22 Sep 2021 11:41:45 +1200 Subject: [PATCH 067/159] tests/krb5: Provide ticket enc-part key to tgs_req() BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit f2f1f3a1e9269f0e7b93006bba2368a6ffbecc7c) --- python/samba/tests/krb5/kdc_base_test.py | 3 +- python/samba/tests/krb5/kdc_tgs_tests.py | 6 ++-- .../ms_kile_client_principal_lookup_tests.py | 28 ++++++++++++------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index b4d3739aa11..b71ae66bf54 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1215,7 +1215,8 @@ class KDCBaseTest(RawKerberosTest): names=[service, target_name]) rep, enc_part = self.tgs_req(cname, sname, realm, ticket, key, etype, - to_rodc=to_rodc) + to_rodc=to_rodc, + service_creds=target_creds) service_ticket = rep['ticket'] diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py index dad9e6b88df..0904233b01f 100755 --- a/python/samba/tests/krb5/kdc_tgs_tests.py +++ b/python/samba/tests/krb5/kdc_tgs_tests.py @@ -132,7 +132,8 @@ class KdcTgsTests(KDCBaseTest): names=["ldap", samdb.host_dns_name()]) (rep, _) = self.tgs_req( - cname, sname, uc.get_realm(), ticket, key, etype) + cname, sname, uc.get_realm(), ticket, key, etype, + service_creds=self.get_dc_creds()) self.check_tgs_reply(rep) @@ -175,7 +176,8 @@ class KdcTgsTests(KDCBaseTest): names=[mc.get_username()]) (rep, enc_part) = self.tgs_req( - cname, sname, uc.get_realm(), ticket, key, etype) + cname, sname, uc.get_realm(), ticket, key, etype, + service_creds=mc) self.check_tgs_reply(rep) # Check the contents of the service ticket diff --git a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py b/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py index 64ebe15ad70..ce796f63ac2 100755 --- a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py +++ b/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py @@ -126,7 +126,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): names=[mc.get_username()]) (rep, enc_part) = self.tgs_req( - cname, sname, uc.get_realm(), ticket, key, etype) + cname, sname, uc.get_realm(), ticket, key, etype, + service_creds=mc) self.check_tgs_reply(rep) # Check the contents of the pac, and the ticket @@ -185,7 +186,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): names=[mc.get_username()]) (rep, enc_part) = self.tgs_req( - cname, sname, mc.get_realm(), ticket, key, etype) + cname, sname, mc.get_realm(), ticket, key, etype, + service_creds=mc) self.check_tgs_reply(rep) # Check the contents of the pac, and the ticket @@ -247,7 +249,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): names=[mc.get_username()]) (rep, enc_part) = self.tgs_req( - cname, sname, uc.get_realm(), ticket, key, etype) + cname, sname, uc.get_realm(), ticket, key, etype, + service_creds=mc) self.check_tgs_reply(rep) # Check the contents of the service ticket @@ -322,7 +325,7 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): (rep, enc_part) = self.tgs_req( cname, sname, uc.get_realm(), ticket, key, etype, - expect_pac=False) + service_creds=mc, expect_pac=False) self.check_tgs_reply(rep) # Check the contents of the service ticket @@ -390,7 +393,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): names=[mc.get_username()]) (rep, enc_part) = self.tgs_req( - cname, sname, uc.get_realm(), ticket, key, etype) + cname, sname, uc.get_realm(), ticket, key, etype, + service_creds=mc) self.check_tgs_reply(rep) # Check the contents of the pac, and the ticket @@ -492,7 +496,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): names=[mc.get_username()]) (rep, enc_part) = self.tgs_req( - cname, sname, uc.get_realm(), ticket, key, etype) + cname, sname, uc.get_realm(), ticket, key, etype, + service_creds=mc) self.check_tgs_reply(rep) # Check the contents of the pac, and the ticket @@ -555,7 +560,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): names=[mc.get_username()]) (rep, enc_part) = self.tgs_req( - cname, sname, uc.get_realm(), ticket, key, etype) + cname, sname, uc.get_realm(), ticket, key, etype, + service_creds=mc) self.check_tgs_reply(rep) # Check the contents of the pac, and the ticket @@ -619,7 +625,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): names=[mc.get_username()]) (rep, enc_part) = self.tgs_req( - cname, sname, uc.get_realm(), ticket, key, etype) + cname, sname, uc.get_realm(), ticket, key, etype, + service_creds=mc) self.check_tgs_reply(rep) # Check the contents of the pac, and the ticket @@ -697,7 +704,7 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): (rep, enc_part) = self.tgs_req( cname, sname, uc.get_realm(), ticket, key, etype, - expect_pac=False) + service_creds=mc, expect_pac=False) self.check_tgs_reply(rep) # Check the contents of the service ticket @@ -767,7 +774,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): names=[mc.get_username()]) (rep, enc_part) = self.tgs_req( - cname, sname, uc.get_realm(), ticket, key, etype) + cname, sname, uc.get_realm(), ticket, key, etype, + service_creds=mc) self.check_tgs_reply(rep) # Check the contents of the pac, and the ticket -- 2.25.1 From 101b874203099dbb8b4b59968b1495073e4691dc Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 20 Sep 2021 14:05:58 +1200 Subject: [PATCH 068/159] tests/krb5: Simplify account creation BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 394e8db261b10d130c5e5730989bf68f9bf4f85f) --- .../ms_kile_client_principal_lookup_tests.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py b/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py index ce796f63ac2..501bc4892f4 100755 --- a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py +++ b/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py @@ -282,15 +282,11 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): samdb = self.get_samdb() user_name = "mskileusr" alt_name = "mskilealtsec" - (uc, dn) = self.create_account(samdb, user_name) + (uc, dn) = self.create_account(samdb, user_name, + account_control=UF_DONT_REQUIRE_PREAUTH) realm = uc.get_realm().lower() alt_sec = "Kerberos:%s@%s" % (alt_name, realm) self.add_attribute(samdb, dn, "altSecurityIdentities", alt_sec) - self.modify_attribute( - samdb, - dn, - "userAccountControl", - str(UF_NORMAL_ACCOUNT | UF_DONT_REQUIRE_PREAUTH)) mach_name = "mskilemac" (mc, _) = self.create_account(samdb, mach_name, machine_account=True) @@ -660,15 +656,11 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): samdb = self.get_samdb() user_name = "mskileusr" alt_name = "mskilealtsec" - (uc, dn) = self.create_account(samdb, user_name) + (uc, dn) = self.create_account(samdb, user_name, + account_control=UF_DONT_REQUIRE_PREAUTH) realm = uc.get_realm().lower() alt_sec = "Kerberos:%s@%s" % (alt_name, realm) self.add_attribute(samdb, dn, "altSecurityIdentities", alt_sec) - self.modify_attribute( - samdb, - dn, - "userAccountControl", - str(UF_NORMAL_ACCOUNT | UF_DONT_REQUIRE_PREAUTH)) ename = alt_name + "@" + realm mach_name = "mskilemac" -- 2.25.1 From e040cad6827843cdf06e6a6c7c0ab5baa3fadc36 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 21 Sep 2021 13:54:47 +1200 Subject: [PATCH 069/159] tests/krb5: Add get_rodc_krbtgt_creds() to RawKerberosTest BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 1458cd9065de34c42bd5ec63feb2f66c25103982) --- python/samba/tests/krb5/raw_testcase.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index b531e33041d..59882e44173 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -750,6 +750,17 @@ class RawKerberosTest(TestCaseInTempDir): c.set_gensec_features(c.get_gensec_features() | FEATURE_SEAL) return c + def get_rodc_krbtgt_creds(self, + require_keys=True, + require_strongest_key=False): + if require_strongest_key: + self.assertTrue(require_keys) + c = self._get_krb5_creds(prefix='RODC_KRBTGT', + allow_missing_password=True, + allow_missing_keys=not require_keys, + require_strongest_key=require_strongest_key) + return c + def get_krbtgt_creds(self, require_keys=True, require_strongest_key=False): -- 2.25.1 From 080c3b0d474157f2263357bd29b73566f1dc2181 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 20 Sep 2021 14:10:07 +1200 Subject: [PATCH 070/159] tests/krb5: Verify checksums of tickets obtained from the KDC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit ea7b550a500d9e458498d37688b67dafd3d9509d) --- python/samba/tests/krb5/raw_testcase.py | 34 +++++++++++++++++-------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 59882e44173..985792887ca 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2254,6 +2254,13 @@ class RawKerberosTest(TestCaseInTempDir): armor_key = kdc_exchange_dict['armor_key'] self.verify_ticket_checksum(ticket, ticket_checksum, armor_key) + to_rodc = kdc_exchange_dict['to_rodc'] + if to_rodc: + krbtgt_creds = self.get_rodc_krbtgt_creds() + else: + krbtgt_creds = self.get_krbtgt_creds() + krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) + expect_pac = kdc_exchange_dict['expect_pac'] ticket_session_key = None @@ -2386,6 +2393,9 @@ class RawKerberosTest(TestCaseInTempDir): ticket_private=ticket_private, encpart_private=encpart_private) + if ticket_decryption_key is not None: + self.verify_ticket(ticket_creds, krbtgt_key, expect_pac=expect_pac) + kdc_exchange_dict['rep_ticket_creds'] = ticket_creds def check_pac_options_claims_support(self, pac_options): @@ -3061,16 +3071,20 @@ class RawKerberosTest(TestCaseInTempDir): if is_tgt: self.assertNotIn(krb5pac.PAC_TYPE_TICKET_CHECKSUM, checksums) else: - ticket_checksum, ticket_ctype = checksums[ - krb5pac.PAC_TYPE_TICKET_CHECKSUM] - enc_part['authorization-data'] = auth_data - enc_part = self.der_encode(enc_part, - asn1Spec=krb5_asn1.EncTicketPart()) - - krbtgt_key.verify_checksum(KU_NON_KERB_CKSUM_SALT, - enc_part, - ticket_ctype, - ticket_checksum) + ticket_checksum, ticket_ctype = checksums.get( + krb5pac.PAC_TYPE_TICKET_CHECKSUM, + (None, None)) + if self.strict_checking: + self.assertIsNotNone(ticket_checksum) + if ticket_checksum is not None: + enc_part['authorization-data'] = auth_data + enc_part = self.der_encode(enc_part, + asn1Spec=krb5_asn1.EncTicketPart()) + + krbtgt_key.verify_checksum(KU_NON_KERB_CKSUM_SALT, + enc_part, + ticket_ctype, + ticket_checksum) def modified_ticket(self, ticket, *, -- 2.25.1 From 0c89db611f0d541201477a3a41f6a63df01825dc Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 20 Sep 2021 15:06:18 +1200 Subject: [PATCH 071/159] tests/krb5: Add method to determine if principal is krbtgt BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit c0b81f0dd54d0d71b5d0f5a870b505e82d0e85b8) --- python/samba/tests/krb5/raw_testcase.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 985792887ca..cadf2b50dc9 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2977,7 +2977,7 @@ class RawKerberosTest(TestCaseInTempDir): def verify_ticket(self, ticket, krbtgt_key, expect_pac=True): # Check if the ticket is a TGT. sname = ticket.ticket['sname'] - is_tgt = sname['name-string'][0] == b'krbtgt' + is_tgt = self.is_tgs(sname) # Decrypt the ticket. @@ -3371,6 +3371,10 @@ class RawKerberosTest(TestCaseInTempDir): krb5pac.PAC_TYPE_KDC_CHECKSUM: krbtgt_key } + def is_tgs(self, principal): + name = principal['name-string'][0] + return name in ('krbtgt', b'krbtgt') + def get_empty_pac(self): return self.AuthorizationData_create(AD_WIN2K_PAC, bytes(1)) -- 2.25.1 From 681687d95f2bfaec412d9eeeeb64e24363ec1a60 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 20 Sep 2021 15:10:35 +1200 Subject: [PATCH 072/159] tests/krb5: Add classes for testing invalid checksums BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Thu Sep 23 19:28:44 UTC 2021 on sn-devel-184 (cherry picked from commit 5b331443d0698256ee7fcc040a1ab8137efe925d) --- python/samba/tests/krb5/raw_testcase.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index cadf2b50dc9..579d52b3e92 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -303,6 +303,29 @@ class RodcPacEncryptionKey(Krb5EncryptionKey): cksum) +class ZeroedChecksumKey(Krb5EncryptionKey): + def make_checksum(self, usage, plaintext, ctype=None): + return self.make_zeroed_checksum(ctype) + + +class WrongLengthChecksumKey(Krb5EncryptionKey): + def __init__(self, key, kvno, length): + super().__init__(key, kvno) + + self._length = length + + def make_checksum(self, usage, plaintext, ctype=None): + checksum = super().make_checksum(usage, plaintext, ctype) + + diff = self._length - len(checksum) + if diff > 0: + checksum += bytes(diff) + elif diff < 0: + checksum = checksum[:self._length] + + return checksum + + class KerberosCredentials(Credentials): fast_supported_bits = (security.KERB_ENCTYPE_FAST_SUPPORTED | -- 2.25.1 From b754e69e548e8a61e6bbd42e1ec512debb37b0e1 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 11:16:24 +1300 Subject: [PATCH 073/159] tests/krb5: Rename method parameter For class methods, the name given to the first parameter is generally 'cls' rather than 'self'. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit d501ddca3b7b9c39c0b3eccf19176e3122cf5b9d) --- python/samba/tests/krb5/raw_testcase.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 579d52b3e92..11c0e308e3d 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -373,10 +373,10 @@ class KerberosCredentials(Credentials): ]) @classmethod - def etypes_to_bits(self, etypes): + def etypes_to_bits(cls, etypes): bits = 0 for etype in etypes: - bit = self.etype_map[etype] + bit = cls.etype_map[etype] if bits & bit: raise ValueError(f'Got duplicate etype: {etype}') bits |= bit @@ -384,14 +384,14 @@ class KerberosCredentials(Credentials): return bits @classmethod - def bits_to_etypes(self, bits): + def bits_to_etypes(cls, bits): etypes = () - for etype, bit in self.etype_map.items(): + for etype, bit in cls.etype_map.items(): if bit & bits: bits &= ~bit etypes += (etype,) - bits &= ~self.fast_supported_bits + bits &= ~cls.fast_supported_bits if bits != 0: raise ValueError(f'Unsupported etype bits: {bits}') -- 2.25.1 From 572ef6db32daba4956f8671193c73b3a60c547f5 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 11:16:51 +1300 Subject: [PATCH 074/159] tests/krb5: Remove unused parameter BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 8e4b21590836dab02c1864f6ac12b3879c4bd69c) --- python/samba/tests/krb5/raw_testcase.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 11c0e308e3d..1119c9926d7 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2169,7 +2169,6 @@ class RawKerberosTest(TestCaseInTempDir): self.check_rep_padata(kdc_exchange_dict, callback_dict, - rep, fast_response['padata'], error_code=0) @@ -2509,7 +2508,6 @@ class RawKerberosTest(TestCaseInTempDir): etype_info2 = self.check_rep_padata(kdc_exchange_dict, callback_dict, - rep, rep_padata, error_code) @@ -2520,7 +2518,6 @@ class RawKerberosTest(TestCaseInTempDir): def check_rep_padata(self, kdc_exchange_dict, callback_dict, - rep, rep_padata, error_code): rep_msg_type = kdc_exchange_dict['rep_msg_type'] -- 2.25.1 From 5458bcb9bb60c46ec7f6cff31aa8a95863bb5618 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 11:23:17 +1300 Subject: [PATCH 075/159] tests/krb5: Allow for missing msDS-KeyVersionNumber attribute BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit ce433ff868d3cdf8e8a6e4995d89d6e036335fb6) --- python/samba/tests/krb5/kdc_base_test.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index b71ae66bf54..bb92bbd65e5 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -294,8 +294,10 @@ class KDCBaseTest(RawKerberosTest): res = samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['msDS-KeyVersionNumber']) - kvno = int(res[0]['msDS-KeyVersionNumber'][0]) - creds.set_kvno(kvno) + kvno = res[0].get('msDS-KeyVersionNumber', idx=0) + if kvno is not None: + self.assertEqual(int(kvno), 1) + creds.set_kvno(1) return (creds, dn) -- 2.25.1 From d1b1da69b8f1daba4681e17566fe1dfeb7d70921 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 30 Sep 2021 10:51:01 +1300 Subject: [PATCH 076/159] tests/krb5: Fix sending PA-PAC-OPTIONS and PA-PAC-REQUEST These padata were not being sent if other FAST padata was not specified. Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 6f1282e8d34073d8499ce919908b39645b017cb8) --- python/samba/tests/krb5/raw_testcase.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 1119c9926d7..ceff1b6220e 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1801,8 +1801,6 @@ class RawKerberosTest(TestCaseInTempDir): fast_padata, req_body = generate_fast_padata_fn(kdc_exchange_dict, callback_dict, req_body) - - fast_padata += additional_padata else: fast_padata = [] @@ -1847,6 +1845,7 @@ class RawKerberosTest(TestCaseInTempDir): KU_FAST_REQ_CHKSUM, checksum_blob) + fast_padata += additional_padata fast = generate_fast_fn(kdc_exchange_dict, callback_dict, inner_req_body, -- 2.25.1 From 0ac26e181af9bf23825551d3dd96fd26b8e13635 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 30 Sep 2021 10:54:33 +1300 Subject: [PATCH 077/159] tests/krb5: Fix PA-PAC-OPTIONS checking Make the check work correctly if bits other than the claims bit are specified. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 1fd00135fa4dff4331d86b228ccc01f834476997) --- python/samba/tests/krb5/raw_testcase.py | 44 +++++++++++++------------ 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index ceff1b6220e..0217674ed2d 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2354,10 +2354,10 @@ class RawKerberosTest(TestCaseInTempDir): if self.strict_checking: self.assertElementEqual(encpart_private, 'caddr', []) - sent_claims = self.sent_claims(kdc_exchange_dict) + sent_pac_options = self.get_sent_pac_options(kdc_exchange_dict) if self.strict_checking: - if sent_claims or canonicalize: + if canonicalize or '1' in sent_pac_options: self.assertElementPresent(encpart_private, 'encrypted-pa-data') enc_pa_dict = self.get_pa_dict( @@ -2381,12 +2381,15 @@ class RawKerberosTest(TestCaseInTempDir): else: self.assertNotIn(PADATA_SUPPORTED_ETYPES, enc_pa_dict) - # ClaimsCompIdFASTSupported registry key - if sent_claims: + if '1' in sent_pac_options: self.assertIn(PADATA_PAC_OPTIONS, enc_pa_dict) - self.check_pac_options_claims_support( - enc_pa_dict[PADATA_PAC_OPTIONS]) + pac_options = self.der_decode( + enc_pa_dict[PADATA_PAC_OPTIONS], + asn1Spec=krb5_asn1.PA_PAC_OPTIONS()) + + self.assertElementEqual(pac_options, 'options', + sent_pac_options) else: self.assertNotIn(PADATA_PAC_OPTIONS, enc_pa_dict) else: @@ -2419,11 +2422,6 @@ class RawKerberosTest(TestCaseInTempDir): kdc_exchange_dict['rep_ticket_creds'] = ticket_creds - def check_pac_options_claims_support(self, pac_options): - pac_options = self.der_decode(pac_options, - asn1Spec=krb5_asn1.PA_PAC_OPTIONS()) - self.assertEqual('1', pac_options['options'][0]) # claims bit - def generic_check_kdc_error(self, kdc_exchange_dict, callback_dict, @@ -2565,8 +2563,9 @@ class RawKerberosTest(TestCaseInTempDir): if not sent_fast and error_code != 0: expected_patypes += (PADATA_PW_SALT,) else: - sent_claims = self.sent_claims(kdc_exchange_dict) - if sent_claims and error_code not in (0, KDC_ERR_GENERIC): + sent_pac_options = self.get_sent_pac_options(kdc_exchange_dict) + if ('1' in sent_pac_options + and error_code not in (0, KDC_ERR_GENERIC)): expected_patypes += (PADATA_PAC_OPTIONS,) elif error_code != KDC_ERR_GENERIC: if expect_etype_info: @@ -2656,8 +2655,9 @@ class RawKerberosTest(TestCaseInTempDir): continue if patype == PADATA_PAC_OPTIONS: self.assertIsNone(pac_options) - pac_options = pavalue - self.assertIsNotNone(pac_options) + pac_options = self.der_decode( + pavalue, + asn1Spec=krb5_asn1.PA_PAC_OPTIONS()) continue if patype == PADATA_PW_SALT: self.assertIsNone(pw_salt) @@ -2677,7 +2677,7 @@ class RawKerberosTest(TestCaseInTempDir): inner=True) if pac_options is not None: - self.check_pac_options_claims_support(pac_options) + self.assertElementEqual(pac_options, 'options', sent_pac_options) if pw_salt is not None: self.assertEqual(12, len(pw_salt)) @@ -3418,19 +3418,21 @@ class RawKerberosTest(TestCaseInTempDir): return PADATA_ENCRYPTED_CHALLENGE in fast_pa_dict - def sent_claims(self, kdc_exchange_dict): + def get_sent_pac_options(self, kdc_exchange_dict): fast_pa_dict = self.get_fast_pa_dict(kdc_exchange_dict) if PADATA_PAC_OPTIONS not in fast_pa_dict: - return False + return '' pac_options = self.der_decode(fast_pa_dict[PADATA_PAC_OPTIONS], asn1Spec=krb5_asn1.PA_PAC_OPTIONS()) pac_options = pac_options['options'] - claims_pos = len(tuple(krb5_asn1.PACOptionFlags('claims'))) - 1 - return (claims_pos < len(pac_options) - and pac_options[claims_pos] == '1') + # Mask out unsupported bits. + pac_options, remaining = pac_options[:4], pac_options[4:] + pac_options += '0' * len(remaining) + + return pac_options def get_krbtgt_sname(self): krbtgt_creds = self.get_krbtgt_creds() -- 2.25.1 From 6f3ef190e4eeab04ec9aafc3ef9fb189b85624fb Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 11:47:39 +1300 Subject: [PATCH 078/159] tests/krb5: Rename allowed_to_delegate_to parameter for clarity This helps to distinguish resourced-based and non-resource-based constrained delegation. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 31817c383c2014224b1397fde610624663313246) --- python/samba/tests/krb5/kdc_base_test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index bb92bbd65e5..e6639270f69 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -598,7 +598,7 @@ class KDCBaseTest(RawKerberosTest): 'no_auth_data_required': False, 'supported_enctypes': None, 'not_delegated': False, - 'allowed_to_delegate_to': None, + 'delegation_to_spn': None, 'trusted_to_auth_for_delegation': False, 'fast_support': False } @@ -629,13 +629,13 @@ class KDCBaseTest(RawKerberosTest): no_auth_data_required, supported_enctypes, not_delegated, - allowed_to_delegate_to, + delegation_to_spn, trusted_to_auth_for_delegation, fast_support): if machine_account: self.assertFalse(not_delegated) else: - self.assertIsNone(allowed_to_delegate_to) + self.assertIsNone(delegation_to_spn) self.assertFalse(trusted_to_auth_for_delegation) samdb = self.get_samdb() @@ -664,8 +664,8 @@ class KDCBaseTest(RawKerberosTest): if enctypes is not None: details['msDS-SupportedEncryptionTypes'] = str(enctypes) - if allowed_to_delegate_to: - details['msDS-AllowedToDelegateTo'] = allowed_to_delegate_to + if delegation_to_spn: + details['msDS-AllowedToDelegateTo'] = delegation_to_spn if machine_account: spn = 'host/' + user_name -- 2.25.1 From 20a5c954a0b15c060f9a91ba7b42f7118500244d Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 11:50:36 +1300 Subject: [PATCH 079/159] tests/krb5: Allow created accounts to use resource-based constrained delegation BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit bba8cb8dce19e47a7b813efd9a7527e38856435e) --- python/samba/tests/krb5/kdc_base_test.py | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index e6639270f69..918e04a1dbe 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -301,6 +301,30 @@ class KDCBaseTest(RawKerberosTest): return (creds, dn) + def get_security_descriptor(self, dn): + samdb = self.get_samdb() + + sid = self.get_objectSid(samdb, dn) + + owner_sid = security.dom_sid(security.SID_BUILTIN_ADMINISTRATORS) + + ace = security.ace() + ace.access_mask = security.SEC_ADS_GENERIC_ALL + + ace.trustee = security.dom_sid(sid) + + dacl = security.acl() + dacl.revision = security.SECURITY_ACL_REVISION_ADS + dacl.aces = [ace] + dacl.num_aces = 1 + + security_desc = security.descriptor() + security_desc.type |= security.SEC_DESC_DACL_PRESENT + security_desc.owner_sid = owner_sid + security_desc.dacl = dacl + + return ndr_pack(security_desc) + def create_rodc(self, ctx): ctx.nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn] ctx.full_nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn] @@ -599,6 +623,7 @@ class KDCBaseTest(RawKerberosTest): 'supported_enctypes': None, 'not_delegated': False, 'delegation_to_spn': None, + 'delegation_from_dn': None, 'trusted_to_auth_for_delegation': False, 'fast_support': False } @@ -630,12 +655,14 @@ class KDCBaseTest(RawKerberosTest): supported_enctypes, not_delegated, delegation_to_spn, + delegation_from_dn, trusted_to_auth_for_delegation, fast_support): if machine_account: self.assertFalse(not_delegated) else: self.assertIsNone(delegation_to_spn) + self.assertIsNone(delegation_from_dn) self.assertFalse(trusted_to_auth_for_delegation) samdb = self.get_samdb() @@ -667,6 +694,12 @@ class KDCBaseTest(RawKerberosTest): if delegation_to_spn: details['msDS-AllowedToDelegateTo'] = delegation_to_spn + if delegation_from_dn: + security_descriptor = self.get_security_descriptor( + delegation_from_dn) + details['msDS-AllowedToActOnBehalfOfOtherIdentity'] = ( + security_descriptor) + if machine_account: spn = 'host/' + user_name else: -- 2.25.1 From 0d0c2b1a5efe802dd70ef8d64270a41b603f4a35 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 11:52:17 +1300 Subject: [PATCH 080/159] tests/krb5: Add assertion to make failures clearer These failures may occur if tests are not run against an RODC. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit cda50b5c505072989abf84c209e16ff4efe2e628) --- python/samba/tests/krb5/kdc_base_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 918e04a1dbe..25433ba1069 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -590,6 +590,7 @@ class KDCBaseTest(RawKerberosTest): scope=ldb.SCOPE_BASE, attrs=[group_attr]) orig_msg = res[0] + self.assertIn(group_attr, orig_msg) members = list(orig_msg[group_attr]) members.append(account_dn) -- 2.25.1 From 36bc124d181ee9c757dfb1d2ef6dd94431d47e51 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 11:54:49 +1300 Subject: [PATCH 081/159] tests/krb5: Introduce helper method for creating invalid length checksums BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 9d142dc3a452b0f06efc66f422402ee6e553ee7c) --- python/samba/tests/krb5/raw_testcase.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 0217674ed2d..6107442409f 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -314,17 +314,20 @@ class WrongLengthChecksumKey(Krb5EncryptionKey): self._length = length - def make_checksum(self, usage, plaintext, ctype=None): - checksum = super().make_checksum(usage, plaintext, ctype) - - diff = self._length - len(checksum) + @classmethod + def _adjust_to_length(cls, checksum, length): + diff = length - len(checksum) if diff > 0: checksum += bytes(diff) elif diff < 0: - checksum = checksum[:self._length] + checksum = checksum[:length] return checksum + def make_checksum(self, usage, plaintext, ctype=None): + checksum = super().make_checksum(usage, plaintext, ctype) + return self._adjust_to_length(checksum, self._length) + class KerberosCredentials(Credentials): -- 2.25.1 From 523b52e7d931c3df7397cce92ab2438caa3fdb22 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 11:56:21 +1300 Subject: [PATCH 082/159] tests/krb5: Fix method for creating invalid length zeroed checksum Previously the base class method was being used. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit ae09219c3a1c6d47817f51baf3784e8986c7478d) --- python/samba/tests/krb5/raw_testcase.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 6107442409f..a3f17e4dc4a 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -324,6 +324,9 @@ class WrongLengthChecksumKey(Krb5EncryptionKey): return checksum + def make_zeroed_checksum(self, ctype=None): + return bytes(self._length) + def make_checksum(self, usage, plaintext, ctype=None): checksum = super().make_checksum(usage, plaintext, ctype) return self._adjust_to_length(checksum, self._length) -- 2.25.1 From 3ac077e988b65c31aa9d5637cb0668dcdb8a5bd6 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 11:59:42 +1300 Subject: [PATCH 083/159] tests/krb5: Fix checksum generation and verification The KDC and server checksums may be generated using the same key, but only the KDC checksum should have an RODCIdentifier. To fix this, instead of overriding the existing methods, add additional ones for RODC-specific signatures, so that both types of signatures can be generated or verified. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit a927cecafdd5ad6dc5189fa98cb42684c9c3b033) --- python/samba/tests/krb5/raw_testcase.py | 57 ++++++++++++++----------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index a3f17e4dc4a..aefbdd6d761 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -281,15 +281,15 @@ class RodcPacEncryptionKey(Krb5EncryptionKey): else: self.rodc_id = b'' - def make_zeroed_checksum(self, ctype=None): + def make_rodc_zeroed_checksum(self, ctype=None): checksum = super().make_zeroed_checksum(ctype) return checksum + bytes(len(self.rodc_id)) - def make_checksum(self, usage, plaintext, ctype=None): + def make_rodc_checksum(self, usage, plaintext, ctype=None): checksum = super().make_checksum(usage, plaintext, ctype) return checksum + self.rodc_id - def verify_checksum(self, usage, plaintext, ctype, cksum): + def verify_rodc_checksum(self, usage, plaintext, ctype, cksum): if self.rodc_id: cksum, cksum_rodc_id = cksum[:-2], cksum[-2:] @@ -303,12 +303,15 @@ class RodcPacEncryptionKey(Krb5EncryptionKey): cksum) -class ZeroedChecksumKey(Krb5EncryptionKey): +class ZeroedChecksumKey(RodcPacEncryptionKey): def make_checksum(self, usage, plaintext, ctype=None): return self.make_zeroed_checksum(ctype) + def make_rodc_checksum(self, usage, plaintext, ctype=None): + return self.make_rodc_zeroed_checksum(ctype) -class WrongLengthChecksumKey(Krb5EncryptionKey): + +class WrongLengthChecksumKey(RodcPacEncryptionKey): def __init__(self, key, kvno, length): super().__init__(key, kvno) @@ -331,6 +334,13 @@ class WrongLengthChecksumKey(Krb5EncryptionKey): checksum = super().make_checksum(usage, plaintext, ctype) return self._adjust_to_length(checksum, self._length) + def make_rodc_zeroed_checksum(self, ctype=None): + return bytes(self._length) + + def make_rodc_checksum(self, usage, plaintext, ctype=None): + checksum = super().make_rodc_checksum(usage, plaintext, ctype) + return self._adjust_to_length(checksum, self._length) + class KerberosCredentials(Credentials): @@ -3080,18 +3090,17 @@ class RawKerberosTest(TestCaseInTempDir): server_checksum, server_ctype = checksums[ krb5pac.PAC_TYPE_SRV_CHECKSUM] - Krb5EncryptionKey.verify_checksum(key, - KU_NON_KERB_CKSUM_SALT, - pac_data, - server_ctype, - server_checksum) + key.verify_checksum(KU_NON_KERB_CKSUM_SALT, + pac_data, + server_ctype, + server_checksum) kdc_checksum, kdc_ctype = checksums[ krb5pac.PAC_TYPE_KDC_CHECKSUM] - krbtgt_key.verify_checksum(KU_NON_KERB_CKSUM_SALT, - server_checksum, - kdc_ctype, - kdc_checksum) + krbtgt_key.verify_rodc_checksum(KU_NON_KERB_CKSUM_SALT, + server_checksum, + kdc_ctype, + kdc_checksum) if is_tgt: self.assertNotIn(krb5pac.PAC_TYPE_TICKET_CHECKSUM, checksums) @@ -3106,10 +3115,10 @@ class RawKerberosTest(TestCaseInTempDir): enc_part = self.der_encode(enc_part, asn1Spec=krb5_asn1.EncTicketPart()) - krbtgt_key.verify_checksum(KU_NON_KERB_CKSUM_SALT, - enc_part, - ticket_ctype, - ticket_checksum) + krbtgt_key.verify_rodc_checksum(KU_NON_KERB_CKSUM_SALT, + enc_part, + ticket_ctype, + ticket_checksum) def modified_ticket(self, ticket, *, @@ -3300,16 +3309,15 @@ class RawKerberosTest(TestCaseInTempDir): if buffer_type == krb5pac.PAC_TYPE_TICKET_CHECKSUM: self.assertIsNotNone(enc_part) - signature = checksum_key.make_checksum( + signature = checksum_key.make_rodc_checksum( KU_NON_KERB_CKSUM_SALT, enc_part) elif buffer_type == krb5pac.PAC_TYPE_SRV_CHECKSUM: - signature = Krb5EncryptionKey.make_zeroed_checksum( - checksum_key) + signature = checksum_key.make_zeroed_checksum() else: - signature = checksum_key.make_zeroed_checksum() + signature = checksum_key.make_rodc_zeroed_checksum() checksum_buffer.info.signature = signature checksum_buffer.info.type = ctype @@ -3325,8 +3333,7 @@ class RawKerberosTest(TestCaseInTempDir): server_checksum_key = checksum_keys[krb5pac.PAC_TYPE_SRV_CHECKSUM] pac_data = ndr_pack(pac) - server_checksum = Krb5EncryptionKey.make_checksum( - server_checksum_key, + server_checksum = server_checksum_key.make_checksum( KU_NON_KERB_CKSUM_SALT, pac_data) @@ -3339,7 +3346,7 @@ class RawKerberosTest(TestCaseInTempDir): kdc_checksum_key = checksum_keys[krb5pac.PAC_TYPE_KDC_CHECKSUM] - kdc_checksum = kdc_checksum_key.make_checksum( + kdc_checksum = kdc_checksum_key.make_rodc_checksum( KU_NON_KERB_CKSUM_SALT, server_checksum) -- 2.25.1 From 22d766e823fcab412b69ba6ed0b673105a379639 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 12:03:33 +1300 Subject: [PATCH 084/159] tests/krb5: Allow excluding the PAC server checksum BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit dcf45a151a198f7165cd332a26db78a5d8e8f8c5) --- python/samba/tests/krb5/raw_testcase.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index aefbdd6d761..4c1aedbca0f 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -3342,7 +3342,10 @@ class RawKerberosTest(TestCaseInTempDir): kdc_checksum_buffer = checksum_buffers.get( krb5pac.PAC_TYPE_KDC_CHECKSUM) if kdc_checksum_buffer is not None: - self.assertIsNotNone(server_checksum_buffer) + if server_checksum_buffer is None: + # There's no server signature to make the checksum over, so + # just make the checksum over an empty bytes object. + server_checksum = bytes() kdc_checksum_key = checksum_keys[krb5pac.PAC_TYPE_KDC_CHECKSUM] -- 2.25.1 From 2dc8ddc9c80756a29e6d629fef414745c6f84f35 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 12:06:03 +1300 Subject: [PATCH 085/159] tests/krb5: Fix handling authdata with missing PAC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit a4bc712ee02f32c2d04dfc2d99d58931344e5ceb) --- python/samba/tests/krb5/raw_testcase.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 4c1aedbca0f..b9895e547c4 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -3200,11 +3200,11 @@ class RawKerberosTest(TestCaseInTempDir): # Get a copy of the authdata with an empty PAC, and the # existing PAC (if present). empty_pac = self.get_empty_pac() - empty_pac_auth_data, pac_data = self.replace_pac(auth_data, - empty_pac) + empty_pac_auth_data, pac_data = self.replace_pac( + auth_data, + empty_pac, + expect_pac=expect_pac) - if expect_pac: - self.assertIsNotNone(pac_data) if pac_data is not None: pac = ndr_unpack(krb5pac.PAC_DATA, pac_data) @@ -3234,7 +3234,8 @@ class RawKerberosTest(TestCaseInTempDir): # Replace the PAC in the authorization data and re-add it to the # ticket enc-part. - auth_data, _ = self.replace_pac(auth_data, new_pac) + auth_data, _ = self.replace_pac(auth_data, new_pac, + expect_pac=expect_pac) enc_part['authorization-data'] = auth_data # Re-encrypt the ticket enc-part with the new key. -- 2.25.1 From 623e1c88fb1bb84eb591add09f45423837730364 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 12:16:58 +1300 Subject: [PATCH 086/159] tests/krb5: Fix status code checking The type used to encode the status code is actually KERB-ERROR-DATA, rather than PA-DATA. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 012b6fcd1976c6570e9b92c133d8c21e543e5a4f) --- python/samba/tests/krb5/raw_testcase.py | 89 +++++++++----------- python/samba/tests/krb5/rfc4120_constants.py | 6 ++ 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index b9895e547c4..db7db28cac5 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -49,6 +49,7 @@ from samba.tests.krb5.rfc4120_constants import ( KDC_ERR_GENERIC, KDC_ERR_PREAUTH_FAILED, KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS, + KERB_ERR_TYPE_EXTENDED, KRB_AP_REQ, KRB_AS_REP, KRB_AS_REQ, @@ -85,7 +86,6 @@ from samba.tests.krb5.rfc4120_constants import ( PADATA_PAC_REQUEST, PADATA_PK_AS_REQ, PADATA_PK_AS_REP_19, - PADATA_PW_SALT, PADATA_SUPPORTED_ETYPES ) import samba.tests.krb5.kcrypto as kcrypto @@ -2497,34 +2497,51 @@ class RawKerberosTest(TestCaseInTempDir): self.assertIsNotNone(edata) if edata is not None: if rep_msg_type == KRB_TGS_REP and not sent_fast: - rep_padata = [self.der_decode(edata, - asn1Spec=krb5_asn1.PA_DATA())] + error_data = self.der_decode( + edata, + asn1Spec=krb5_asn1.KERB_ERROR_DATA()) + self.assertEqual(KERB_ERR_TYPE_EXTENDED, + error_data['data-type']) + + extended_error = error_data['data-value'] + + self.assertEqual(12, len(extended_error)) + + status = int.from_bytes(extended_error[:4], 'little') + flags = int.from_bytes(extended_error[8:], 'little') + + expected_status = kdc_exchange_dict['expected_status'] + self.assertEqual(expected_status, status) + + self.assertEqual(3, flags) else: + self.assertIsNone(kdc_exchange_dict['expected_status']) + rep_padata = self.der_decode(edata, asn1Spec=krb5_asn1.METHOD_DATA()) - self.assertGreater(len(rep_padata), 0) + self.assertGreater(len(rep_padata), 0) - if sent_fast: - self.assertEqual(1, len(rep_padata)) - rep_pa_dict = self.get_pa_dict(rep_padata) - self.assertIn(PADATA_FX_FAST, rep_pa_dict) + if sent_fast: + self.assertEqual(1, len(rep_padata)) + rep_pa_dict = self.get_pa_dict(rep_padata) + self.assertIn(PADATA_FX_FAST, rep_pa_dict) - armor_key = kdc_exchange_dict['armor_key'] - self.assertIsNotNone(armor_key) - fast_response = self.check_fx_fast_data( - kdc_exchange_dict, - rep_pa_dict[PADATA_FX_FAST], - armor_key, - expect_strengthen_key=False) + armor_key = kdc_exchange_dict['armor_key'] + self.assertIsNotNone(armor_key) + fast_response = self.check_fx_fast_data( + kdc_exchange_dict, + rep_pa_dict[PADATA_FX_FAST], + armor_key, + expect_strengthen_key=False) - rep_padata = fast_response['padata'] + rep_padata = fast_response['padata'] - etype_info2 = self.check_rep_padata(kdc_exchange_dict, - callback_dict, - rep_padata, - error_code) + etype_info2 = self.check_rep_padata(kdc_exchange_dict, + callback_dict, + rep_padata, + error_code) - kdc_exchange_dict['preauth_etype_info2'] = etype_info2 + kdc_exchange_dict['preauth_etype_info2'] = etype_info2 return rep @@ -2576,13 +2593,10 @@ class RawKerberosTest(TestCaseInTempDir): expected_patypes += (PADATA_FX_COOKIE,) if rep_msg_type == KRB_TGS_REP: - if not sent_fast and error_code != 0: - expected_patypes += (PADATA_PW_SALT,) - else: - sent_pac_options = self.get_sent_pac_options(kdc_exchange_dict) - if ('1' in sent_pac_options - and error_code not in (0, KDC_ERR_GENERIC)): - expected_patypes += (PADATA_PAC_OPTIONS,) + sent_pac_options = self.get_sent_pac_options(kdc_exchange_dict) + if ('1' in sent_pac_options + and error_code not in (0, KDC_ERR_GENERIC)): + expected_patypes += (PADATA_PAC_OPTIONS,) elif error_code != KDC_ERR_GENERIC: if expect_etype_info: self.assertGreater(len(expect_etype_info2), 0) @@ -2621,7 +2635,6 @@ class RawKerberosTest(TestCaseInTempDir): fast_error = None fx_fast = None pac_options = None - pw_salt = None for pa in rep_padata: patype = self.getElementValue(pa, 'padata-type') pavalue = self.getElementValue(pa, 'padata-value') @@ -2675,11 +2688,6 @@ class RawKerberosTest(TestCaseInTempDir): pavalue, asn1Spec=krb5_asn1.PA_PAC_OPTIONS()) continue - if patype == PADATA_PW_SALT: - self.assertIsNone(pw_salt) - pw_salt = pavalue - self.assertIsNotNone(pw_salt) - continue if fast_cookie is not None: kdc_exchange_dict['fast_cookie'] = fast_cookie @@ -2695,19 +2703,6 @@ class RawKerberosTest(TestCaseInTempDir): if pac_options is not None: self.assertElementEqual(pac_options, 'options', sent_pac_options) - if pw_salt is not None: - self.assertEqual(12, len(pw_salt)) - - status = int.from_bytes(pw_salt[:4], 'little') - flags = int.from_bytes(pw_salt[8:], 'little') - - expected_status = kdc_exchange_dict['expected_status'] - self.assertEqual(expected_status, status) - - self.assertEqual(3, flags) - else: - self.assertIsNone(kdc_exchange_dict.get('expected_status')) - if enc_challenge is not None: if not sent_enc_challenge: self.assertEqual(len(enc_challenge), 0) diff --git a/python/samba/tests/krb5/rfc4120_constants.py b/python/samba/tests/krb5/rfc4120_constants.py index ac2bac4d91e..76f2b75d94e 100644 --- a/python/samba/tests/krb5/rfc4120_constants.py +++ b/python/samba/tests/krb5/rfc4120_constants.py @@ -78,6 +78,12 @@ KDC_ERR_SKEW = 37 KDC_ERR_GENERIC = 60 KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS = 93 +# Extended error types +KERB_AP_ERR_TYPE_SKEW_RECOVERY = int( + krb5_asn1.KerbErrorDataTypeValues('kERB-AP-ERR-TYPE-SKEW-RECOVERY')) +KERB_ERR_TYPE_EXTENDED = int( + krb5_asn1.KerbErrorDataTypeValues('kERB-ERR-TYPE-EXTENDED')) + # Name types NT_UNKNOWN = int(krb5_asn1.NameTypeValues('kRB5-NT-UNKNOWN')) NT_PRINCIPAL = int(krb5_asn1.NameTypeValues('kRB5-NT-PRINCIPAL')) -- 2.25.1 From 2442fc8337a95942eaca079ed5a4b2f6aaae116e Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 13:01:30 +1300 Subject: [PATCH 087/159] tests/krb5: Make expected_sname checking more explicit BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett [abartlet@samba.org backported from commit 8f6d369d709614e2f5c0684882c62f0476bcafa2 as Samba 4.14 as the test which crashes older MIT KDC versions is omitted] --- python/samba/tests/krb5/fast_tests.py | 39 ++++++++++--------------- python/samba/tests/krb5/raw_testcase.py | 6 +--- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 431b48f00d6..5180eb57563 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -99,11 +99,7 @@ class FAST_Tests(KDCBaseTest): ]) def test_simple_no_sname(self): - krbtgt_creds = self.get_krbtgt_creds() - krbtgt_username = krbtgt_creds.get_username() - krbtgt_realm = krbtgt_creds.get_realm() - expected_sname = self.PrincipalName_create( - name_type=NT_SRV_INST, names=[krbtgt_username, krbtgt_realm]) + expected_sname = self.get_krbtgt_sname() self._run_test_sequence([ { @@ -116,11 +112,7 @@ class FAST_Tests(KDCBaseTest): ]) def test_simple_tgs_no_sname(self): - krbtgt_creds = self.get_krbtgt_creds() - krbtgt_username = krbtgt_creds.get_username() - krbtgt_realm = krbtgt_creds.get_realm() - expected_sname = self.PrincipalName_create( - name_type=NT_SRV_INST, names=[krbtgt_username, krbtgt_realm]) + expected_sname = self.get_krbtgt_sname() self._run_test_sequence([ { @@ -134,11 +126,7 @@ class FAST_Tests(KDCBaseTest): ]) def test_fast_no_sname(self): - krbtgt_creds = self.get_krbtgt_creds() - krbtgt_username = krbtgt_creds.get_username() - krbtgt_realm = krbtgt_creds.get_realm() - expected_sname = self.PrincipalName_create( - name_type=NT_SRV_INST, names=[krbtgt_username, krbtgt_realm]) + expected_sname = self.get_krbtgt_sname() self._run_test_sequence([ { @@ -153,11 +141,7 @@ class FAST_Tests(KDCBaseTest): ]) def test_fast_tgs_no_sname(self): - krbtgt_creds = self.get_krbtgt_creds() - krbtgt_username = krbtgt_creds.get_username() - krbtgt_realm = krbtgt_creds.get_realm() - expected_sname = self.PrincipalName_create( - name_type=NT_SRV_INST, names=[krbtgt_username, krbtgt_realm]) + expected_sname = self.get_krbtgt_sname() self._run_test_sequence([ { @@ -830,6 +814,8 @@ class FAST_Tests(KDCBaseTest): ]) def test_fast_ad_fx_fast_armor(self): + expected_sname = self.get_krbtgt_sname() + # If the authenticator or TGT authentication data contains the # AD-fx-fast-armor authdata type, the KDC must reject the request # (RFC6113 5.4.1.1). @@ -849,7 +835,8 @@ class FAST_Tests(KDCBaseTest): 'use_fast': True, 'gen_authdata_fn': self.generate_fast_armor_auth_data, 'gen_tgt_fn': self.get_user_tgt, - 'fast_armor': None + 'fast_armor': None, + 'expected_sname': expected_sname } ]) @@ -877,6 +864,8 @@ class FAST_Tests(KDCBaseTest): ]) def test_fast_ad_fx_fast_armor_ticket(self): + expected_sname = self.get_krbtgt_sname() + # If the authenticator or TGT authentication data contains the # AD-fx-fast-armor authdata type, the KDC must reject the request # (RFC6113 5.4.2). @@ -896,7 +885,8 @@ class FAST_Tests(KDCBaseTest): 'expected_error_mode': KDC_ERR_GENERIC, 'use_fast': True, 'gen_tgt_fn': self.gen_tgt_fast_armor_auth_data, - 'fast_armor': None + 'fast_armor': None, + 'expected_sname': expected_sname } ]) @@ -956,6 +946,8 @@ class FAST_Tests(KDCBaseTest): ]) def test_fast_tgs_no_subkey(self): + expected_sname = self.get_krbtgt_sname() + # Show that omitting the subkey in the TGS-REQ authenticator fails # (RFC6113 5.4.2). self._run_test_sequence([ @@ -965,7 +957,8 @@ class FAST_Tests(KDCBaseTest): 'use_fast': True, 'gen_tgt_fn': self.get_user_tgt, 'fast_armor': None, - 'include_subkey': False + 'include_subkey': False, + 'expected_sname': expected_sname } ]) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index db7db28cac5..f6aeb00dc8f 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2475,11 +2475,7 @@ class RawKerberosTest(TestCaseInTempDir): else: self.assertElementMissing(rep, 'cname') self.assertElementEqualUTF8(rep, 'realm', expected_srealm) - if sent_fast and error_code == KDC_ERR_GENERIC: - self.assertElementEqualPrincipal(rep, 'sname', - self.get_krbtgt_sname()) - else: - self.assertElementEqualPrincipal(rep, 'sname', expected_sname) + self.assertElementEqualPrincipal(rep, 'sname', expected_sname) self.assertElementMissing(rep, 'e-text') if (error_code == KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS or (rep_msg_type == KRB_TGS_REP -- 2.25.1 From f03a675183f15fbbf5992cb86dd691df37ad8e9a Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 13:03:49 +1300 Subject: [PATCH 088/159] tests/krb5: Fix assertElementFlags() BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 788b3a29eea62f9f38ca8865c7cb7860bdc94bec) --- python/samba/tests/krb5/fast_tests.py | 4 ++-- python/samba/tests/krb5/raw_testcase.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 5180eb57563..cf9b9d718bb 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -1305,10 +1305,10 @@ class FAST_Tests(KDCBaseTest): expected_flags = kdc_dict.pop('expected_flags', None) if expected_flags is not None: - expected_flags = krb5_asn1.KDCOptions(expected_flags) + expected_flags = krb5_asn1.TicketFlags(expected_flags) unexpected_flags = kdc_dict.pop('unexpected_flags', None) if unexpected_flags is not None: - unexpected_flags = krb5_asn1.KDCOptions(unexpected_flags) + unexpected_flags = krb5_asn1.TicketFlags(unexpected_flags) if rep_type == KRB_AS_REP: kdc_exchange_dict = self.as_exchange_dict( diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index f6aeb00dc8f..a24faf1d060 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1053,14 +1053,14 @@ class RawKerberosTest(TestCaseInTempDir): v = self.getElementValue(obj, elem) self.assertIsNotNone(v) if expected is not None: - self.assertIsInstance(expected, krb5_asn1.KDCOptions) + self.assertIsInstance(expected, krb5_asn1.TicketFlags) for i, flag in enumerate(expected): if flag == 1: self.assertEqual('1', v[i], f"'{expected.namedValues[i]}' " f"expected in {v}") if unexpected is not None: - self.assertIsInstance(unexpected, krb5_asn1.KDCOptions) + self.assertIsInstance(unexpected, krb5_asn1.TicketFlags) for i, flag in enumerate(unexpected): if flag == 1: self.assertEqual('0', v[i], -- 2.25.1 From daece552364da4dc00e8ffc585aff25c5627e14e Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 14:02:37 +1300 Subject: [PATCH 089/159] tests/krb5: Remove unneeded parameters from ticket cache key BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 7fba83c6c6309a525742c38e904d3e473db99ef1) --- python/samba/tests/krb5/kdc_base_test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 25433ba1069..7ddaa53b541 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1278,8 +1278,7 @@ class KDCBaseTest(RawKerberosTest): expected_flags=None, unexpected_flags=None, fresh=False): user_name = creds.get_username() - cache_key = (user_name, to_rodc, kdc_options, - expected_flags, unexpected_flags) + cache_key = (user_name, to_rodc, kdc_options) if not fresh: tgt = self.tkt_cache.get(cache_key) -- 2.25.1 From 40aa7f266bdf1f50e4410a9285bbbee9c51c0516 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 15:48:58 +1300 Subject: [PATCH 090/159] tests/krb5: Fix checking for presence of error data BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit ab92dc16d20b0996b8c46714652c15019c795095) --- python/samba/tests/krb5/fast_tests.py | 39 +++++++++++++++++------- python/samba/tests/krb5/kdc_base_test.py | 4 ++- python/samba/tests/krb5/kdc_tgs_tests.py | 3 +- python/samba/tests/krb5/raw_testcase.py | 27 ++++++++-------- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index cf9b9d718bb..28fe0686335 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -107,7 +107,8 @@ class FAST_Tests(KDCBaseTest): 'expected_error_mode': (KDC_ERR_GENERIC, KDC_ERR_S_PRINCIPAL_UNKNOWN), 'use_fast': False, 'sname': None, - 'expected_sname': expected_sname + 'expected_sname': expected_sname, + 'expect_edata': False } ]) @@ -121,7 +122,8 @@ class FAST_Tests(KDCBaseTest): 'use_fast': False, 'gen_tgt_fn': self.get_user_tgt, 'sname': None, - 'expected_sname': expected_sname + 'expected_sname': expected_sname, + 'expect_edata': False } ]) @@ -172,6 +174,7 @@ class FAST_Tests(KDCBaseTest): 'expected_error_mode': KDC_ERR_NOT_US, 'use_fast': False, 'gen_tgt_fn': self.get_user_service_ticket, + 'expect_edata': False } ]) @@ -182,6 +185,7 @@ class FAST_Tests(KDCBaseTest): 'expected_error_mode': KDC_ERR_NOT_US, 'use_fast': False, 'gen_tgt_fn': self.get_mach_service_ticket, + 'expect_edata': False } ]) @@ -294,7 +298,8 @@ class FAST_Tests(KDCBaseTest): 'expected_error_mode': KDC_ERR_ETYPE_NOSUPP, 'use_fast': False, 'gen_tgt_fn': self.get_mach_tgt, - 'etypes': () + 'etypes': (), + 'expect_edata': False } ]) @@ -342,7 +347,8 @@ class FAST_Tests(KDCBaseTest): 'use_fast': True, 'gen_fast_fn': self.generate_empty_fast, 'fast_armor': None, - 'gen_armor_tgt_fn': self.get_mach_tgt + 'gen_armor_tgt_fn': self.get_mach_tgt, + 'expect_edata': False } ]) @@ -365,7 +371,8 @@ class FAST_Tests(KDCBaseTest): 'expected_error_mode': KDC_ERR_GENERIC, 'use_fast': True, 'fast_armor': None, # no armor, - 'gen_armor_tgt_fn': self.get_mach_tgt + 'gen_armor_tgt_fn': self.get_mach_tgt, + 'expect_edata': False } ]) @@ -809,7 +816,8 @@ class FAST_Tests(KDCBaseTest): # should be KRB_APP_ERR_MODIFIED 'use_fast': False, 'gen_authdata_fn': self.generate_fast_used_auth_data, - 'gen_tgt_fn': self.get_user_tgt + 'gen_tgt_fn': self.get_user_tgt, + 'expect_edata': False } ]) @@ -836,7 +844,8 @@ class FAST_Tests(KDCBaseTest): 'gen_authdata_fn': self.generate_fast_armor_auth_data, 'gen_tgt_fn': self.get_user_tgt, 'fast_armor': None, - 'expected_sname': expected_sname + 'expected_sname': expected_sname, + 'expect_edata': False } ]) @@ -886,7 +895,8 @@ class FAST_Tests(KDCBaseTest): 'use_fast': True, 'gen_tgt_fn': self.gen_tgt_fast_armor_auth_data, 'fast_armor': None, - 'expected_sname': expected_sname + 'expected_sname': expected_sname, + 'expect_edata': False } ]) @@ -958,7 +968,8 @@ class FAST_Tests(KDCBaseTest): 'gen_tgt_fn': self.get_user_tgt, 'fast_armor': None, 'include_subkey': False, - 'expected_sname': expected_sname + 'expected_sname': expected_sname, + 'expect_edata': False } ]) @@ -1209,6 +1220,10 @@ class FAST_Tests(KDCBaseTest): else: tgt_cname = client_cname + expect_edata = kdc_dict.pop('expect_edata', None) + if expect_edata is not None: + self.assertTrue(expected_error_mode) + expected_cname = kdc_dict.pop('expected_cname', tgt_cname) expected_anon = kdc_dict.pop('expected_anon', False) @@ -1343,7 +1358,8 @@ class FAST_Tests(KDCBaseTest): inner_req=inner_req, outer_req=outer_req, pac_request=True, - pac_options=pac_options) + pac_options=pac_options, + expect_edata=expect_edata) else: # KRB_TGS_REP kdc_exchange_dict = self.tgs_exchange_dict( expected_crealm=expected_crealm, @@ -1376,7 +1392,8 @@ class FAST_Tests(KDCBaseTest): inner_req=inner_req, outer_req=outer_req, pac_request=None, - pac_options=pac_options) + pac_options=pac_options, + expect_edata=expect_edata) repeat = kdc_dict.pop('repeat', 1) for _ in range(repeat): diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 7ddaa53b541..d25fc0b42b2 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1162,7 +1162,8 @@ class KDCBaseTest(RawKerberosTest): def tgs_req(self, cname, sname, realm, ticket, key, etypes, expected_error_mode=0, padata=None, kdc_options=0, - to_rodc=False, service_creds=None, expect_pac=True): + to_rodc=False, service_creds=None, expect_pac=True, + expect_edata=None): '''Send a TGS-REQ, returns the response and the decrypted and decoded enc-part ''' @@ -1209,6 +1210,7 @@ class KDCBaseTest(RawKerberosTest): tgt=tgt, authenticator_subkey=subkey, kdc_options=str(kdc_options), + expect_edata=expect_edata, expect_pac=expect_pac, to_rodc=to_rodc) diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py index 0904233b01f..2b55ba8a376 100755 --- a/python/samba/tests/krb5/kdc_tgs_tests.py +++ b/python/samba/tests/krb5/kdc_tgs_tests.py @@ -85,7 +85,8 @@ class KdcTgsTests(KDCBaseTest): names=["host", samdb.host_dns_name()]) (rep, enc_part) = self.tgs_req(cname, sname, realm, ticket, key, etype, - expected_error_mode=KDC_ERR_BADMATCH) + expected_error_mode=KDC_ERR_BADMATCH, + expect_edata=False) self.assertIsNone( enc_part, diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index a24faf1d060..1a3aedbd436 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1959,6 +1959,7 @@ class RawKerberosTest(TestCaseInTempDir): outer_req=None, pac_request=None, pac_options=None, + expect_edata=None, expect_pac=True, to_rodc=False): if expected_error_mode == 0: @@ -2005,6 +2006,7 @@ class RawKerberosTest(TestCaseInTempDir): 'outer_req': outer_req, 'pac_request': pac_request, 'pac_options': pac_options, + 'expect_edata': expect_edata, 'expect_pac': expect_pac, 'to_rodc': to_rodc } @@ -2046,6 +2048,7 @@ class RawKerberosTest(TestCaseInTempDir): outer_req=None, pac_request=None, pac_options=None, + expect_edata=None, expect_pac=True, to_rodc=False): if expected_error_mode == 0: @@ -2091,6 +2094,7 @@ class RawKerberosTest(TestCaseInTempDir): 'outer_req': outer_req, 'pac_request': pac_request, 'pac_options': pac_options, + 'expect_edata': expect_edata, 'expect_pac': expect_pac, 'to_rodc': to_rodc } @@ -2477,20 +2481,20 @@ class RawKerberosTest(TestCaseInTempDir): self.assertElementEqualUTF8(rep, 'realm', expected_srealm) self.assertElementEqualPrincipal(rep, 'sname', expected_sname) self.assertElementMissing(rep, 'e-text') - if (error_code == KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS - or (rep_msg_type == KRB_TGS_REP - and not sent_fast) - or (sent_fast and fast_armor_type is not None - and fast_armor_type != FX_FAST_ARMOR_AP_REQUEST) - or inner): + expected_status = kdc_exchange_dict['expected_status'] + expect_edata = kdc_exchange_dict['expect_edata'] + if expect_edata is None: + expect_edata = (error_code != KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS + and (not sent_fast or fast_armor_type is None + or fast_armor_type == FX_FAST_ARMOR_AP_REQUEST) + and not inner) + if not expect_edata: + self.assertIsNone(expected_status) self.assertElementMissing(rep, 'e-data') return rep edata = self.getElementValue(rep, 'e-data') if self.strict_checking: - if error_code != KDC_ERR_GENERIC: - # Predicting whether an ERR_GENERIC error contains e-data is - # more complicated. - self.assertIsNotNone(edata) + self.assertIsNotNone(edata) if edata is not None: if rep_msg_type == KRB_TGS_REP and not sent_fast: error_data = self.der_decode( @@ -2506,12 +2510,11 @@ class RawKerberosTest(TestCaseInTempDir): status = int.from_bytes(extended_error[:4], 'little') flags = int.from_bytes(extended_error[8:], 'little') - expected_status = kdc_exchange_dict['expected_status'] self.assertEqual(expected_status, status) self.assertEqual(3, flags) else: - self.assertIsNone(kdc_exchange_dict['expected_status']) + self.assertIsNone(expected_status) rep_padata = self.der_decode(edata, asn1Spec=krb5_asn1.METHOD_DATA()) -- 2.25.1 From 4055fc3d8ade7dd706168a955693e459ba4b6646 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 16:10:07 +1300 Subject: [PATCH 091/159] tests/krb5: Add expect_claims parameter to kdc_exchange_dict BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 7cfc225b549108739bd86e222f2f35eb96af4ea3) --- python/samba/tests/krb5/raw_testcase.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 1a3aedbd436..0415f1ff6e6 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1961,6 +1961,7 @@ class RawKerberosTest(TestCaseInTempDir): pac_options=None, expect_edata=None, expect_pac=True, + expect_claims=True, to_rodc=False): if expected_error_mode == 0: expected_error_mode = () @@ -2008,6 +2009,7 @@ class RawKerberosTest(TestCaseInTempDir): 'pac_options': pac_options, 'expect_edata': expect_edata, 'expect_pac': expect_pac, + 'expect_claims': expect_claims, 'to_rodc': to_rodc } if callback_dict is None: @@ -2050,6 +2052,7 @@ class RawKerberosTest(TestCaseInTempDir): pac_options=None, expect_edata=None, expect_pac=True, + expect_claims=True, to_rodc=False): if expected_error_mode == 0: expected_error_mode = () @@ -2096,6 +2099,7 @@ class RawKerberosTest(TestCaseInTempDir): 'pac_options': pac_options, 'expect_edata': expect_edata, 'expect_pac': expect_pac, + 'expect_claims': expect_claims, 'to_rodc': to_rodc } if callback_dict is None: -- 2.25.1 From 31a2b4221ca6ab2508ac76cefd2aedabde049086 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 16:15:26 +1300 Subject: [PATCH 092/159] tests/krb5: Check buffer types in PAC with STRICT_CHECKING=1 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit aa2e583fdea4fd93e4e71c54630e32a1035d1e2a) --- librpc/idl/krb5pac.idl | 3 ++ python/samba/tests/krb5/raw_testcase.py | 52 +++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/librpc/idl/krb5pac.idl b/librpc/idl/krb5pac.idl index 3239d7656b6..515150ab9cd 100644 --- a/librpc/idl/krb5pac.idl +++ b/librpc/idl/krb5pac.idl @@ -113,6 +113,9 @@ interface krb5pac PAC_TYPE_LOGON_NAME = 10, PAC_TYPE_CONSTRAINED_DELEGATION = 11, PAC_TYPE_UPN_DNS_INFO = 12, + PAC_TYPE_CLIENT_CLAIMS_INFO = 13, + PAC_TYPE_DEVICE_INFO = 14, + PAC_TYPE_DEVICE_CLAIMS_INFO = 15, PAC_TYPE_TICKET_CHECKSUM = 16 } PAC_TYPE; diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 0415f1ff6e6..320de0a4dbe 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2340,6 +2340,13 @@ class RawKerberosTest(TestCaseInTempDir): self.assertElementPresent(ticket_private, 'authorization-data', expect_empty=not expect_pac) + if expect_pac: + authorization_data = self.getElementValue(ticket_private, + 'authorization-data') + pac_data = self.get_pac(authorization_data) + + self.check_pac_buffers(pac_data, kdc_exchange_dict) + encpart_session_key = None if encpart_private is not None: self.assertElementPresent(encpart_private, 'key') @@ -2446,6 +2453,47 @@ class RawKerberosTest(TestCaseInTempDir): kdc_exchange_dict['rep_ticket_creds'] = ticket_creds + def check_pac_buffers(self, pac_data, kdc_exchange_dict): + pac = ndr_unpack(krb5pac.PAC_DATA, pac_data) + + rep_msg_type = kdc_exchange_dict['rep_msg_type'] + armor_tgt = kdc_exchange_dict['armor_tgt'] + + expected_sname = kdc_exchange_dict['expected_sname'] + expect_claims = kdc_exchange_dict['expect_claims'] + + expected_types = [krb5pac.PAC_TYPE_LOGON_INFO, + krb5pac.PAC_TYPE_SRV_CHECKSUM, + krb5pac.PAC_TYPE_KDC_CHECKSUM, + krb5pac.PAC_TYPE_LOGON_NAME, + krb5pac.PAC_TYPE_UPN_DNS_INFO] + + kdc_options = kdc_exchange_dict['kdc_options'] + pos = len(tuple(krb5_asn1.KDCOptions('cname-in-addl-tkt'))) - 1 + constrained_delegation = (pos < len(kdc_options) + and kdc_options[pos] == '1') + if constrained_delegation: + expected_types.append(krb5pac.PAC_TYPE_CONSTRAINED_DELEGATION) + + if self.kdc_fast_support: + if expect_claims: + expected_types.append(krb5pac.PAC_TYPE_CLIENT_CLAIMS_INFO) + + if (rep_msg_type == KRB_TGS_REP + and armor_tgt is not None): + expected_types.append(krb5pac.PAC_TYPE_DEVICE_INFO) + expected_types.append(krb5pac.PAC_TYPE_DEVICE_CLAIMS_INFO) + + if not self.is_tgs(expected_sname): + expected_types.append(krb5pac.PAC_TYPE_TICKET_CHECKSUM) + + if self.strict_checking: + buffer_types = [pac_buffer.type + for pac_buffer in pac.buffers] + self.assertCountEqual(expected_types, buffer_types, + f'expected: {expected_types} ' + f'got: {buffer_types}') + def generic_check_kdc_error(self, kdc_exchange_dict, callback_dict, @@ -3397,6 +3445,10 @@ class RawKerberosTest(TestCaseInTempDir): return new_auth_data, old_pac + def get_pac(self, auth_data, expect_pac=True): + _, pac = self.replace_pac(auth_data, None, expect_pac) + return pac + def get_krbtgt_checksum_key(self): krbtgt_creds = self.get_krbtgt_creds() krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) -- 2.25.1 From 1ef4641c28ec084ba0b4cb2e40976972581d71d5 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 16:26:54 +1300 Subject: [PATCH 093/159] tests/krb5: Check constrained delegation PAC buffer BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 0e232fa1c9e5760ae6b9a99b5e7aa5513b84aa8b) --- python/samba/tests/krb5/raw_testcase.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 320de0a4dbe..8144bd37b2f 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2053,6 +2053,8 @@ class RawKerberosTest(TestCaseInTempDir): expect_edata=None, expect_pac=True, expect_claims=True, + expected_proxy_target=None, + expected_transited_services=None, to_rodc=False): if expected_error_mode == 0: expected_error_mode = () @@ -2100,6 +2102,8 @@ class RawKerberosTest(TestCaseInTempDir): 'expect_edata': expect_edata, 'expect_pac': expect_pac, 'expect_claims': expect_claims, + 'expected_proxy_target': expected_proxy_target, + 'expected_transited_services': expected_transited_services, 'to_rodc': to_rodc } if callback_dict is None: @@ -2494,6 +2498,23 @@ class RawKerberosTest(TestCaseInTempDir): f'expected: {expected_types} ' f'got: {buffer_types}') + for pac_buffer in pac.buffers: + if pac_buffer.type == krb5pac.PAC_TYPE_CONSTRAINED_DELEGATION: + expected_proxy_target = kdc_exchange_dict[ + 'expected_proxy_target'] + expected_transited_services = kdc_exchange_dict[ + 'expected_transited_services'] + + delegation_info = pac_buffer.info.info + + self.assertEqual(expected_proxy_target, + str(delegation_info.proxy_target)) + + transited_services = list(map( + str, delegation_info.transited_services)) + self.assertEqual(expected_transited_services, + transited_services) + def generic_check_kdc_error(self, kdc_exchange_dict, callback_dict, -- 2.25.1 From 0f3a5ab6a41a85aa99eb1e225f6515588ff9b781 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 16:41:23 +1300 Subject: [PATCH 094/159] tests/krb5: Save account SPN This is useful for testing delegation. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit bb58b4b58c66a6ada79e886dd0c44401e1c5878c) --- python/samba/tests/krb5/kdc_base_test.py | 1 + python/samba/tests/krb5/raw_testcase.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index d25fc0b42b2..70ab14786da 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -285,6 +285,7 @@ class KDCBaseTest(RawKerberosTest): else: creds.set_workstation('') creds.set_dn(ldb.Dn(samdb, dn)) + creds.set_spn(spn) # # Save the account name so it can be deleted in tearDownClass self.accounts.add(dn) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 8144bd37b2f..c34ffb848e1 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -365,6 +365,7 @@ class KerberosCredentials(Credentials): self.forced_salt = None self.dn = None + self.spn = None def set_as_supported_enctypes(self, value): self.as_supported_enctypes = int(value) @@ -467,6 +468,12 @@ class KerberosCredentials(Credentials): def get_dn(self): return self.dn + def set_spn(self, spn): + self.spn = spn + + def get_spn(self): + return self.spn + class KerberosTicketCreds: def __init__(self, ticket, session_key, -- 2.25.1 From b4d1f97a9887ca0d398f5e68f9bbed937fdb67bc Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 16:48:50 +1300 Subject: [PATCH 095/159] tests/krb5: Allow specifying options and expected flags when obtaining a ticket BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 34020766bb7094d1ab5d4fc4c0ee89ccb81f39f1) --- python/samba/tests/krb5/kdc_base_test.py | 31 +++++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 70ab14786da..34a23b3b876 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1164,7 +1164,7 @@ class KDCBaseTest(RawKerberosTest): def tgs_req(self, cname, sname, realm, ticket, key, etypes, expected_error_mode=0, padata=None, kdc_options=0, to_rodc=False, service_creds=None, expect_pac=True, - expect_edata=None): + expect_edata=None, expected_flags=None, unexpected_flags=None): '''Send a TGS-REQ, returns the response and the decrypted and decoded enc-part ''' @@ -1203,6 +1203,8 @@ class KDCBaseTest(RawKerberosTest): expected_srealm=realm, expected_sname=sname, expected_error_mode=expected_error_mode, + expected_flags=expected_flags, + unexpected_flags=unexpected_flags, check_error_fn=check_error_fn, check_rep_fn=check_rep_fn, check_kdc_private_fn=self.generic_check_kdc_private, @@ -1230,10 +1232,12 @@ class KDCBaseTest(RawKerberosTest): return rep, enc_part def get_service_ticket(self, tgt, target_creds, service='host', - to_rodc=False, fresh=False): + to_rodc=False, kdc_options=None, + expected_flags=None, unexpected_flags=None, + fresh=False): user_name = tgt.cname['name-string'][0] target_name = target_creds.get_username() - cache_key = (user_name, target_name, service, to_rodc) + cache_key = (user_name, target_name, service, to_rodc, kdc_options) if not fresh: ticket = self.tkt_cache.get(cache_key) @@ -1243,6 +1247,10 @@ class KDCBaseTest(RawKerberosTest): etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) + if kdc_options is None: + kdc_options = '0' + kdc_options = krb5_asn1.KDCOptions(kdc_options) + key = tgt.session_key ticket = tgt.ticket @@ -1255,7 +1263,10 @@ class KDCBaseTest(RawKerberosTest): rep, enc_part = self.tgs_req(cname, sname, realm, ticket, key, etype, to_rodc=to_rodc, - service_creds=target_creds) + service_creds=target_creds, + kdc_options=kdc_options, + expected_flags=expected_flags, + unexpected_flags=unexpected_flags) service_ticket = rep['ticket'] @@ -1309,11 +1320,11 @@ class KDCBaseTest(RawKerberosTest): self.TicketDecryptionKey_from_creds(krbtgt_creds)) if kdc_options is None: - kdc_options = krb5_asn1.KDCOptions('forwardable,' - 'renewable,' - 'canonicalize,' - 'renewable-ok') - kdc_options = str(kdc_options) + kdc_options = ('forwardable,' + 'renewable,' + 'canonicalize,' + 'renewable-ok') + kdc_options = krb5_asn1.KDCOptions(kdc_options) pac_options = '1' # supports claims @@ -1370,6 +1381,8 @@ class KDCBaseTest(RawKerberosTest): expected_srealm=expected_realm, expected_sname=expected_sname, expected_salt=salt, + expected_flags=expected_flags, + unexpected_flags=unexpected_flags, expected_supported_etypes=expected_etypes, etypes=etype, padata=padata, -- 2.25.1 From d8631f5a28d34a34a7d42f6b47b6b0737bc25889 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 16:52:01 +1300 Subject: [PATCH 096/159] tests/krb5: Supply supported account enctypes in tgs_req() BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 248249dc0acac89d1495c3572cbd2cbe8bdca362) --- python/samba/tests/krb5/kdc_base_test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 34a23b3b876..93951586cc7 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1181,8 +1181,10 @@ class KDCBaseTest(RawKerberosTest): if service_creds is not None: decryption_key = self.TicketDecryptionKey_from_creds( service_creds) + expected_supported_etypes = service_creds.tgs_supported_enctypes else: decryption_key = None + expected_supported_etypes = None if not expected_error_mode: check_error_fn = None @@ -1205,6 +1207,7 @@ class KDCBaseTest(RawKerberosTest): expected_error_mode=expected_error_mode, expected_flags=expected_flags, unexpected_flags=unexpected_flags, + expected_supported_etypes=expected_supported_etypes, check_error_fn=check_error_fn, check_rep_fn=check_rep_fn, check_kdc_private_fn=self.generic_check_kdc_private, -- 2.25.1 From 483b623ca8eca70002f0fd493da8f289bc947fc9 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 30 Sep 2021 16:53:35 +1300 Subject: [PATCH 097/159] tests/krb5: Add parameter to enforce presence of ticket checksums This allows existing tests to pass before this functionality is implemented. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit ef24fe982d750a42be81808379b0254d8488c559) --- python/samba/tests/krb5/raw_testcase.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index c34ffb848e1..72a39b23b0e 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1942,6 +1942,7 @@ class RawKerberosTest(TestCaseInTempDir): expected_flags=None, unexpected_flags=None, ticket_decryption_key=None, + expect_ticket_checksum=None, generate_fast_fn=None, generate_fast_armor_fn=None, generate_fast_padata_fn=None, @@ -1990,6 +1991,7 @@ class RawKerberosTest(TestCaseInTempDir): 'expected_flags': expected_flags, 'unexpected_flags': unexpected_flags, 'ticket_decryption_key': ticket_decryption_key, + 'expect_ticket_checksum': expect_ticket_checksum, 'generate_fast_fn': generate_fast_fn, 'generate_fast_armor_fn': generate_fast_armor_fn, 'generate_fast_padata_fn': generate_fast_padata_fn, @@ -2034,6 +2036,7 @@ class RawKerberosTest(TestCaseInTempDir): expected_flags=None, unexpected_flags=None, ticket_decryption_key=None, + expect_ticket_checksum=None, generate_fast_fn=None, generate_fast_armor_fn=None, generate_fast_padata_fn=None, @@ -2083,6 +2086,7 @@ class RawKerberosTest(TestCaseInTempDir): 'expected_flags': expected_flags, 'unexpected_flags': unexpected_flags, 'ticket_decryption_key': ticket_decryption_key, + 'expect_ticket_checksum': expect_ticket_checksum, 'generate_fast_fn': generate_fast_fn, 'generate_fast_armor_fn': generate_fast_armor_fn, 'generate_fast_padata_fn': generate_fast_padata_fn, @@ -2459,8 +2463,15 @@ class RawKerberosTest(TestCaseInTempDir): ticket_private=ticket_private, encpart_private=encpart_private) + # TODO: This parameter should be removed when all service tickets are + # issued with ticket checksums. + expect_ticket_checksum = kdc_exchange_dict['expect_ticket_checksum'] + if expect_ticket_checksum: + self.assertIsNotNone(ticket_decryption_key) + if ticket_decryption_key is not None: - self.verify_ticket(ticket_creds, krbtgt_key, expect_pac=expect_pac) + self.verify_ticket(ticket_creds, krbtgt_key, expect_pac=expect_pac, + expect_ticket_checksum=expect_ticket_checksum) kdc_exchange_dict['rep_ticket_creds'] = ticket_creds @@ -3083,7 +3094,8 @@ class RawKerberosTest(TestCaseInTempDir): ticket_blob) self.assertEqual(expected_checksum, checksum) - def verify_ticket(self, ticket, krbtgt_key, expect_pac=True): + def verify_ticket(self, ticket, krbtgt_key, expect_pac=True, + expect_ticket_checksum=True): # Check if the ticket is a TGT. sname = ticket.ticket['sname'] is_tgt = self.is_tgs(sname) @@ -3182,8 +3194,10 @@ class RawKerberosTest(TestCaseInTempDir): ticket_checksum, ticket_ctype = checksums.get( krb5pac.PAC_TYPE_TICKET_CHECKSUM, (None, None)) - if self.strict_checking: + if expect_ticket_checksum: self.assertIsNotNone(ticket_checksum) + elif expect_ticket_checksum is False: + self.assertIsNone(ticket_checksum) if ticket_checksum is not None: enc_part['authorization-data'] = auth_data enc_part = self.der_encode(enc_part, -- 2.25.1 From 95cc7ea52f62c4c6d44ea47bb45e36e25bc77f0a Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 14 Oct 2021 16:43:05 +1300 Subject: [PATCH 098/159] tests/krb5: Add compatability tests for ticket checksums BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett [abartlet@samba.org: Backported from ec4b264bdf9ab64a728212580b344fbf35c3c673 to Samba 4.14 due to conflicts in knownfail as the test which crashes older MIT KDC versions is omitted] --- .../samba/tests/krb5/compatability_tests.py | 44 ++++++++++++++++++- selftest/knownfail_heimdal_kdc | 6 ++- source4/selftest/tests.py | 7 ++- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/python/samba/tests/krb5/compatability_tests.py b/python/samba/tests/krb5/compatability_tests.py index cd67549212a..0da72796894 100755 --- a/python/samba/tests/krb5/compatability_tests.py +++ b/python/samba/tests/krb5/compatability_tests.py @@ -23,7 +23,7 @@ import os sys.path.insert(0, "bin/python") os.environ["PYTHONUNBUFFERED"] = "1" -from samba.tests.krb5.raw_testcase import RawKerberosTest +from samba.tests.krb5.kdc_base_test import KDCBaseTest import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1 from samba.tests.krb5.rfc4120_constants import ( AES128_CTS_HMAC_SHA1_96, @@ -50,7 +50,7 @@ MIT_ENC_AS_REP_PART_TYPE_TAG = 0x7A ENC_PA_REP_FLAG = 0x00010000 -class SimpleKerberosTests(RawKerberosTest): +class SimpleKerberosTests(KDCBaseTest): def setUp(self): super(SimpleKerberosTests, self).setUp() @@ -120,6 +120,46 @@ class SimpleKerberosTests(RawKerberosTest): self.fail( "(Heimdal) Salt populated for ARCFOUR_HMAC_MD5 encryption") + def test_heimdal_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() + + 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) + + # 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, expect_ticket_checksum=True) + + def test_mit_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() + + 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) + + # Get a service ticket from the DC. + service_ticket = self.get_service_ticket(tgt, target_creds) + + # Ensure the PAC does not contain the expected checksums. + self.verify_ticket(service_ticket, key, expect_ticket_checksum=False) + def as_pre_auth_req(self, creds, etypes): user = creds.get_username() realm = creds.get_realm() diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 80b8224f015..6eb667f8969 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -1,7 +1,7 @@ # # We expect all the MIT specific compatability tests to fail on heimdal # kerberos -^samba.tests.krb5.compatability_tests.samba.tests.krb5.compatability_tests.SimpleKerberosTests.test_mit_ +^samba.tests.krb5.compatability_tests.samba.tests.krb5.compatability_tests.SimpleKerberosTests.test_mit_(?!ticket_signature) # # Heimdal currently fails the following MS-KILE client principal lookup # tests @@ -121,3 +121,7 @@ ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_outer_no_sname.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_tgs_outer_no_sname.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_no_sname.ad_dc +# +# Heimdal currently does not generate ticket signatures +# +^samba.tests.krb5.compatability_tests.samba.tests.krb5.compatability_tests.SimpleKerberosTests.test_heimdal_ticket_signature diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index c83136d6421..92e5b8f5946 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -1424,7 +1424,12 @@ planpythontestsuite("ad_dc", "samba.tests.krb5.as_canonicalization_tests", 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD' }) -planpythontestsuite("ad_dc", "samba.tests.krb5.compatability_tests") +planpythontestsuite("ad_dc", "samba.tests.krb5.compatability_tests", + environ={ + 'ADMIN_USERNAME': '$USERNAME', + 'ADMIN_PASSWORD': '$PASSWORD', + 'STRICT_CHECKING': '0', + }) planpythontestsuite("ad_dc", "samba.tests.krb5.kdc_tests") planpythontestsuite( "ad_dc", -- 2.25.1 From 6b06fb1dc61f62dde6619e9845b5e15b5fbef255 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 11 Oct 2021 14:37:03 +1300 Subject: [PATCH 099/159] tests/krb5: Use correct principal name type BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 687c8f94c68af9f1e44771dfd7219eeb41382bba) --- python/samba/tests/krb5/fast_tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/fast_tests.py b/python/samba/tests/krb5/fast_tests.py index 28fe0686335..1cdfd5e20c4 100755 --- a/python/samba/tests/krb5/fast_tests.py +++ b/python/samba/tests/krb5/fast_tests.py @@ -43,6 +43,7 @@ from samba.tests.krb5.rfc4120_constants import ( KRB_AS_REP, KRB_TGS_REP, NT_PRINCIPAL, + NT_SRV_HST, NT_SRV_INST, PADATA_FX_COOKIE, PADATA_FX_FAST, @@ -1136,7 +1137,7 @@ class FAST_Tests(KDCBaseTest): target_realm = target_creds.get_realm() target_service = 'host' target_sname = self.PrincipalName_create( - name_type=NT_SRV_INST, names=[target_service, target_username]) + name_type=NT_SRV_HST, names=[target_service, target_username]) target_decryption_key = self.TicketDecryptionKey_from_creds( target_creds) target_etypes = target_creds.tgs_supported_enctypes -- 2.25.1 From 5e150a9aaabaf31f2eadc9f17b7e5aa2241ff033 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 11 Oct 2021 14:39:26 +1300 Subject: [PATCH 100/159] tests/krb5: Clarify checksum type assertion message BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit ee2b7e2c77f021984ec583fa0c4c756979197b0f) --- python/samba/tests/krb5/raw_testcase.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 72a39b23b0e..e52eb5ae5b9 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -248,7 +248,8 @@ class Krb5EncryptionKey: def verify_checksum(self, usage, plaintext, ctype, cksum): if self.ctype != ctype: - raise AssertionError(f'{self.ctype} != {ctype}') + raise AssertionError(f'key checksum type ({self.ctype}) != ' + f'checksum type ({ctype})') kcrypto.verify_checksum(ctype, self.key, -- 2.25.1 From b7d628065e848460318305052e792758e59685f2 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 11 Oct 2021 16:15:43 +1300 Subject: [PATCH 101/159] tests/krb5: Fix padata checking at functional level 2003 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 72265227e9c2037b63cdfb01a456a86ac8932f59) --- python/samba/tests/krb5/raw_testcase.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index e52eb5ae5b9..bb9d414da5b 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2661,11 +2661,10 @@ class RawKerberosTest(TestCaseInTempDir): if kcrypto.Enctype.RC4 in proposed_etypes: expect_etype_info = True for etype in proposed_etypes: - if etype in (kcrypto.Enctype.AES256, kcrypto.Enctype.AES128): - expect_etype_info = False if etype not in client_as_etypes: continue if etype in (kcrypto.Enctype.AES256, kcrypto.Enctype.AES128): + expect_etype_info = False if etype > expected_aes_type: expected_aes_type = etype if etype in (kcrypto.Enctype.RC4,) and error_code != 0: @@ -2865,7 +2864,8 @@ class RawKerberosTest(TestCaseInTempDir): else: self.assertIsNone(etype_info2) if expect_etype_info: - self.assertIsNotNone(etype_info) + if self.strict_checking: + self.assertIsNotNone(etype_info) else: if self.strict_checking: self.assertIsNone(etype_info) -- 2.25.1 From e95adf228ae031ea9281d48466e0d93d8f34677f Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 12 Oct 2021 11:34:59 +1300 Subject: [PATCH 102/159] tests/krb5: Add environment variable to specify KDC FAST support BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett [abartlet@samba.org backportd from commit 238f52bad811688624e9fd4b1595266e2149094a because tests.py changed in more recent releases with new tests nearby] --- python/samba/tests/krb5/raw_testcase.py | 6 +++- source4/selftest/tests.py | 37 +++++++++++++++++-------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index bb9d414da5b..fbddb7f83b1 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -568,7 +568,11 @@ class RawKerberosTest(TestCaseInTempDir): # obtained. cls.creds_dict = {} - cls.kdc_fast_support = False + kdc_fast_support = samba.tests.env_get_var_value('FAST_SUPPORT', + allow_missing=True) + if kdc_fast_support is None: + kdc_fast_support = '0' + cls.kdc_fast_support = bool(int(kdc_fast_support)) def setUp(self): super().setUp() diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 92e5b8f5946..c34d25d81d5 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -809,39 +809,47 @@ planoldpythontestsuite("nt4_dc", "samba.tests.netbios", extra_args=['-U"$USERNAM planoldpythontestsuite("ad_dc:local", "samba.tests.gpo", extra_args=['-U"$USERNAME%$PASSWORD"']) planoldpythontestsuite("ad_dc:local", "samba.tests.dckeytab", extra_args=['-U"$USERNAME%$PASSWORD"']) +have_fast_support = int('SAMBA_USES_MITKDC' in config_hash) planoldpythontestsuite("none", "samba.tests.krb5.kcrypto") planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.simple_tests", - environ={'SERVICE_USERNAME':'$SERVER'}) + environ={'SERVICE_USERNAME':'$SERVER', + 'FAST_SUPPORT': have_fast_support}) planoldpythontestsuite("ad_dc_default:local", "samba.tests.krb5.s4u_tests", environ={'SERVICE_USERNAME':'srv_account', 'SERVICE_PASSWORD':'$PASSWORD', - 'FOR_USER':'$USERNAME'}) + 'FOR_USER':'$USERNAME', + 'FAST_SUPPORT': have_fast_support}) -planoldpythontestsuite("fl2008r2dc:local", "samba.tests.krb5.xrealm_tests") +planoldpythontestsuite("fl2008r2dc:local", "samba.tests.krb5.xrealm_tests", + environ={'FAST_SUPPORT': have_fast_support}) planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ccache", environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', - 'STRICT_CHECKING': '0' + 'STRICT_CHECKING': '0', + 'FAST_SUPPORT': have_fast_support }) planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ldap", environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', - 'STRICT_CHECKING': '0' + 'STRICT_CHECKING': '0', + 'FAST_SUPPORT': have_fast_support }) planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_rpc", environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', - 'STRICT_CHECKING': '0' + 'STRICT_CHECKING': '0', + 'FAST_SUPPORT': have_fast_support }) planoldpythontestsuite("ad_dc_smb1", "samba.tests.krb5.test_smb", environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', - 'STRICT_CHECKING': '0' + 'STRICT_CHECKING': '0', + 'FAST_SUPPORT': have_fast_support }) for env in ["ad_dc", smbv1_disabled_testenv]: @@ -1402,6 +1410,7 @@ for env in ["fl2008r2dc", "fl2003dc"]: 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', + 'FAST_SUPPORT': have_fast_support }) @@ -1422,22 +1431,26 @@ for env in ["rodc", "promoted_dc", "fl2000dc", "fl2008r2dc"]: planpythontestsuite("ad_dc", "samba.tests.krb5.as_canonicalization_tests", environ={ 'ADMIN_USERNAME': '$USERNAME', - 'ADMIN_PASSWORD': '$PASSWORD' + 'ADMIN_PASSWORD': '$PASSWORD', + 'FAST_SUPPORT': have_fast_support }) planpythontestsuite("ad_dc", "samba.tests.krb5.compatability_tests", environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', + 'FAST_SUPPORT': have_fast_support, }) -planpythontestsuite("ad_dc", "samba.tests.krb5.kdc_tests") +planpythontestsuite("ad_dc", "samba.tests.krb5.kdc_tests", + environ={'FAST_SUPPORT': have_fast_support}) planpythontestsuite( "ad_dc", "samba.tests.krb5.kdc_tgs_tests", environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', - 'STRICT_CHECKING': '0' + 'STRICT_CHECKING': '0', + 'FAST_SUPPORT': have_fast_support }) planpythontestsuite( "ad_dc", @@ -1446,6 +1459,7 @@ planpythontestsuite( 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', + 'FAST_SUPPORT': have_fast_support }) planpythontestsuite( "ad_dc", @@ -1453,7 +1467,8 @@ planpythontestsuite( environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', - 'STRICT_CHECKING': '0' + 'STRICT_CHECKING': '0', + 'FAST_SUPPORT': have_fast_support }) for env in [ -- 2.25.1 From b2e245b90f10c1cfc98e555dfc6234dc62078790 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 11 Oct 2021 14:45:45 +1300 Subject: [PATCH 103/159] tests/krb5: Check padata types when STRICT_CHECKING=0 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett [abartlet@samba.org backported from commit bd22dcd9cc4dfda827f892224eb2da4a16564176 to Samba 4.14 due to conflicts in knownfail as the test which crashes older MIT KDC versions is omitted] --- python/samba/tests/krb5/raw_testcase.py | 25 +++++++++++++++++++++---- selftest/knownfail_mit_kdc | 9 +++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index fbddb7f83b1..dbcff787f70 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1079,6 +1079,20 @@ class RawKerberosTest(TestCaseInTempDir): f"'{unexpected.namedValues[i]}' " f"unexpected in {v}") + def assertSequenceElementsEqual(self, expected, got, *, + require_strict=None): + if self.strict_checking: + self.assertEqual(expected, got) + else: + fail_msg = f'expected: {expected} got: {got}' + + if require_strict is not None: + fail_msg += f' (ignoring: {require_strict})' + expected = (x for x in expected if x not in require_strict) + got = (x for x in got if x not in require_strict) + + self.assertCountEqual(expected, got, fail_msg) + def get_KerberosTimeWithUsec(self, epoch=None, offset=None): if epoch is None: epoch = time.time() @@ -2714,10 +2728,13 @@ class RawKerberosTest(TestCaseInTempDir): expected_patypes += (PADATA_FX_FAST,) expected_patypes += (PADATA_FX_COOKIE,) - if self.strict_checking: - for i, patype in enumerate(expected_patypes): - self.assertElementEqual(rep_padata[i], 'padata-type', patype) - self.assertEqual(len(rep_padata), len(expected_patypes)) + got_patypes = tuple(pa['padata-type'] for pa in rep_padata) + self.assertSequenceElementsEqual(expected_patypes, got_patypes, + require_strict={PADATA_FX_COOKIE, + PADATA_FX_FAST, + PADATA_PAC_OPTIONS, + PADATA_PK_AS_REP_19, + PADATA_PK_AS_REQ}) etype_info2 = None etype_info = None diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc index f167c2bf856..4e0b20c5c80 100644 --- a/selftest/knownfail_mit_kdc +++ b/selftest/knownfail_mit_kdc @@ -290,6 +290,11 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ ^samba.tests.krb5.ms_kile_client_principal_lookup_tests.samba.tests.krb5.ms_kile_client_principal_lookup_tests.MS_Kile_Client_Principal_Lookup_Tests.test_nt_principal_step_4_b ^samba.tests.krb5.ms_kile_client_principal_lookup_tests.samba.tests.krb5.ms_kile_client_principal_lookup_tests.MS_Kile_Client_Principal_Lookup_Tests.test_nt_principal_step_4_c ^samba.tests.krb5.ms_kile_client_principal_lookup_tests.samba.tests.krb5.ms_kile_client_principal_lookup_tests.MS_Kile_Client_Principal_Lookup_Tests.test_nt_principal_step_6_c +# +# MIT currently fails some as_req_no_preauth tests. +# +^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 @@ -304,6 +309,9 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_authdata_fast_not_used.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_enc_timestamp.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_encrypted_challenge_clock_skew.ad_dc +^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_encrypted_challenge_no_fast.ad_dc +^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_encrypted_challenge_wrong_key.ad_dc +^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_encrypted_challenge_wrong_key_kdc.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_invalid_tgt.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_invalid_tgt_mach.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_tgs_armor.ad_dc @@ -318,5 +326,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_tgs_outer_no_sname.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_no_sname.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_tgs_no_sname.ad_dc +^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_simple_fast_no_etypes.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_simple_no_sname.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_simple_tgs_no_sname.ad_dc -- 2.25.1 From 27bb27a4460ffc3c3ac61d2c74d00e0e8258cb07 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 11 Oct 2021 14:48:03 +1300 Subject: [PATCH 104/159] tests/krb5: Check logon name in PAC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit e7c39cc44f2e16aecb01c0afc195911a474ef0b9) --- python/samba/tests/krb5/raw_testcase.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index dbcff787f70..b0cd2bfdf0f 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2552,6 +2552,12 @@ class RawKerberosTest(TestCaseInTempDir): self.assertEqual(expected_transited_services, transited_services) + elif pac_buffer.type == krb5pac.PAC_TYPE_LOGON_NAME: + expected_cname = kdc_exchange_dict['expected_cname'] + account_name = expected_cname['name-string'][0] + + self.assertEqual(account_name, pac_buffer.info.account_name) + def generic_check_kdc_error(self, kdc_exchange_dict, callback_dict, -- 2.25.1 From 6913a289a5433b526b48b3f6069d6f2beb50899a Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 11 Oct 2021 14:49:34 +1300 Subject: [PATCH 105/159] tests/krb5: Simplify padata checking BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit cf3ca6ac4567d7c7954ea4ecc8cc9dd5effcc094) --- python/samba/tests/krb5/raw_testcase.py | 169 ++++++------------------ 1 file changed, 41 insertions(+), 128 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index b0cd2bfdf0f..b51ad499192 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2742,73 +2742,32 @@ class RawKerberosTest(TestCaseInTempDir): PADATA_PK_AS_REP_19, PADATA_PK_AS_REQ}) - etype_info2 = None - etype_info = None - enc_timestamp = None - enc_challenge = None - pk_as_req = None - pk_as_rep19 = None - fast_cookie = None - fast_error = None - fx_fast = None - pac_options = None - for pa in rep_padata: - patype = self.getElementValue(pa, 'padata-type') - pavalue = self.getElementValue(pa, 'padata-value') - if patype == PADATA_ETYPE_INFO2: - self.assertIsNone(etype_info2) - etype_info2 = self.der_decode(pavalue, - asn1Spec=krb5_asn1.ETYPE_INFO2()) - continue - if patype == PADATA_ETYPE_INFO: - self.assertIsNone(etype_info) - etype_info = self.der_decode(pavalue, - asn1Spec=krb5_asn1.ETYPE_INFO()) - continue - if patype == PADATA_ENC_TIMESTAMP: - self.assertIsNone(enc_timestamp) - enc_timestamp = pavalue - self.assertEqual(len(enc_timestamp), 0) - continue - if patype == PADATA_ENCRYPTED_CHALLENGE: - self.assertIsNone(enc_challenge) - enc_challenge = pavalue - continue - if patype == PADATA_PK_AS_REQ: - self.assertIsNone(pk_as_req) - pk_as_req = pavalue - self.assertEqual(len(pk_as_req), 0) - continue - if patype == PADATA_PK_AS_REP_19: - self.assertIsNone(pk_as_rep19) - pk_as_rep19 = pavalue - self.assertEqual(len(pk_as_rep19), 0) - continue - if patype == PADATA_FX_COOKIE: - self.assertIsNone(fast_cookie) - fast_cookie = pavalue - self.assertIsNotNone(fast_cookie) - continue - if patype == PADATA_FX_ERROR: - self.assertIsNone(fast_error) - fast_error = pavalue - self.assertIsNotNone(fast_error) - continue - if patype == PADATA_FX_FAST: - self.assertIsNone(fx_fast) - fx_fast = pavalue - self.assertEqual(len(fx_fast), 0) - continue - if patype == PADATA_PAC_OPTIONS: - self.assertIsNone(pac_options) - pac_options = self.der_decode( - pavalue, - asn1Spec=krb5_asn1.PA_PAC_OPTIONS()) - continue + if not expected_patypes: + return None + + pa_dict = self.get_pa_dict(rep_padata) + + enc_timestamp = pa_dict.get(PADATA_ENC_TIMESTAMP) + if enc_timestamp is not None: + self.assertEqual(len(enc_timestamp), 0) + + pk_as_req = pa_dict.get(PADATA_PK_AS_REQ) + if pk_as_req is not None: + self.assertEqual(len(pk_as_req), 0) + + pk_as_rep19 = pa_dict.get(PADATA_PK_AS_REP_19) + if pk_as_rep19 is not None: + self.assertEqual(len(pk_as_rep19), 0) + fx_fast = pa_dict.get(PADATA_FX_FAST) + if fx_fast is not None: + self.assertEqual(len(fx_fast), 0) + + fast_cookie = pa_dict.get(PADATA_FX_COOKIE) if fast_cookie is not None: kdc_exchange_dict['fast_cookie'] = fast_cookie + fast_error = pa_dict.get(PADATA_FX_ERROR) if fast_error is not None: fast_error = self.der_decode(fast_error, asn1Spec=krb5_asn1.KRB_ERROR()) @@ -2817,9 +2776,14 @@ class RawKerberosTest(TestCaseInTempDir): fast_error, inner=True) + pac_options = pa_dict.get(PADATA_PAC_OPTIONS) if pac_options is not None: + pac_options = self.der_decode( + pac_options, + asn1Spec=krb5_asn1.PA_PAC_OPTIONS()) self.assertElementEqual(pac_options, 'options', sent_pac_options) + enc_challenge = pa_dict.get(PADATA_ENCRYPTED_CHALLENGE) if enc_challenge is not None: if not sent_enc_challenge: self.assertEqual(len(enc_challenge), 0) @@ -2862,52 +2826,21 @@ class RawKerberosTest(TestCaseInTempDir): self.assertLess(current_time - 300, rep_time) self.assertLess(rep_time, current_time + 300) - if all(etype not in client_as_etypes or etype not in proposed_etypes - for etype in (kcrypto.Enctype.AES256, - kcrypto.Enctype.AES128, - kcrypto.Enctype.RC4)): - self.assertIsNone(etype_info2) - self.assertIsNone(etype_info) - if rep_msg_type == KRB_AS_REP: - if self.strict_checking: - if sent_fast: - self.assertIsNotNone(enc_challenge) - self.assertIsNone(enc_timestamp) - else: - self.assertIsNotNone(enc_timestamp) - self.assertIsNone(enc_challenge) - self.assertIsNotNone(pk_as_req) - self.assertIsNotNone(pk_as_rep19) - else: - self.assertIsNone(enc_timestamp) - self.assertIsNone(enc_challenge) - self.assertIsNone(pk_as_req) - self.assertIsNone(pk_as_rep19) - return None - - if error_code != KDC_ERR_GENERIC: - if self.strict_checking: - self.assertIsNotNone(etype_info2) - else: - self.assertIsNone(etype_info2) - if expect_etype_info: - if self.strict_checking: - self.assertIsNotNone(etype_info) - else: - if self.strict_checking: - self.assertIsNone(etype_info) - if unexpect_etype_info: - self.assertIsNone(etype_info) - - if error_code != KDC_ERR_GENERIC and self.strict_checking: + etype_info2 = pa_dict.get(PADATA_ETYPE_INFO2) + if etype_info2 is not None: + etype_info2 = self.der_decode(etype_info2, + asn1Spec=krb5_asn1.ETYPE_INFO2()) self.assertGreaterEqual(len(etype_info2), 1) - self.assertEqual(len(etype_info2), len(expect_etype_info2)) + if self.strict_checking: + self.assertEqual(len(etype_info2), len(expect_etype_info2)) for i in range(0, len(etype_info2)): e = self.getElementValue(etype_info2[i], 'etype') - self.assertEqual(e, expect_etype_info2[i]) + if self.strict_checking: + self.assertEqual(e, expect_etype_info2[i]) salt = self.getElementValue(etype_info2[i], 'salt') if e == kcrypto.Enctype.RC4: - self.assertIsNone(salt) + if self.strict_checking: + self.assertIsNone(salt) else: self.assertIsNotNone(salt) expected_salt = kdc_exchange_dict['expected_salt'] @@ -2916,7 +2849,11 @@ class RawKerberosTest(TestCaseInTempDir): s2kparams = self.getElementValue(etype_info2[i], 's2kparams') if self.strict_checking: self.assertIsNone(s2kparams) + + etype_info = pa_dict.get(PADATA_ETYPE_INFO) if etype_info is not None: + etype_info = self.der_decode(etype_info, + asn1Spec=krb5_asn1.ETYPE_INFO()) self.assertEqual(len(etype_info), 1) e = self.getElementValue(etype_info[0], 'etype') self.assertEqual(e, kcrypto.Enctype.RC4) @@ -2926,30 +2863,6 @@ class RawKerberosTest(TestCaseInTempDir): self.assertIsNotNone(salt) self.assertEqual(len(salt), 0) - if error_code not in (KDC_ERR_PREAUTH_FAILED, - KDC_ERR_GENERIC): - if sent_fast: - self.assertIsNotNone(enc_challenge) - if self.strict_checking: - self.assertIsNone(enc_timestamp) - else: - self.assertIsNotNone(enc_timestamp) - if self.strict_checking: - self.assertIsNone(enc_challenge) - if not sent_enc_challenge: - if self.strict_checking: - self.assertIsNotNone(pk_as_req) - self.assertIsNotNone(pk_as_rep19) - else: - self.assertIsNone(pk_as_req) - self.assertIsNone(pk_as_rep19) - else: - if self.strict_checking: - self.assertIsNone(enc_timestamp) - self.assertIsNone(enc_challenge) - self.assertIsNone(pk_as_req) - self.assertIsNone(pk_as_rep19) - return etype_info2 def generate_simple_fast(self, -- 2.25.1 From beb9319d983f0ae0ba2875b308c4fa977a1ab86b Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 8 Oct 2021 11:48:41 +1300 Subject: [PATCH 106/159] tests/krb5: Disable debugging output for tests This reduces the time spent running the tests in a testenv. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit dfd613661eec4b81e162f2d86a8fa9266c2fdc03) --- python/samba/tests/krb5/as_canonicalization_tests.py | 4 ++-- python/samba/tests/krb5/as_req_tests.py | 4 ++-- python/samba/tests/krb5/compatability_tests.py | 4 ++-- python/samba/tests/krb5/kdc_tests.py | 4 ++-- python/samba/tests/krb5/kdc_tgs_tests.py | 4 ++-- python/samba/tests/krb5/s4u_tests.py | 4 ++-- python/samba/tests/krb5/simple_tests.py | 4 ++-- python/samba/tests/krb5/test_ccache.py | 4 ++-- python/samba/tests/krb5/test_ldap.py | 4 ++-- python/samba/tests/krb5/test_rpc.py | 4 ++-- python/samba/tests/krb5/test_smb.py | 4 ++-- python/samba/tests/krb5/xrealm_tests.py | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/python/samba/tests/krb5/as_canonicalization_tests.py b/python/samba/tests/krb5/as_canonicalization_tests.py index 29d8cf418f5..9538d0ae3cf 100755 --- a/python/samba/tests/krb5/as_canonicalization_tests.py +++ b/python/samba/tests/krb5/as_canonicalization_tests.py @@ -427,8 +427,8 @@ class KerberosASCanonicalizationTests(KDCBaseTest): if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() diff --git a/python/samba/tests/krb5/as_req_tests.py b/python/samba/tests/krb5/as_req_tests.py index 8d9b90fee69..7d7baaebf24 100755 --- a/python/samba/tests/krb5/as_req_tests.py +++ b/python/samba/tests/krb5/as_req_tests.py @@ -198,8 +198,8 @@ class AsReqKerberosTests(KDCBaseTest): self.assertIsNotNone(as_rep) if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() diff --git a/python/samba/tests/krb5/compatability_tests.py b/python/samba/tests/krb5/compatability_tests.py index 0da72796894..ed2dc565b6d 100755 --- a/python/samba/tests/krb5/compatability_tests.py +++ b/python/samba/tests/krb5/compatability_tests.py @@ -261,7 +261,7 @@ class SimpleKerberosTests(KDCBaseTest): if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() diff --git a/python/samba/tests/krb5/kdc_tests.py b/python/samba/tests/krb5/kdc_tests.py index 928f3c25c0f..b7c8566a1ec 100755 --- a/python/samba/tests/krb5/kdc_tests.py +++ b/python/samba/tests/krb5/kdc_tests.py @@ -222,7 +222,7 @@ class KdcTests(RawKerberosTest): if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py index 2b55ba8a376..3075cc6b0a9 100755 --- a/python/samba/tests/krb5/kdc_tgs_tests.py +++ b/python/samba/tests/krb5/kdc_tgs_tests.py @@ -211,7 +211,7 @@ class KdcTgsTests(KDCBaseTest): if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() diff --git a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py index 57575f0595d..bbddef4d6e0 100755 --- a/python/samba/tests/krb5/s4u_tests.py +++ b/python/samba/tests/krb5/s4u_tests.py @@ -197,7 +197,7 @@ class S4UKerberosTests(RawKerberosTest): if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() diff --git a/python/samba/tests/krb5/simple_tests.py b/python/samba/tests/krb5/simple_tests.py index 795d753b4f7..3cd3b17bb31 100755 --- a/python/samba/tests/krb5/simple_tests.py +++ b/python/samba/tests/krb5/simple_tests.py @@ -179,7 +179,7 @@ class SimpleKerberosTests(RawKerberosTest): if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() diff --git a/python/samba/tests/krb5/test_ccache.py b/python/samba/tests/krb5/test_ccache.py index feb7a7bd9be..c44ea02d504 100755 --- a/python/samba/tests/krb5/test_ccache.py +++ b/python/samba/tests/krb5/test_ccache.py @@ -129,7 +129,7 @@ class CcacheTests(KDCBaseTest): if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() diff --git a/python/samba/tests/krb5/test_ldap.py b/python/samba/tests/krb5/test_ldap.py index d304fb9d71e..95b2d24221a 100755 --- a/python/samba/tests/krb5/test_ldap.py +++ b/python/samba/tests/krb5/test_ldap.py @@ -90,7 +90,7 @@ class LdapTests(KDCBaseTest): if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() diff --git a/python/samba/tests/krb5/test_rpc.py b/python/samba/tests/krb5/test_rpc.py index 324b57f2847..40ac6df7a35 100755 --- a/python/samba/tests/krb5/test_rpc.py +++ b/python/samba/tests/krb5/test_rpc.py @@ -73,7 +73,7 @@ class RpcTests(KDCBaseTest): if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() diff --git a/python/samba/tests/krb5/test_smb.py b/python/samba/tests/krb5/test_smb.py index 45d4fe5e0c1..eebc9a9d4fe 100755 --- a/python/samba/tests/krb5/test_smb.py +++ b/python/samba/tests/krb5/test_smb.py @@ -104,7 +104,7 @@ class SmbTests(KDCBaseTest): if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() diff --git a/python/samba/tests/krb5/xrealm_tests.py b/python/samba/tests/krb5/xrealm_tests.py index 073cb755b46..73a6b3cf52d 100755 --- a/python/samba/tests/krb5/xrealm_tests.py +++ b/python/samba/tests/krb5/xrealm_tests.py @@ -181,7 +181,7 @@ class XrealmKerberosTests(RawKerberosTest): if __name__ == "__main__": - global_asn1_print = True - global_hexdump = True + global_asn1_print = False + global_hexdump = False import unittest unittest.main() -- 2.25.1 From 35a5a29c7ccca0ef9c3d4917240fb8003d1c38b0 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 5 Oct 2021 19:47:22 +1300 Subject: [PATCH 107/159] tests/krb5: Provide clearer assertion messages for test failures BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 5233f002000f196875af488b4f4d1df26fca90de) --- python/samba/tests/krb5/raw_testcase.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index b51ad499192..188a54451d3 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -1942,7 +1942,12 @@ class RawKerberosTest(TestCaseInTempDir): self.assertIsNone(check_error_fn) self.assertEqual(0, len(expected_error_mode)) self.assertIsNotNone(expected_msg_type) - self.assertEqual(msg_type, expected_msg_type) + if msg_type == KRB_ERROR: + error_code = self.getElementValue(rep, 'error-code') + fail_msg = f'Got unexpected error: {error_code}' + else: + fail_msg = f'Expected to fail with error: {expected_error_mode}' + self.assertEqual(msg_type, expected_msg_type, fail_msg) if msg_type == KRB_ERROR: return check_error_fn(kdc_exchange_dict, -- 2.25.1 From b8a1f5192d9726df85ab360073cfdca8086a89e1 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 5 Oct 2021 16:32:01 +1300 Subject: [PATCH 108/159] tests/krb5: Fix sha1 checksum type Previously, sha1 signatures were being designated as rsa-md5-des3 signatures. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit ebe729786806c69e95b26ffc410e887e203accb8) --- python/samba/tests/krb5/kcrypto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/kcrypto.py b/python/samba/tests/krb5/kcrypto.py index 4a4a12a66d4..4bf38d3c36b 100755 --- a/python/samba/tests/krb5/kcrypto.py +++ b/python/samba/tests/krb5/kcrypto.py @@ -81,8 +81,8 @@ class Cksumtype(object): MD4_DES = 3 MD5 = 7 MD5_DES = 8 - SHA1 = 9 SHA1_DES3 = 12 + SHA1 = 14 SHA1_AES128 = 15 SHA1_AES256 = 16 HMAC_MD5 = -138 -- 2.25.1 From 1ed3af883f6d7b93583104738a669e8f8a9a7192 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 13 Oct 2021 12:26:22 +1300 Subject: [PATCH 109/159] selftest/dbcheck: Fix up RODC one-way links Test accounts were replicated to the RODC and then deleted, causing state links to remain in the database. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 40e5db4aabcd32834ee524857b77d36921f6bdfe) --- testprogs/blackbox/dbcheck.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testprogs/blackbox/dbcheck.sh b/testprogs/blackbox/dbcheck.sh index caf89ca402b..e2ba987e2de 100755 --- a/testprogs/blackbox/dbcheck.sh +++ b/testprogs/blackbox/dbcheck.sh @@ -19,7 +19,7 @@ dbcheck() { # This list of attributes can be freely extended dbcheck_fix_one_way_links() { - $PYTHON $BINDIR/samba-tool dbcheck --quiet --fix --yes fix_all_old_dn_string_component_mismatch --attrs="lastKnownParent defaultObjectCategory fromServer rIDSetReferences" --cross-ncs $ARGS + $PYTHON $BINDIR/samba-tool dbcheck --quiet --fix --yes fix_all_old_dn_string_component_mismatch --attrs="lastKnownParent defaultObjectCategory fromServer rIDSetReferences msDS-RevealOnDemandGroup msDS-NeverRevealGroup" --cross-ncs $ARGS } # This list of attributes can be freely extended -- 2.25.1 From 202ba79036c22a96987da0635ba8b68cbf89c3e4 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 14 Oct 2021 16:58:15 +1300 Subject: [PATCH 110/159] tests/krb5: Add TKT_SIG_SUPPORT environment variable This lets us indicate that service tickets should be issued with ticket checksums in the PAC. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett [abartlet@samba.org backported from commit ae2c57fb0332f94ac44d0886c5edbed707ef52fe due to changes in other tests nearby in tests.py] --- python/samba/tests/krb5/raw_testcase.py | 6 ++++ source4/selftest/tests.py | 41 +++++++++++++++++-------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 188a54451d3..1f7c51c07a5 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -574,6 +574,12 @@ class RawKerberosTest(TestCaseInTempDir): kdc_fast_support = '0' cls.kdc_fast_support = bool(int(kdc_fast_support)) + tkt_sig_support = samba.tests.env_get_var_value('TKT_SIG_SUPPORT', + allow_missing=True) + if tkt_sig_support is None: + tkt_sig_support = '0' + cls.tkt_sig_support = bool(int(tkt_sig_support)) + def setUp(self): super().setUp() self.do_asn1_print = False diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index c34d25d81d5..f7b34baba8a 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -810,46 +810,54 @@ planoldpythontestsuite("ad_dc:local", "samba.tests.gpo", extra_args=['-U"$USERNA planoldpythontestsuite("ad_dc:local", "samba.tests.dckeytab", extra_args=['-U"$USERNAME%$PASSWORD"']) have_fast_support = int('SAMBA_USES_MITKDC' in config_hash) +tkt_sig_support = 0 planoldpythontestsuite("none", "samba.tests.krb5.kcrypto") planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.simple_tests", environ={'SERVICE_USERNAME':'$SERVER', - 'FAST_SUPPORT': have_fast_support}) + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support}) planoldpythontestsuite("ad_dc_default:local", "samba.tests.krb5.s4u_tests", environ={'SERVICE_USERNAME':'srv_account', 'SERVICE_PASSWORD':'$PASSWORD', 'FOR_USER':'$USERNAME', - 'FAST_SUPPORT': have_fast_support}) + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support}) planoldpythontestsuite("fl2008r2dc:local", "samba.tests.krb5.xrealm_tests", - environ={'FAST_SUPPORT': have_fast_support}) + environ={'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support}) planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ccache", environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', - 'FAST_SUPPORT': have_fast_support + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support }) planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ldap", environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', - 'FAST_SUPPORT': have_fast_support + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support }) planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_rpc", environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', - 'FAST_SUPPORT': have_fast_support + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support }) planoldpythontestsuite("ad_dc_smb1", "samba.tests.krb5.test_smb", environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', - 'FAST_SUPPORT': have_fast_support + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support }) for env in ["ad_dc", smbv1_disabled_testenv]: @@ -1410,7 +1418,8 @@ for env in ["fl2008r2dc", "fl2003dc"]: 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', - 'FAST_SUPPORT': have_fast_support + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support }) @@ -1432,7 +1441,8 @@ planpythontestsuite("ad_dc", "samba.tests.krb5.as_canonicalization_tests", environ={ 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', - 'FAST_SUPPORT': have_fast_support + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support }) planpythontestsuite("ad_dc", "samba.tests.krb5.compatability_tests", environ={ @@ -1440,9 +1450,11 @@ planpythontestsuite("ad_dc", "samba.tests.krb5.compatability_tests", 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support }) planpythontestsuite("ad_dc", "samba.tests.krb5.kdc_tests", - environ={'FAST_SUPPORT': have_fast_support}) + environ={'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support}) planpythontestsuite( "ad_dc", "samba.tests.krb5.kdc_tgs_tests", @@ -1450,7 +1462,8 @@ planpythontestsuite( 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', - 'FAST_SUPPORT': have_fast_support + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support }) planpythontestsuite( "ad_dc", @@ -1459,7 +1472,8 @@ planpythontestsuite( 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', - 'FAST_SUPPORT': have_fast_support + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support }) planpythontestsuite( "ad_dc", @@ -1468,7 +1482,8 @@ planpythontestsuite( 'ADMIN_USERNAME': '$USERNAME', 'ADMIN_PASSWORD': '$PASSWORD', 'STRICT_CHECKING': '0', - 'FAST_SUPPORT': have_fast_support + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support }) for env in [ -- 2.25.1 From 1ccfb869ed22f8fc32631f4fea505723225863c8 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Tue, 5 Oct 2021 15:39:11 +1300 Subject: [PATCH 111/159] tests/krb5: Require ticket checksums if decryption key is available We perform this check conditionally, because MIT doesn't currently add ticket checksums. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit bf63221722903665e7b20991021fb5cdf4e4327e) --- python/samba/tests/krb5/raw_testcase.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 1f7c51c07a5..2e289c90ce7 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2493,15 +2493,14 @@ class RawKerberosTest(TestCaseInTempDir): ticket_private=ticket_private, encpart_private=encpart_private) - # TODO: This parameter should be removed when all service tickets are - # issued with ticket checksums. expect_ticket_checksum = kdc_exchange_dict['expect_ticket_checksum'] if expect_ticket_checksum: self.assertIsNotNone(ticket_decryption_key) if ticket_decryption_key is not None: self.verify_ticket(ticket_creds, krbtgt_key, expect_pac=expect_pac, - expect_ticket_checksum=expect_ticket_checksum) + expect_ticket_checksum=expect_ticket_checksum + or self.tkt_sig_support) kdc_exchange_dict['rep_ticket_creds'] = ticket_creds -- 2.25.1 From 4601d2b117f41a3de47c62fb9f98d7c5f252d357 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 6 Oct 2021 16:35:47 +1300 Subject: [PATCH 112/159] tests/krb5: Verify tickets obtained with get_service_ticket() We only require the ticket checksum with Heimdal, because MIT currently doesn't add it. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit d86eee2fd0fb72e52d878ceba0c476ca58abe6cf) --- python/samba/tests/krb5/kdc_base_test.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 93951586cc7..8a5e12bbed4 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1287,6 +1287,14 @@ class KDCBaseTest(RawKerberosTest): sname=sname, decryption_key=target_key) + if to_rodc: + krbtgt_creds = self.get_rodc_krbtgt_creds() + else: + krbtgt_creds = self.get_krbtgt_creds() + krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) + self.verify_ticket(service_ticket_creds, krbtgt_key, + expect_ticket_checksum=self.tkt_sig_support) + self.tkt_cache[cache_key] = service_ticket_creds return service_ticket_creds -- 2.25.1 From 100cb097015f2efb1645d69de8a4f3826de1ce71 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Thu, 30 Sep 2021 15:03:04 +1300 Subject: [PATCH 113/159] tests/krb5: Add constrained delegation tests BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 56ccdba54e0c7cf3409d8430ea1012e5d3d9b092) --- python/samba/tests/krb5/rfc4120_constants.py | 5 + python/samba/tests/krb5/rodc_tests.py | 73 ++ python/samba/tests/krb5/s4u_tests.py | 958 ++++++++++++++++++- python/samba/tests/usage.py | 1 + selftest/knownfail_heimdal_kdc | 29 + source4/selftest/tests.py | 11 +- 6 files changed, 1070 insertions(+), 7 deletions(-) create mode 100755 python/samba/tests/krb5/rodc_tests.py diff --git a/python/samba/tests/krb5/rfc4120_constants.py b/python/samba/tests/krb5/rfc4120_constants.py index 76f2b75d94e..39bb2db8e32 100644 --- a/python/samba/tests/krb5/rfc4120_constants.py +++ b/python/samba/tests/krb5/rfc4120_constants.py @@ -69,12 +69,17 @@ PADATA_SUPPORTED_ETYPES = int( KDC_ERR_C_PRINCIPAL_UNKNOWN = 6 KDC_ERR_S_PRINCIPAL_UNKNOWN = 7 KDC_ERR_POLICY = 12 +KDC_ERR_BADOPTION = 13 KDC_ERR_ETYPE_NOSUPP = 14 +KDC_ERR_SUMTYPE_NOSUPP = 15 KDC_ERR_PREAUTH_FAILED = 24 KDC_ERR_PREAUTH_REQUIRED = 25 +KDC_ERR_BAD_INTEGRITY = 31 KDC_ERR_NOT_US = 35 KDC_ERR_BADMATCH = 36 KDC_ERR_SKEW = 37 +KDC_ERR_MODIFIED = 41 +KDC_ERR_INAPP_CKSUM = 50 KDC_ERR_GENERIC = 60 KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS = 93 diff --git a/python/samba/tests/krb5/rodc_tests.py b/python/samba/tests/krb5/rodc_tests.py new file mode 100755 index 00000000000..4579f9eb552 --- /dev/null +++ b/python/samba/tests/krb5/rodc_tests.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# Unix SMB/CIFS implementation. +# Copyright (C) Stefan Metzmacher 2020 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import sys +import os + +from samba.tests.krb5.kdc_base_test import KDCBaseTest + +sys.path.insert(0, "bin/python") +os.environ["PYTHONUNBUFFERED"] = "1" + +global_asn1_print = False +global_hexdump = False + + +class RodcKerberosTests(KDCBaseTest): + + def setUp(self): + super().setUp() + self.do_asn1_print = global_asn1_print + self.do_hexdump = global_hexdump + + # Ensure that an RODC correctly issues tickets signed with its krbtgt key + # and including the RODCIdentifier. + def test_rodc_ticket_signature(self): + user_creds = self.get_cached_creds( + machine_account=False, + opts={ + 'revealed_to_rodc': True + }) + target_creds = self.get_cached_creds( + machine_account=True, + opts={ + 'revealed_to_rodc': True + }) + + krbtgt_creds = self.get_rodc_krbtgt_creds() + rodc_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) + + # Get a TGT from the RODC. + tgt = self.get_tgt(user_creds, to_rodc=True) + + # Ensure the PAC contains the expected checksums. + self.verify_ticket(tgt, rodc_key) + + # Get a service ticket from the RODC. + service_ticket = self.get_service_ticket(tgt, target_creds, + to_rodc=True) + + # Ensure the PAC contains the expected checksums. + self.verify_ticket(service_ticket, rodc_key) + + +if __name__ == "__main__": + global_asn1_print = False + global_hexdump = False + import unittest + unittest.main() diff --git a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py index bbddef4d6e0..9a25256081a 100755 --- a/python/samba/tests/krb5/s4u_tests.py +++ b/python/samba/tests/krb5/s4u_tests.py @@ -18,17 +18,34 @@ import sys import os +import functools sys.path.insert(0, "bin/python") os.environ["PYTHONUNBUFFERED"] = "1" +from samba import ntstatus +from samba.dcerpc import krb5pac, lsa + from samba.tests import env_get_var_value -from samba.tests.krb5.kcrypto import Cksumtype -from samba.tests.krb5.raw_testcase import RawKerberosTest +from samba.tests.krb5.kcrypto import Cksumtype, Enctype +from samba.tests.krb5.kdc_base_test import KDCBaseTest +from samba.tests.krb5.raw_testcase import ( + RodcPacEncryptionKey, + ZeroedChecksumKey +) from samba.tests.krb5.rfc4120_constants import ( + AES256_CTS_HMAC_SHA1_96, + ARCFOUR_HMAC_MD5, + KDC_ERR_BADOPTION, + KDC_ERR_BAD_INTEGRITY, + KDC_ERR_GENERIC, + KDC_ERR_INAPP_CKSUM, + KDC_ERR_MODIFIED, + KDC_ERR_SUMTYPE_NOSUPP, KU_PA_ENC_TIMESTAMP, KU_AS_REP_ENC_PART, KU_TGS_REP_ENC_PART_SUB_KEY, + NT_PRINCIPAL ) import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1 @@ -36,7 +53,7 @@ global_asn1_print = False global_hexdump = False -class S4UKerberosTests(RawKerberosTest): +class S4UKerberosTests(KDCBaseTest): def setUp(self): super(S4UKerberosTests, self).setUp() @@ -119,8 +136,14 @@ class S4UKerberosTests(RawKerberosTest): self.assertEqual(msg_type, 11) enc_part2 = key.decrypt(KU_AS_REP_ENC_PART, rep['enc-part']['cipher']) - enc_part2 = self.der_decode( - enc_part2, asn1Spec=krb5_asn1.EncASRepPart()) + # MIT KDC encodes both EncASRepPart and EncTGSRepPart with + # application tag 26 + try: + enc_part2 = self.der_decode( + enc_part2, asn1Spec=krb5_asn1.EncASRepPart()) + except Exception: + enc_part2 = self.der_decode( + enc_part2, asn1Spec=krb5_asn1.EncTGSRepPart()) # S4U2Self Request sname = cname @@ -195,6 +218,931 @@ class S4UKerberosTests(RawKerberosTest): msg_type = self._test_s4u2self(pa_s4u2self_ctype=Cksumtype.CRC32) self.assertEqual(msg_type, 30) + def _run_s4u2self_test(self, kdc_dict): + client_opts = kdc_dict.pop('client_opts', None) + client_creds = self.get_cached_creds(machine_account=False, + opts=client_opts) + + service_opts = kdc_dict.pop('service_opts', None) + service_creds = self.get_cached_creds(machine_account=True, + opts=service_opts) + + service_tgt = self.get_tgt(service_creds) + modify_service_tgt_fn = kdc_dict.pop('modify_service_tgt_fn', None) + if modify_service_tgt_fn is not None: + service_tgt = modify_service_tgt_fn(service_tgt) + + client_name = client_creds.get_username() + client_cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, + names=[client_name]) + + service_name = service_creds.get_username()[:-1] + service_sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, + names=['host', service_name]) + + realm = client_creds.get_realm() + + expected_flags = kdc_dict.pop('expected_flags', None) + if expected_flags is not None: + expected_flags = krb5_asn1.TicketFlags(expected_flags) + + unexpected_flags = kdc_dict.pop('unexpected_flags', None) + if unexpected_flags is not None: + unexpected_flags = krb5_asn1.TicketFlags(unexpected_flags) + + kdc_options = kdc_dict.pop('kdc_options', '0') + kdc_options = krb5_asn1.KDCOptions(kdc_options) + + service_decryption_key = self.TicketDecryptionKey_from_creds( + service_creds) + + authenticator_subkey = self.RandomKey(Enctype.AES256) + + etypes = kdc_dict.pop('etypes', (AES256_CTS_HMAC_SHA1_96, + ARCFOUR_HMAC_MD5)) + + def generate_s4u2self_padata(_kdc_exchange_dict, + _callback_dict, + req_body): + pa_s4u = self.PA_S4U2Self_create( + name=client_cname, + realm=realm, + tgt_session_key=service_tgt.session_key, + ctype=None) + + return [pa_s4u], req_body + + kdc_exchange_dict = self.tgs_exchange_dict( + expected_crealm=realm, + expected_cname=client_cname, + expected_srealm=realm, + expected_sname=service_sname, + expected_flags=expected_flags, + unexpected_flags=unexpected_flags, + ticket_decryption_key=service_decryption_key, + expect_ticket_checksum=True, + generate_padata_fn=generate_s4u2self_padata, + check_rep_fn=self.generic_check_kdc_rep, + check_kdc_private_fn=self.generic_check_kdc_private, + expected_error_mode=0, + tgt=service_tgt, + authenticator_subkey=authenticator_subkey, + kdc_options=str(kdc_options), + expect_claims=False) + + self._generic_kdc_exchange(kdc_exchange_dict, + cname=None, + realm=realm, + sname=service_sname, + etypes=etypes) + + # Ensure we used all the parameters given to us. + self.assertEqual({}, kdc_dict) + + # Test performing an S4U2Self operation with a forwardable ticket. The + # resulting ticket should have the 'forwardable' flag set. + def test_s4u2self_forwardable(self): + self._run_s4u2self_test( + { + 'client_opts': { + 'not_delegated': False + }, + 'kdc_options': 'forwardable', + 'modify_service_tgt_fn': functools.partial( + self.set_ticket_forwardable, flag=True), + 'expected_flags': 'forwardable' + }) + + # Test performing an S4U2Self operation without requesting a forwardable + # ticket. The resulting ticket should not have the 'forwardable' flag set. + def test_s4u2self_without_forwardable(self): + self._run_s4u2self_test( + { + 'client_opts': { + 'not_delegated': False + }, + 'modify_service_tgt_fn': functools.partial( + self.set_ticket_forwardable, flag=True), + 'unexpected_flags': 'forwardable' + }) + + # Do an S4U2Self with a non-forwardable TGT. The 'forwardable' flag should + # not be set on the ticket. + def test_s4u2self_not_forwardable(self): + self._run_s4u2self_test( + { + 'client_opts': { + 'not_delegated': False + }, + 'kdc_options': 'forwardable', + 'modify_service_tgt_fn': functools.partial( + self.set_ticket_forwardable, flag=False), + 'unexpected_flags': 'forwardable' + }) + + # Do an S4U2Self with the not_delegated flag set on the client. The + # 'forwardable' flag should not be set on the ticket. + def test_s4u2self_client_not_delegated(self): + self._run_s4u2self_test( + { + 'client_opts': { + 'not_delegated': True + }, + 'kdc_options': 'forwardable', + 'modify_service_tgt_fn': functools.partial( + self.set_ticket_forwardable, flag=True), + 'unexpected_flags': 'forwardable' + }) + + # Do an S4U2Self with a service not trusted to authenticate for delegation, + # but having an empty msDS-AllowedToDelegateTo attribute. The 'forwardable' + # flag should be set on the ticket. + def test_s4u2self_not_trusted_empty_allowed(self): + self._run_s4u2self_test( + { + 'client_opts': { + 'not_delegated': False + }, + 'service_opts': { + 'trusted_to_auth_for_delegation': False, + 'delegation_to_spn': () + }, + 'kdc_options': 'forwardable', + 'modify_service_tgt_fn': functools.partial( + self.set_ticket_forwardable, flag=True), + 'expected_flags': 'forwardable' + }) + + # Do an S4U2Self with a service not trusted to authenticate for delegation + # and having a non-empty msDS-AllowedToDelegateTo attribute. The + # 'forwardable' flag should not be set on the ticket. + def test_s4u2self_not_trusted_nonempty_allowed(self): + self._run_s4u2self_test( + { + 'client_opts': { + 'not_delegated': False + }, + 'service_opts': { + 'trusted_to_auth_for_delegation': False, + 'delegation_to_spn': ('test',) + }, + 'kdc_options': 'forwardable', + 'modify_service_tgt_fn': functools.partial( + self.set_ticket_forwardable, flag=True), + 'unexpected_flags': 'forwardable' + }) + + # Do an S4U2Self with a service trusted to authenticate for delegation and + # having an empty msDS-AllowedToDelegateTo attribute. The 'forwardable' + # flag should be set on the ticket. + def test_s4u2self_trusted_empty_allowed(self): + self._run_s4u2self_test( + { + 'client_opts': { + 'not_delegated': False + }, + 'service_opts': { + 'trusted_to_auth_for_delegation': True, + 'delegation_to_spn': () + }, + 'kdc_options': 'forwardable', + 'modify_service_tgt_fn': functools.partial( + self.set_ticket_forwardable, flag=True), + 'expected_flags': 'forwardable' + }) + + # Do an S4U2Self with a service trusted to authenticate for delegation and + # having a non-empty msDS-AllowedToDelegateTo attribute. The 'forwardable' + # flag should be set on the ticket. + def test_s4u2self_trusted_nonempty_allowed(self): + self._run_s4u2self_test( + { + 'client_opts': { + 'not_delegated': False + }, + 'service_opts': { + 'trusted_to_auth_for_delegation': True, + 'delegation_to_spn': ('test',) + }, + 'kdc_options': 'forwardable', + 'modify_service_tgt_fn': functools.partial( + self.set_ticket_forwardable, flag=True), + 'expected_flags': 'forwardable' + }) + + def _run_delegation_test(self, kdc_dict): + client_opts = kdc_dict.pop('client_opts', None) + client_creds = self.get_cached_creds(machine_account=False, + opts=client_opts) + + service1_opts = kdc_dict.pop('service1_opts', {}) + service2_opts = kdc_dict.pop('service2_opts', {}) + + allow_delegation = kdc_dict.pop('allow_delegation', False) + allow_rbcd = kdc_dict.pop('allow_rbcd', False) + self.assertFalse(allow_delegation and allow_rbcd) + + if allow_rbcd: + service1_creds = self.get_cached_creds(machine_account=True, + opts=service1_opts) + + self.assertNotIn('delegation_from_dn', service2_opts) + service2_opts['delegation_from_dn'] = str(service1_creds.get_dn()) + + service2_creds = self.get_cached_creds(machine_account=True, + opts=service2_opts) + else: + service2_creds = self.get_cached_creds(machine_account=True, + opts=service2_opts) + + if allow_delegation: + self.assertNotIn('delegation_to_spn', service1_opts) + service1_opts['delegation_to_spn'] = service2_creds.get_spn() + + service1_creds = self.get_cached_creds(machine_account=True, + opts=service1_opts) + + client_tkt_options = kdc_dict.pop('client_tkt_options', 'forwardable') + expected_flags = krb5_asn1.TicketFlags(client_tkt_options) + + client_tgt = self.get_tgt(client_creds, + kdc_options=client_tkt_options, + expected_flags=expected_flags) + client_service_tkt = self.get_service_ticket( + client_tgt, + service1_creds, + kdc_options=client_tkt_options, + expected_flags=expected_flags) + + service1_tgt = self.get_tgt(service1_creds) + + modify_client_tkt_fn = kdc_dict.pop('modify_client_tkt_fn', None) + if modify_client_tkt_fn is not None: + client_service_tkt = modify_client_tkt_fn(client_service_tkt) + + additional_tickets = [client_service_tkt.ticket] + + modify_service_tgt_fn = kdc_dict.pop('modify_service_tgt_fn', None) + if modify_service_tgt_fn is not None: + service1_tgt = modify_service_tgt_fn(service1_tgt) + + kdc_options = kdc_dict.pop('kdc_options', None) + if kdc_options is None: + kdc_options = str(krb5_asn1.KDCOptions('cname-in-addl-tkt')) + + client_username = client_creds.get_username() + client_realm = client_creds.get_realm() + client_cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, + names=[client_username]) + + service1_name = service1_creds.get_username()[:-1] + service1_realm = service1_creds.get_realm() + + service2_name = service2_creds.get_username()[:-1] + service2_realm = service2_creds.get_realm() + service2_service = 'host' + service2_sname = self.PrincipalName_create( + name_type=NT_PRINCIPAL, names=[service2_service, + service2_name]) + service2_decryption_key = self.TicketDecryptionKey_from_creds( + service2_creds) + service2_etypes = service2_creds.tgs_supported_enctypes + + expected_error_mode = kdc_dict.pop('expected_error_mode') + expected_status = kdc_dict.pop('expected_status', None) + if expected_error_mode: + check_error_fn = self.generic_check_kdc_error + check_rep_fn = None + else: + check_error_fn = None + check_rep_fn = self.generic_check_kdc_rep + + self.assertIsNone(expected_status) + + expect_edata = kdc_dict.pop('expect_edata', None) + if expect_edata is not None: + self.assertTrue(expected_error_mode) + + pac_options = kdc_dict.pop('pac_options', None) + + authenticator_subkey = self.RandomKey(Enctype.AES256) + + etypes = kdc_dict.pop('etypes', (AES256_CTS_HMAC_SHA1_96, + ARCFOUR_HMAC_MD5)) + + expected_proxy_target = service2_creds.get_spn() + + expected_transited_services = kdc_dict.pop( + 'expected_transited_services', []) + + transited_service = f'host/{service1_name}@{service1_realm}' + expected_transited_services.append(transited_service) + + kdc_exchange_dict = self.tgs_exchange_dict( + expected_crealm=client_realm, + expected_cname=client_cname, + expected_srealm=service2_realm, + expected_sname=service2_sname, + expected_supported_etypes=service2_etypes, + ticket_decryption_key=service2_decryption_key, + check_error_fn=check_error_fn, + check_rep_fn=check_rep_fn, + check_kdc_private_fn=self.generic_check_kdc_private, + expected_error_mode=expected_error_mode, + expected_status=expected_status, + callback_dict={}, + tgt=service1_tgt, + authenticator_subkey=authenticator_subkey, + kdc_options=kdc_options, + pac_options=pac_options, + expect_edata=expect_edata, + expected_proxy_target=expected_proxy_target, + expected_transited_services=expected_transited_services) + + self._generic_kdc_exchange(kdc_exchange_dict, + cname=None, + realm=service2_realm, + sname=service2_sname, + etypes=etypes, + additional_tickets=additional_tickets) + + # Ensure we used all the parameters given to us. + self.assertEqual({}, kdc_dict) + + def test_constrained_delegation(self): + # Test constrained delegation. + self._run_delegation_test( + { + 'expected_error_mode': 0, + 'allow_delegation': True + }) + + def test_constrained_delegation_existing_delegation_info(self): + # Test constrained delegation with an existing S4U_DELEGATION_INFO + # structure in the PAC. + + services = ['service1', 'service2', 'service3'] + + self._run_delegation_test( + { + 'expected_error_mode': 0, + 'allow_delegation': True, + 'modify_client_tkt_fn': functools.partial( + self.add_delegation_info, services=services), + 'expected_transited_services': services + }) + + def test_constrained_delegation_not_allowed(self): + # Test constrained delegation when the delegating service does not + # allow it. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_BADOPTION, + 'expected_status': ntstatus.NT_STATUS_NOT_SUPPORTED, + 'allow_delegation': False + }) + + def test_constrained_delegation_no_client_pac(self): + # Test constrained delegation when the client service ticket does not + # contain a PAC. + self._run_delegation_test( + { + 'expected_error_mode': (KDC_ERR_BADOPTION, + KDC_ERR_MODIFIED), + 'allow_delegation': True, + 'modify_client_tkt_fn': self.remove_ticket_pac, + 'expect_edata': False + }) + + def test_constrained_delegation_no_service_pac(self): + # Test constrained delegation when the service TGT does not contain a + # PAC. + self._run_delegation_test( + { + 'expected_error_mode': 0, + 'allow_delegation': True, + 'modify_service_tgt_fn': self.remove_ticket_pac + }) + + def test_constrained_delegation_non_forwardable(self): + # Test constrained delegation with a non-forwardable ticket. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_BADOPTION, + 'expected_status': ntstatus.NT_STATUS_ACCOUNT_RESTRICTION, + 'allow_delegation': True, + 'modify_client_tkt_fn': functools.partial( + self.set_ticket_forwardable, flag=False) + }) + + def test_constrained_delegation_pac_options_rbcd(self): + # Test constrained delegation, but with the RBCD bit set in the PAC + # options. + self._run_delegation_test( + { + 'expected_error_mode': 0, + 'pac_options': '0001', # supports RBCD + 'allow_delegation': True + }) + + def test_rbcd_existing_delegation_info(self): + # Test constrained delegation with an existing S4U_DELEGATION_INFO + # structure in the PAC. + + services = ['service1', 'service2', 'service3'] + + self._run_delegation_test( + { + 'expected_error_mode': 0, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_client_tkt_fn': functools.partial( + self.add_delegation_info, services=services), + 'expected_transited_services': services + }) + + def test_rbcd_not_allowed(self): + # Test resource-based constrained delegation when the target service + # does not allow it. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_BADOPTION, + 'expected_status': ntstatus.NT_STATUS_NOT_FOUND, + 'allow_rbcd': False, + 'pac_options': '0001' # supports RBCD + }) + + def test_rbcd_no_client_pac_a(self): + # Test constrained delegation when the client service ticket does not + # contain a PAC, and an empty msDS-AllowedToDelegateTo attribute. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_MODIFIED, + 'expected_status': ntstatus.NT_STATUS_NOT_SUPPORTED, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_client_tkt_fn': self.remove_ticket_pac + }) + + def test_rbcd_no_client_pac_b(self): + # Test constrained delegation when the client service ticket does not + # contain a PAC, and a non-empty msDS-AllowedToDelegateTo attribute. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_MODIFIED, + 'expected_status': ntstatus.NT_STATUS_NO_MATCH, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_client_tkt_fn': self.remove_ticket_pac, + 'service1_opts': { + 'delegation_to_spn': ('host/test') + } + }) + + def test_rbcd_no_service_pac(self): + # Test constrained delegation when the service TGT does not contain a + # PAC. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_BADOPTION, + 'expected_status': + ntstatus.NT_STATUS_NOT_FOUND, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_service_tgt_fn': self.remove_ticket_pac + }) + + def test_rbcd_non_forwardable(self): + # Test resource-based constrained delegation with a non-forwardable + # ticket. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_BADOPTION, + 'expected_status': ntstatus.NT_STATUS_ACCOUNT_RESTRICTION, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_client_tkt_fn': functools.partial( + self.set_ticket_forwardable, flag=False) + }) + + def test_rbcd_no_pac_options_a(self): + # Test resource-based constrained delegation without the RBCD bit set + # in the PAC options, and an empty msDS-AllowedToDelegateTo attribute. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_BADOPTION, + 'expected_status': ntstatus.NT_STATUS_NOT_SUPPORTED, + 'allow_rbcd': True, + 'pac_options': '1' # does not support RBCD + }) + + def test_rbcd_no_pac_options_b(self): + # Test resource-based constrained delegation without the RBCD bit set + # in the PAC options, and a non-empty msDS-AllowedToDelegateTo + # attribute. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_BADOPTION, + 'expected_status': ntstatus.NT_STATUS_NO_MATCH, + 'allow_rbcd': True, + 'pac_options': '1', # does not support RBCD + 'service1_opts': { + 'delegation_to_spn': ('host/test') + } + }) + + def test_bronze_bit_constrained_delegation_old_checksum(self): + # Attempt to modify the ticket without updating the PAC checksums. + self._run_delegation_test( + { + 'expected_error_mode': (KDC_ERR_MODIFIED, + KDC_ERR_BAD_INTEGRITY), + 'allow_delegation': True, + 'client_tkt_options': '0', # non-forwardable ticket + 'modify_client_tkt_fn': functools.partial( + self.set_ticket_forwardable, + flag=True, update_pac_checksums=False), + 'expect_edata': False + }) + + def test_bronze_bit_rbcd_old_checksum(self): + # Attempt to modify the ticket without updating the PAC checksums. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_MODIFIED, + 'expected_status': ntstatus.NT_STATUS_NOT_SUPPORTED, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'client_tkt_options': '0', # non-forwardable ticket + 'modify_client_tkt_fn': functools.partial( + self.set_ticket_forwardable, + flag=True, update_pac_checksums=False) + }) + + def test_constrained_delegation_missing_client_checksum(self): + # Present a user ticket without the required checksums. + for checksum in self.pac_checksum_types: + with self.subTest(checksum=checksum): + if checksum == krb5pac.PAC_TYPE_TICKET_CHECKSUM: + expected_error_mode = (KDC_ERR_BADOPTION, + KDC_ERR_MODIFIED) + else: + expected_error_mode = KDC_ERR_GENERIC + + self._run_delegation_test( + { + 'expected_error_mode': expected_error_mode, + 'allow_delegation': True, + 'modify_client_tkt_fn': functools.partial( + self.remove_pac_checksum, checksum=checksum), + 'expect_edata': False + }) + + 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): + with self.subTest(checksum=checksum): + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_GENERIC, + 'expected_status': + ntstatus.NT_STATUS_INSUFFICIENT_RESOURCES, + 'allow_delegation': True, + 'modify_service_tgt_fn': functools.partial( + self.remove_pac_checksum, checksum=checksum) + }) + + def test_rbcd_missing_client_checksum(self): + # Present a user ticket without the required checksums. + for checksum in self.pac_checksum_types: + with self.subTest(checksum=checksum): + if checksum == krb5pac.PAC_TYPE_TICKET_CHECKSUM: + expected_error_mode = KDC_ERR_MODIFIED + else: + expected_error_mode = KDC_ERR_GENERIC + + 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': functools.partial( + self.remove_pac_checksum, checksum=checksum) + }) + + 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): + with self.subTest(checksum=checksum): + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_GENERIC, + 'expected_status': + ntstatus.NT_STATUS_INSUFFICIENT_RESOURCES, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_service_tgt_fn': functools.partial( + self.remove_pac_checksum, checksum=checksum) + }) + + def test_constrained_delegation_zeroed_client_checksum(self): + # Present a user ticket with invalid checksums. + for checksum in self.pac_checksum_types: + with self.subTest(checksum=checksum): + self._run_delegation_test( + { + 'expected_error_mode': (KDC_ERR_MODIFIED, + KDC_ERR_BAD_INTEGRITY), + 'allow_delegation': True, + 'modify_client_tkt_fn': functools.partial( + self.zeroed_pac_checksum, checksum=checksum), + 'expect_edata': False + }) + + def test_constrained_delegation_zeroed_service_checksum(self): + # Present the service's ticket with invalid checksums. + for checksum in self.pac_checksum_types: + with self.subTest(checksum=checksum): + if checksum == krb5pac.PAC_TYPE_SRV_CHECKSUM: + expected_error_mode = (KDC_ERR_MODIFIED, + KDC_ERR_BAD_INTEGRITY) + expected_status = ntstatus.NT_STATUS_WRONG_PASSWORD + else: + expected_error_mode = 0 + expected_status = None + + self._run_delegation_test( + { + 'expected_error_mode': expected_error_mode, + 'expected_status': expected_status, + 'allow_delegation': True, + 'modify_service_tgt_fn': functools.partial( + self.zeroed_pac_checksum, checksum=checksum) + }) + + def test_rbcd_zeroed_client_checksum(self): + # Present a user ticket with invalid checksums. + for checksum in self.pac_checksum_types: + with self.subTest(checksum=checksum): + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_MODIFIED, + 'expected_status': + ntstatus.NT_STATUS_NOT_SUPPORTED, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_client_tkt_fn': functools.partial( + self.zeroed_pac_checksum, checksum=checksum) + }) + + def test_rbcd_zeroed_service_checksum(self): + # Present the service's ticket with invalid checksums. + for checksum in self.pac_checksum_types: + with self.subTest(checksum=checksum): + if checksum == krb5pac.PAC_TYPE_SRV_CHECKSUM: + expected_error_mode = (KDC_ERR_MODIFIED, + KDC_ERR_BAD_INTEGRITY) + expected_status = ntstatus.NT_STATUS_WRONG_PASSWORD + else: + expected_error_mode = 0 + expected_status = None + + self._run_delegation_test( + { + 'expected_error_mode': expected_error_mode, + 'expected_status': expected_status, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_service_tgt_fn': functools.partial( + self.zeroed_pac_checksum, checksum=checksum) + }) + + unkeyed_ctypes = {Cksumtype.MD5, Cksumtype.SHA1, Cksumtype.CRC32} + + def test_constrained_delegation_unkeyed_client_checksum(self): + # Present a user ticket with invalid checksums. + for checksum in self.pac_checksum_types: + for ctype in self.unkeyed_ctypes: + with self.subTest(checksum=checksum, ctype=ctype): + if (checksum == krb5pac.PAC_TYPE_SRV_CHECKSUM + and ctype == Cksumtype.SHA1): + expected_error_mode = (KDC_ERR_SUMTYPE_NOSUPP, + KDC_ERR_INAPP_CKSUM) + else: + 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': functools.partial( + self.unkeyed_pac_checksum, + checksum=checksum, ctype=ctype), + 'expect_edata': False + }) + + def test_constrained_delegation_unkeyed_service_checksum(self): + # Present the service's ticket with invalid checksums. + for checksum in self.pac_checksum_types: + for ctype in self.unkeyed_ctypes: + with self.subTest(checksum=checksum, ctype=ctype): + if checksum == krb5pac.PAC_TYPE_SRV_CHECKSUM: + if ctype == Cksumtype.SHA1: + expected_error_mode = (KDC_ERR_SUMTYPE_NOSUPP, + KDC_ERR_INAPP_CKSUM) + expected_status = ntstatus.NT_STATUS_LOGON_FAILURE + else: + expected_error_mode = (KDC_ERR_GENERIC, + KDC_ERR_INAPP_CKSUM) + expected_status = ( + ntstatus.NT_STATUS_INSUFFICIENT_RESOURCES) + else: + expected_error_mode = 0 + expected_status = None + + self._run_delegation_test( + { + 'expected_error_mode': expected_error_mode, + 'expected_status': expected_status, + 'allow_delegation': True, + 'modify_service_tgt_fn': functools.partial( + self.unkeyed_pac_checksum, + checksum=checksum, ctype=ctype) + }) + + def test_rbcd_unkeyed_client_checksum(self): + # Present a user ticket with invalid checksums. + for checksum in self.pac_checksum_types: + for ctype in self.unkeyed_ctypes: + with self.subTest(checksum=checksum, ctype=ctype): + if (checksum == krb5pac.PAC_TYPE_SRV_CHECKSUM + and ctype == Cksumtype.SHA1): + expected_error_mode = KDC_ERR_SUMTYPE_NOSUPP + else: + expected_error_mode = KDC_ERR_GENERIC + + 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': functools.partial( + self.unkeyed_pac_checksum, + checksum=checksum, ctype=ctype) + }) + + def test_rbcd_unkeyed_service_checksum(self): + # Present the service's ticket with invalid checksums. + for checksum in self.pac_checksum_types: + for ctype in self.unkeyed_ctypes: + with self.subTest(checksum=checksum, ctype=ctype): + if checksum == krb5pac.PAC_TYPE_SRV_CHECKSUM: + if ctype == Cksumtype.SHA1: + expected_error_mode = (KDC_ERR_SUMTYPE_NOSUPP, + KDC_ERR_BAD_INTEGRITY) + expected_status = ntstatus.NT_STATUS_LOGON_FAILURE + else: + expected_error_mode = KDC_ERR_GENERIC + expected_status = ( + ntstatus.NT_STATUS_INSUFFICIENT_RESOURCES) + else: + expected_error_mode = 0 + expected_status = None + + self._run_delegation_test( + { + 'expected_error_mode': expected_error_mode, + 'expected_status': expected_status, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_service_tgt_fn': functools.partial( + self.unkeyed_pac_checksum, + checksum=checksum, ctype=ctype) + }) + + def remove_pac_checksum(self, ticket, checksum): + checksum_keys = self.get_krbtgt_checksum_key() + + return self.modified_ticket(ticket, + checksum_keys=checksum_keys, + include_checksums={checksum: False}) + + def zeroed_pac_checksum(self, ticket, checksum): + krbtgt_creds = self.get_krbtgt_creds() + krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) + + server_key = ticket.decryption_key + + checksum_keys = { + krb5pac.PAC_TYPE_SRV_CHECKSUM: server_key, + krb5pac.PAC_TYPE_KDC_CHECKSUM: krbtgt_key, + krb5pac.PAC_TYPE_TICKET_CHECKSUM: krbtgt_key, + } + + if checksum == krb5pac.PAC_TYPE_SRV_CHECKSUM: + zeroed_key = server_key + else: + zeroed_key = krbtgt_key + + checksum_keys[checksum] = ZeroedChecksumKey(zeroed_key.key, + zeroed_key.kvno) + + return self.modified_ticket(ticket, + checksum_keys=checksum_keys, + include_checksums={checksum: True}) + + def unkeyed_pac_checksum(self, ticket, checksum, ctype): + krbtgt_creds = self.get_krbtgt_creds() + krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) + + server_key = ticket.decryption_key + + checksum_keys = { + krb5pac.PAC_TYPE_SRV_CHECKSUM: server_key, + krb5pac.PAC_TYPE_KDC_CHECKSUM: krbtgt_key, + krb5pac.PAC_TYPE_TICKET_CHECKSUM: krbtgt_key, + } + + # Make a copy of the existing key and change the ctype. + key = checksum_keys[checksum] + new_key = RodcPacEncryptionKey(key.key, key.kvno) + new_key.ctype = ctype + checksum_keys[checksum] = new_key + + return self.modified_ticket(ticket, + checksum_keys=checksum_keys, + include_checksums={checksum: True}) + + def add_delegation_info(self, ticket, services=None): + def modify_pac_fn(pac): + pac_buffers = pac.buffers + self.assertNotIn(krb5pac.PAC_TYPE_CONSTRAINED_DELEGATION, + (buffer.type for buffer in pac_buffers)) + + transited_services = list(map(lsa.String, services)) + + delegation = krb5pac.PAC_CONSTRAINED_DELEGATION() + delegation.proxy_target = lsa.String('test_proxy_target') + delegation.transited_services = transited_services + delegation.num_transited_services = len(transited_services) + + info = krb5pac.PAC_CONSTRAINED_DELEGATION_CTR() + info.info = delegation + + pac_buffer = krb5pac.PAC_BUFFER() + pac_buffer.type = krb5pac.PAC_TYPE_CONSTRAINED_DELEGATION + pac_buffer.info = info + + pac_buffers.append(pac_buffer) + + pac.buffers = pac_buffers + pac.num_buffers += 1 + + return pac + + checksum_keys = self.get_krbtgt_checksum_key() + + return self.modified_ticket(ticket, + checksum_keys=checksum_keys, + modify_pac_fn=modify_pac_fn) + + def set_ticket_forwardable(self, ticket, flag, update_pac_checksums=True): + flag = '1' if flag else '0' + + def modify_fn(enc_part): + # Reset the forwardable flag + forwardable_pos = (len(tuple(krb5_asn1.TicketFlags('forwardable'))) + - 1) + + flags = enc_part['flags'] + self.assertLessEqual(forwardable_pos, len(flags)) + enc_part['flags'] = (flags[:forwardable_pos] + + flag + + flags[forwardable_pos+1:]) + + return enc_part + + if update_pac_checksums: + checksum_keys = self.get_krbtgt_checksum_key() + else: + checksum_keys = None + + return self.modified_ticket(ticket, + modify_fn=modify_fn, + checksum_keys=checksum_keys, + update_pac_checksums=update_pac_checksums) + + def remove_ticket_pac(self, ticket): + return self.modified_ticket(ticket, + exclude_pac=True) + if __name__ == "__main__": global_asn1_print = False diff --git a/python/samba/tests/usage.py b/python/samba/tests/usage.py index 7cdf25b48ae..0a9aac1f2cc 100644 --- a/python/samba/tests/usage.py +++ b/python/samba/tests/usage.py @@ -103,6 +103,7 @@ EXCLUDE_USAGE = { 'python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py', 'python/samba/tests/krb5/as_req_tests.py', 'python/samba/tests/krb5/fast_tests.py', + 'python/samba/tests/krb5/rodc_tests.py', } EXCLUDE_HELP = { diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 6eb667f8969..0b8e83f1f3f 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -125,3 +125,32 @@ # Heimdal currently does not generate ticket signatures # ^samba.tests.krb5.compatability_tests.samba.tests.krb5.compatability_tests.SimpleKerberosTests.test_heimdal_ticket_signature +# +# S4U tests +# +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_rbcd_old_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_missing_client_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_unkeyed_client_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_unkeyed_service_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_zeroed_client_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_zeroed_service_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_existing_delegation_info +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_missing_client_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_a +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_b +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_unkeyed_client_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_unkeyed_service_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_client_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_service_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_client_not_delegated +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_forwardable +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_nonempty_allowed +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_trusted_empty_allowed +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_trusted_nonempty_allowed +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_without_forwardable +# +# RODC tests +# +^samba.tests.krb5.rodc_tests.samba.tests.krb5.rodc_tests.RodcKerberosTests.test_rodc_ticket_signature diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index f7b34baba8a..5d3c4ac4bfd 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -817,9 +817,16 @@ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.simple_tests", 'FAST_SUPPORT': have_fast_support, 'TKT_SIG_SUPPORT': tkt_sig_support}) planoldpythontestsuite("ad_dc_default:local", "samba.tests.krb5.s4u_tests", - environ={'SERVICE_USERNAME':'srv_account', - 'SERVICE_PASSWORD':'$PASSWORD', + environ={'ADMIN_USERNAME':'$USERNAME', + 'ADMIN_PASSWORD':'$PASSWORD', 'FOR_USER':'$USERNAME', + 'STRICT_CHECKING':'0', + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support}) +planoldpythontestsuite("rodc:local", "samba.tests.krb5.rodc_tests", + environ={'ADMIN_USERNAME':'$USERNAME', + 'ADMIN_PASSWORD':'$PASSWORD', + 'STRICT_CHECKING':'0', 'FAST_SUPPORT': have_fast_support, 'TKT_SIG_SUPPORT': tkt_sig_support}) -- 2.25.1 From 4e0213ce6607b414b4a126b7a7a0331a1371e521 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 29 Sep 2021 12:07:40 +1300 Subject: [PATCH 114/159] tests/krb5: Don't include empty AD-IF-RELEVANT BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 1a08399cd8169a525cc9e7aed99da84ef20e5b9c) --- python/samba/tests/krb5/raw_testcase.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 2e289c90ce7..e008223eb23 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -3424,14 +3424,19 @@ class RawKerberosTest(TestCaseInTempDir): if expect_pac: self.assertIsNotNone(old_pac, 'Expected PAC') - ad_relevant = self.der_encode( - relevant_elems, - asn1Spec=krb5_asn1.AD_IF_RELEVANT()) - - authdata_elem = self.AuthorizationData_create(AD_IF_RELEVANT, - ad_relevant) + if relevant_elems: + ad_relevant = self.der_encode( + relevant_elems, + asn1Spec=krb5_asn1.AD_IF_RELEVANT()) + + authdata_elem = self.AuthorizationData_create( + AD_IF_RELEVANT, + ad_relevant) + else: + authdata_elem = None - new_auth_data.append(authdata_elem) + if authdata_elem is not None: + new_auth_data.append(authdata_elem) if expect_pac: self.assertIsNotNone(ad_relevant, 'Expected AD-RELEVANT') -- 2.25.1 From 80bdce644a8e7bb641f27f0f6cfb1fa8e33b1f47 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 8 Oct 2021 15:41:35 +1300 Subject: [PATCH 115/159] tests/krb5: Allow bypassing cache when creating accounts BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 3948701f1d0f3ccd06f6dad56ca72833d66b1d84) --- python/samba/tests/krb5/kdc_base_test.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 8a5e12bbed4..87160f675ae 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -610,7 +610,8 @@ class KDCBaseTest(RawKerberosTest): def get_cached_creds(self, *, machine_account, - opts=None): + opts=None, + use_cache=True): if opts is None: opts = {} @@ -638,9 +639,13 @@ class KDCBaseTest(RawKerberosTest): cache_key = tuple(sorted(account_opts.items())) - creds = self.account_cache.get(cache_key) - if creds is None: - creds = self.create_account_opts(**account_opts) + if use_cache: + creds = self.account_cache.get(cache_key) + if creds is not None: + return creds + + creds = self.create_account_opts(**account_opts) + if use_cache: self.account_cache[cache_key] = creds return creds -- 2.25.1 From 4e9e106e3906fcf41734519eb5d1d18b418fa0ee Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 8 Oct 2021 15:40:39 +1300 Subject: [PATCH 116/159] tests/krb5: Fix duplicate account creation BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 3dede18c5a1801023a60cc55b99022b033428350) --- .../krb5/ms_kile_client_principal_lookup_tests.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py b/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py index 501bc4892f4..2ee3d4a2a83 100755 --- a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py +++ b/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py @@ -150,18 +150,15 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): # Create a machine account for the test. # samdb = self.get_samdb() - user_name = "mskilemac" - (mc, dn) = self.create_account(samdb, user_name, machine_account=True) - realm = mc.get_realm().lower() - mach_name = "mskilemac" - (mc, _) = self.create_account(samdb, mach_name, machine_account=True) + (mc, dn) = self.create_account(samdb, mach_name, machine_account=True) + realm = mc.get_realm().lower() # Do the initial AS-REQ, should get a pre-authentication required # response etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) cname = self.PrincipalName_create( - name_type=NT_PRINCIPAL, names=[user_name]) + name_type=NT_PRINCIPAL, names=[mach_name]) sname = self.PrincipalName_create( name_type=NT_SRV_INST, names=["krbtgt", realm]) @@ -180,7 +177,7 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): key = self.EncryptionKey_import(enc_part2['key']) cname = self.PrincipalName_create( name_type=NT_PRINCIPAL, - names=[user_name]) + names=[mach_name]) sname = self.PrincipalName_create( name_type=NT_PRINCIPAL, names=[mc.get_username()]) @@ -197,7 +194,7 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): # check the crealm and cname cname = enc_part['cname'] self.assertEqual(NT_PRINCIPAL, cname['name-type']) - self.assertEqual(user_name.encode('UTF8'), cname['name-string'][0]) + self.assertEqual(mach_name.encode('UTF8'), cname['name-string'][0]) self.assertEqual(realm.upper().encode('UTF8'), enc_part['crealm']) def test_nt_principal_step_3(self): -- 2.25.1 From 27ea32d36b78b2a84a4219981c24319c32feb1d8 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 8 Oct 2021 16:06:58 +1300 Subject: [PATCH 117/159] s4:kdc: Simplify samba_kdc_update_pac_blob() to take ldb_context as parameter BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 7149eeaceb426470b1b8181749d2d081c2fb83a4) --- source4/kdc/mit_samba.c | 7 +------ source4/kdc/pac-glue.c | 5 ++--- source4/kdc/pac-glue.h | 3 +-- source4/kdc/wdc-samba4.c | 2 +- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c index 54dcd545ea1..2936fe2d18a 100644 --- a/source4/kdc/mit_samba.c +++ b/source4/kdc/mit_samba.c @@ -482,7 +482,6 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, DATA_BLOB *deleg_blob = NULL; struct samba_kdc_entry *client_skdc_entry = NULL; struct samba_kdc_entry *krbtgt_skdc_entry = NULL; - struct samba_kdc_entry *server_skdc_entry = NULL; bool is_in_db = false; bool is_untrusted = false; size_t num_types = 0; @@ -513,9 +512,6 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, if (server == NULL) { return EINVAL; } - server_skdc_entry = - talloc_get_type_abort(server->e_data, - struct samba_kdc_entry); if (krbtgt == NULL) { return EINVAL; @@ -575,8 +571,7 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, nt_status = samba_kdc_update_pac_blob(tmp_ctx, context, - krbtgt_skdc_entry, - server_skdc_entry, + krbtgt_skdc_entry->kdc_db_ctx->samdb, *pac, pac_blob, pac_srv_sig, diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c index 04fbc5cf487..88bcb734fc5 100644 --- a/source4/kdc/pac-glue.c +++ b/source4/kdc/pac-glue.c @@ -747,8 +747,7 @@ NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx, NTSTATUS samba_kdc_update_pac_blob(TALLOC_CTX *mem_ctx, krb5_context context, - struct samba_kdc_entry *krbtgt, - struct samba_kdc_entry *server, + struct ldb_context *samdb, const krb5_pac pac, DATA_BLOB *pac_blob, struct PAC_SIGNATURE_DATA *pac_srv_sig, struct PAC_SIGNATURE_DATA *pac_kdc_sig) @@ -768,7 +767,7 @@ NTSTATUS samba_kdc_update_pac_blob(TALLOC_CTX *mem_ctx, * as the token might be generated by a trusted domain. */ nt_status = authsam_update_user_info_dc(mem_ctx, - krbtgt->kdc_db_ctx->samdb, + samdb, user_info_dc); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; diff --git a/source4/kdc/pac-glue.h b/source4/kdc/pac-glue.h index 2eb7fd3b755..7b51b0389f5 100644 --- a/source4/kdc/pac-glue.h +++ b/source4/kdc/pac-glue.h @@ -51,8 +51,7 @@ NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx, NTSTATUS samba_kdc_update_pac_blob(TALLOC_CTX *mem_ctx, krb5_context context, - struct samba_kdc_entry *krbtgt, - struct samba_kdc_entry *server, + struct ldb_context *samdb, const krb5_pac pac, DATA_BLOB *pac_blob, struct PAC_SIGNATURE_DATA *pac_srv_sig, struct PAC_SIGNATURE_DATA *pac_kdc_sig); diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c index a7d8de1f417..68c8a8aa572 100644 --- a/source4/kdc/wdc-samba4.c +++ b/source4/kdc/wdc-samba4.c @@ -186,7 +186,7 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, } nt_status = samba_kdc_update_pac_blob(mem_ctx, context, - krbtgt_skdc_entry, p, + krbtgt_skdc_entry->kdc_db_ctx->samdb, *pac, pac_blob, pac_srv_sig, pac_kdc_sig); if (!NT_STATUS_IS_OK(nt_status)) { -- 2.25.1 From 6c36f76f2ae1941100b3380f7185785da8d3bc58 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 6 Oct 2021 16:40:21 +1300 Subject: [PATCH 118/159] s4:kdc: Fix debugging messages BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit c14c61748b5a2d2a4f4de00615c476fcf381309e) --- source4/kdc/wdc-samba4.c | 48 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c index 68c8a8aa572..037db40ce46 100644 --- a/source4/kdc/wdc-samba4.c +++ b/source4/kdc/wdc-samba4.c @@ -238,10 +238,10 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, switch (types[i]) { case PAC_TYPE_LOGON_INFO: if (logon_info_idx != -1) { - DEBUG(1, ("logon type[%d] twice [%d] and [%d]: \n", - (int)types[i], - (int)logon_info_idx, - (int)i)); + DEBUG(1, ("logon info type[%"PRIu32"] twice [%zd] and [%zu]: \n", + types[i], + logon_info_idx, + i)); SAFE_FREE(types); talloc_free(mem_ctx); return EINVAL; @@ -250,10 +250,10 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, break; case PAC_TYPE_CONSTRAINED_DELEGATION: if (delegation_idx != -1) { - DEBUG(1, ("logon type[%d] twice [%d] and [%d]: \n", - (int)types[i], - (int)logon_info_idx, - (int)i)); + DEBUG(1, ("constrained delegation type[%"PRIu32"] twice [%zd] and [%zu]: \n", + types[i], + delegation_idx, + i)); SAFE_FREE(types); talloc_free(mem_ctx); return EINVAL; @@ -262,10 +262,10 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, break; case PAC_TYPE_LOGON_NAME: if (logon_name_idx != -1) { - DEBUG(1, ("logon type[%d] twice [%d] and [%d]: \n", - (int)types[i], - (int)logon_info_idx, - (int)i)); + DEBUG(1, ("logon name type[%"PRIu32"] twice [%zd] and [%zu]: \n", + types[i], + logon_name_idx, + i)); SAFE_FREE(types); talloc_free(mem_ctx); return EINVAL; @@ -274,10 +274,10 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, break; case PAC_TYPE_UPN_DNS_INFO: if (upn_dns_info_idx != -1) { - DEBUG(1, ("logon type[%d] twice [%d] and [%d]: \n", - (int)types[i], - (int)logon_info_idx, - (int)i)); + DEBUG(1, ("upn dns info type[%"PRIu32"] twice [%zd] and [%zu]: \n", + types[i], + upn_dns_info_idx, + i)); SAFE_FREE(types); talloc_free(mem_ctx); return EINVAL; @@ -286,10 +286,10 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, break; case PAC_TYPE_SRV_CHECKSUM: if (srv_checksum_idx != -1) { - DEBUG(1, ("logon type[%d] twice [%d] and [%d]: \n", - (int)types[i], - (int)logon_info_idx, - (int)i)); + DEBUG(1, ("server checksum type[%"PRIu32"] twice [%zd] and [%zu]: \n", + types[i], + srv_checksum_idx, + i)); SAFE_FREE(types); talloc_free(mem_ctx); return EINVAL; @@ -298,10 +298,10 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, break; case PAC_TYPE_KDC_CHECKSUM: if (kdc_checksum_idx != -1) { - DEBUG(1, ("logon type[%d] twice [%d] and [%d]: \n", - (int)types[i], - (int)logon_info_idx, - (int)i)); + DEBUG(1, ("kdc checksum type[%"PRIu32"] twice [%zd] and [%zu]: \n", + types[i], + kdc_checksum_idx, + i)); SAFE_FREE(types); talloc_free(mem_ctx); return EINVAL; -- 2.25.1 From 0a21ac5ff9ce639751a568876a2bee4e795818c3 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 8 Oct 2021 15:42:29 +1300 Subject: [PATCH 119/159] s4/torture: Expect ticket checksum PAC buffer BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett [abartlet@samba.org backported from commit d5002c34ce1ffef795dc83af3175ca0e04d17dfd due to missing tests in Samba 4.14 that crashed the MIT KDC] --- selftest/knownfail_heimdal_kdc | 41 ++++++++++++++++++++++++++++++++ selftest/knownfail_mit_kdc | 41 ++++++++++++++++++++++++++++++++ source4/torture/rpc/remote_pac.c | 14 +++++++++-- 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 0b8e83f1f3f..31572067ad4 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -154,3 +154,44 @@ # RODC tests # ^samba.tests.krb5.rodc_tests.samba.tests.krb5.rodc_tests.RodcKerberosTests.test_rodc_ticket_signature +# +# 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.STEP1 remote.pac verification.ad_dc_ntvfs:local +^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc:local +^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc_ntvfs:local +^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc:local +^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc_ntvfs:local +^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc:local +^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc_ntvfs:local +^samba4.blackbox.pkinit_pac.netr-mem-arcfour.s4u2proxy-arcfour.ad_dc:local +^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 diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc index 4e0b20c5c80..09efbc7b590 100644 --- a/selftest/knownfail_mit_kdc +++ b/selftest/knownfail_mit_kdc @@ -329,3 +329,44 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_simple_fast_no_etypes.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_simple_no_sname.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_simple_tgs_no_sname.ad_dc +# +# 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.STEP1 remote.pac verification.ad_dc_ntvfs:local +^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc:local +^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc_ntvfs:local +^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc:local +^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc_ntvfs:local +^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc:local +^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc_ntvfs:local +^samba4.blackbox.pkinit_pac.netr-mem-arcfour.s4u2proxy-arcfour.ad_dc:local +^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 diff --git a/source4/torture/rpc/remote_pac.c b/source4/torture/rpc/remote_pac.c index c74746123fe..14c23f674f1 100644 --- a/source4/torture/rpc/remote_pac.c +++ b/source4/torture/rpc/remote_pac.c @@ -266,7 +266,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 = 4; + num_pac_buffers = 5; if (expect_pac_upn_dns_info) { num_pac_buffers += 1; } @@ -317,6 +317,12 @@ static bool test_PACVerify(struct torture_context *tctx, pac_buf->info != NULL, "PAC_TYPE_KDC_CHECKSUM info"); + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_TICKET_CHECKSUM); + torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_TICKET_CHECKSUM"); + torture_assert(tctx, + pac_buf->info != NULL, + "PAC_TYPE_TICKET_CHECKSUM info"); + ok = netlogon_validate_pac(tctx, p, server_creds, secure_channel_type, test_machine_name, negotiate_flags, pac_data, session_info); @@ -1076,7 +1082,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 = 6; + num_pac_buffers = 7; 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"); @@ -1101,6 +1107,10 @@ static bool test_S4U2Proxy(struct torture_context *tctx, torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_KDC_CHECKSUM"); torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_KDC_CHECKSUM info"); + pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_TICKET_CHECKSUM); + 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_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"); -- 2.25.1 From 330ec109145ec100da8104168816dfaddd1fa94d Mon Sep 17 00:00:00 2001 From: Isaac Boukris Date: Mon, 28 Dec 2020 22:07:10 +0200 Subject: [PATCH 120/159] kdc: remove KRB5SignedPath, to be replaced with PAC KRB5SignedPath was a Heimdal-specific authorization data element used to protect the authenticity of evidence tickets when used in constrained delegation (without a Windows PAC). Remove this, to be replaced with the Windows PAC which itself now supports signing the entire ticket in the TGS key. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 [jsutton@samba.org Backported from Heimdal commit bb1d8f2a8c2545bccdf2c9179ce9259bf1050086 - Removed tests - Removed auditing hook (only present in Heimdal master) - Added knownfails ] Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit ccabc7f16cca5b0dcb46233e934e708167f1071b) --- selftest/knownfail_heimdal_kdc | 6 + source4/heimdal/kdc/kerberos5.c | 12 -- source4/heimdal/kdc/krb5tgs.c | 297 ----------------------------- source4/heimdal/lib/asn1/krb5.asn1 | 21 -- 4 files changed, 6 insertions(+), 330 deletions(-) diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 31572067ad4..f0263133eee 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -130,6 +130,7 @@ # ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_rbcd_old_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_missing_client_checksum +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_service_pac ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_unkeyed_client_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_unkeyed_service_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_zeroed_client_checksum @@ -195,3 +196,8 @@ ^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 +# +# The lack of KRB5SignedPath means we no longer return +# KRB5KRB_ERR_RESPONSE_TOO_BIG in this specific case +# +^samba4.krb5.kdc with machine account.as-req-pac-request.fl2000dc:local diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c index 0fa336e871c..a400b21a652 100644 --- a/source4/heimdal/kdc/kerberos5.c +++ b/source4/heimdal/kdc/kerberos5.c @@ -1744,18 +1744,6 @@ _kdc_as_rep(krb5_context context, _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime, et.endtime, et.renew_till); - /* do this as the last thing since this signs the EncTicketPart */ - ret = _kdc_add_KRB5SignedPath(context, - config, - server, - setype, - client->entry.principal, - NULL, - NULL, - &et); - if (ret) - goto out; - log_as_req(context, config, reply_key->keytype, setype, b); ret = _kdc_encode_reply(context, config, diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c index d143eb739eb..c6bab82f517 100644 --- a/source4/heimdal/kdc/krb5tgs.c +++ b/source4/heimdal/kdc/krb5tgs.c @@ -47,230 +47,6 @@ get_krbtgt_realm(const PrincipalName *p) return NULL; } -/* - * The KDC might add a signed path to the ticket authorization data - * field. This is to avoid server impersonating clients and the - * request constrained delegation. - * - * This is done by storing a KRB5_AUTHDATA_IF_RELEVANT with a single - * entry of type KRB5SignedPath. - */ - -static krb5_error_code -find_KRB5SignedPath(krb5_context context, - const AuthorizationData *ad, - krb5_data *data) -{ - AuthorizationData child; - krb5_error_code ret; - int pos; - - if (ad == NULL || ad->len == 0) - return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; - - pos = ad->len - 1; - - if (ad->val[pos].ad_type != KRB5_AUTHDATA_IF_RELEVANT) - return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; - - ret = decode_AuthorizationData(ad->val[pos].ad_data.data, - ad->val[pos].ad_data.length, - &child, - NULL); - if (ret) { - krb5_set_error_message(context, ret, "Failed to decode " - "IF_RELEVANT with %d", ret); - return ret; - } - - if (child.len != 1) { - free_AuthorizationData(&child); - return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; - } - - if (child.val[0].ad_type != KRB5_AUTHDATA_SIGNTICKET) { - free_AuthorizationData(&child); - return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; - } - - if (data) - ret = der_copy_octet_string(&child.val[0].ad_data, data); - free_AuthorizationData(&child); - return ret; -} - -krb5_error_code -_kdc_add_KRB5SignedPath(krb5_context context, - krb5_kdc_configuration *config, - hdb_entry_ex *krbtgt, - krb5_enctype enctype, - krb5_principal client, - krb5_const_principal server, - krb5_principals principals, - EncTicketPart *tkt) -{ - krb5_error_code ret; - KRB5SignedPath sp; - krb5_data data; - krb5_crypto crypto = NULL; - size_t size = 0; - - if (server && principals) { - ret = add_Principals(principals, server); - if (ret) - return ret; - } - - { - KRB5SignedPathData spd; - - spd.client = client; - spd.authtime = tkt->authtime; - spd.delegated = principals; - spd.method_data = NULL; - - ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length, - &spd, &size, ret); - if (ret) - return ret; - if (data.length != size) - krb5_abortx(context, "internal asn.1 encoder error"); - } - - { - Key *key; - ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key); - if (ret == 0) - ret = krb5_crypto_init(context, &key->key, 0, &crypto); - if (ret) { - free(data.data); - return ret; - } - } - - /* - * Fill in KRB5SignedPath - */ - - sp.etype = enctype; - sp.delegated = principals; - sp.method_data = NULL; - - ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0, - data.data, data.length, &sp.cksum); - krb5_crypto_destroy(context, crypto); - free(data.data); - if (ret) - return ret; - - ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret); - free_Checksum(&sp.cksum); - if (ret) - return ret; - if (data.length != size) - krb5_abortx(context, "internal asn.1 encoder error"); - - - /* - * Add IF-RELEVANT(KRB5SignedPath) to the last slot in - * authorization data field. - */ - - ret = _kdc_tkt_add_if_relevant_ad(context, tkt, - KRB5_AUTHDATA_SIGNTICKET, &data); - krb5_data_free(&data); - - return ret; -} - -static krb5_error_code -check_KRB5SignedPath(krb5_context context, - krb5_kdc_configuration *config, - hdb_entry_ex *krbtgt, - krb5_principal cp, - EncTicketPart *tkt, - krb5_principals *delegated, - int *signedpath) -{ - krb5_error_code ret; - krb5_data data; - krb5_crypto crypto = NULL; - - if (delegated) - *delegated = NULL; - - ret = find_KRB5SignedPath(context, tkt->authorization_data, &data); - if (ret == 0) { - KRB5SignedPathData spd; - KRB5SignedPath sp; - size_t size = 0; - - ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL); - krb5_data_free(&data); - if (ret) - return ret; - - spd.client = cp; - spd.authtime = tkt->authtime; - spd.delegated = sp.delegated; - spd.method_data = sp.method_data; - - ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length, - &spd, &size, ret); - if (ret) { - free_KRB5SignedPath(&sp); - return ret; - } - if (data.length != size) - krb5_abortx(context, "internal asn.1 encoder error"); - - { - Key *key; - ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key); - if (ret == 0) - ret = krb5_crypto_init(context, &key->key, 0, &crypto); - if (ret) { - free(data.data); - free_KRB5SignedPath(&sp); - return ret; - } - } - ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, - data.data, data.length, - &sp.cksum); - krb5_crypto_destroy(context, crypto); - free(data.data); - if (ret) { - free_KRB5SignedPath(&sp); - kdc_log(context, config, 5, - "KRB5SignedPath not signed correctly, not marking as signed"); - return 0; - } - - if (delegated && sp.delegated) { - - *delegated = malloc(sizeof(*sp.delegated)); - if (*delegated == NULL) { - free_KRB5SignedPath(&sp); - return ENOMEM; - } - - ret = copy_Principals(*delegated, sp.delegated); - if (ret) { - free_KRB5SignedPath(&sp); - free(*delegated); - *delegated = NULL; - return ret; - } - } - free_KRB5SignedPath(&sp); - - *signedpath = 1; - } - - return 0; -} - /* * */ @@ -738,7 +514,6 @@ tgs_make_reply(krb5_context context, krb5_principal client_principal, hdb_entry_ex *krbtgt, krb5_enctype krbtgt_etype, - krb5_principals spp, const krb5_data *rspac, const METHOD_DATA *enc_pa_data, const char **e_text, @@ -903,20 +678,6 @@ tgs_make_reply(krb5_context context, goto out; } } - - /* Filter out type KRB5SignedPath */ - ret = find_KRB5SignedPath(context, et.authorization_data, NULL); - if (ret == 0) { - if (et.authorization_data->len == 1) { - free_AuthorizationData(et.authorization_data); - free(et.authorization_data); - et.authorization_data = NULL; - } else { - AuthorizationData *ad = et.authorization_data; - free_AuthorizationDataElement(&ad->val[ad->len - 1]); - ad->len--; - } - } } ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key); @@ -945,24 +706,6 @@ tgs_make_reply(krb5_context context, _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime, et.endtime, et.renew_till); - /* Don't sign cross realm tickets, they can't be checked anyway */ - { - char *r = get_krbtgt_realm(&ek.sname); - - if (r == NULL || strcmp(r, ek.srealm) == 0) { - ret = _kdc_add_KRB5SignedPath(context, - config, - krbtgt, - krbtgt_etype, - client_principal, - NULL, - spp, - &et); - if (ret) - goto out; - } - } - if (enc_pa_data->len) { rep.padata = calloc(1, sizeof(*rep.padata)); if (rep.padata == NULL) { @@ -1517,7 +1260,6 @@ tgs_build_reply(krb5_context context, HDB *clientdb, *s4u2self_impersonated_clientdb; krb5_realm ref_realm = NULL; EncTicketPart *tgt = &ticket->ticket; - krb5_principals spp = NULL; const EncryptionKey *ekey; krb5_keyblock sessionkey; krb5_kvno kvno; @@ -1891,23 +1633,6 @@ server_lookup: goto out; } - /* also check the krbtgt for signature */ - ret = check_KRB5SignedPath(context, - config, - krbtgt, - cp, - tgt, - &spp, - &signedpath); - if (ret) { - const char *msg = krb5_get_error_message(context, ret); - kdc_log(context, config, 0, - "KRB5SignedPath check failed for %s (%s) from %s with %s", - spn, cpn, from, msg); - krb5_free_error_message(context, msg); - goto out; - } - /* * Process request */ @@ -2200,27 +1925,6 @@ server_lookup: goto out; } - /* - * Check that the KDC issued the user's ticket. - */ - ret = check_KRB5SignedPath(context, - config, - krbtgt, - cp, - &adtkt, - NULL, - &ad_signedpath); - if (ret) { - const char *msg = krb5_get_error_message(context, ret); - kdc_log(context, config, 0, - "KRB5SignedPath check from service %s failed " - "for delegation to %s for client %s (%s)" - "from %s failed with %s", - spn, tpn, dpn, cpn, from, msg); - krb5_free_error_message(context, msg); - goto out; - } - if (!ad_signedpath) { ret = KRB5KDC_ERR_BADOPTION; kdc_log(context, config, 0, @@ -2318,7 +2022,6 @@ server_lookup: cp, krbtgt_out, krbtgt_etype, - spp, &rspac, &enc_pa_data, e_text, diff --git a/source4/heimdal/lib/asn1/krb5.asn1 b/source4/heimdal/lib/asn1/krb5.asn1 index c2f40c0ecd6..155d51d4ac8 100644 --- a/source4/heimdal/lib/asn1/krb5.asn1 +++ b/source4/heimdal/lib/asn1/krb5.asn1 @@ -43,9 +43,6 @@ EXPORTS KRB-PRIV, KRB-SAFE, KRB-SAFE-BODY, - KRB5SignedPath, - KRB5SignedPathData, - KRB5SignedPathPrincipals, KerberosString, KerberosTime, KrbCredInfo, @@ -713,24 +710,6 @@ PA-S4U2Self ::= SEQUENCE { auth[3] GeneralString } --- never encoded on the wire, just used to checksum over -KRB5SignedPathData ::= SEQUENCE { - client[0] Principal OPTIONAL, - authtime[1] KerberosTime, - delegated[2] Principals OPTIONAL, - method_data[3] METHOD-DATA OPTIONAL -} - -KRB5SignedPath ::= SEQUENCE { - -- DERcoded KRB5SignedPathData - -- krbtgt key (etype), KeyUsage = XXX - etype[0] ENCTYPE, - cksum[1] Checksum, - -- srvs delegated though - delegated[2] Principals OPTIONAL, - method_data[3] METHOD-DATA OPTIONAL -} - AD-LoginAlias ::= SEQUENCE { -- ad-type number TBD -- login-alias [0] PrincipalName, checksum [1] Checksum -- 2.25.1 From 24c5c89e7150ce6b0ed24bccba192352223f3bfe Mon Sep 17 00:00:00 2001 From: Isaac Boukris Date: Fri, 13 Aug 2021 12:44:37 +0300 Subject: [PATCH 121/159] kdc: sign ticket using Windows PAC Split Windows PAC signing and verification logic, as the signing has to be when the ticket is ready. Create sign and verify the PAC KDC signature if the plugin did not, allowing for S4U2Proxy to work, instead of KRB5SignedPath. Use the header key to verify PAC server signature, as the same key used to encrypt/decrypt the ticket should be used for PAC server signature, like U2U tickets are signed witht the tgt session-key and not with the longterm key, and so krbtgt should be no different and the header key should be used. Lookup the delegated client in DB instead of passing the delegator DB entry. Add PAC ticket-signatures and related functions. Note: due to the change from KRB5SignedPath to PAC, S4U2Proxy requests against new KDC will not work if the evidence ticket was acquired from an old KDC, and vide versa. Closes: #767 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 [jsutton@samba.org Backported from Heimdal commit 2ffaba9401d19c718764d4bd24180960290238e9 - Removed tests - Adapted to Samba's version of Heimdal - Addressed build failures with -O3 - Added knownfails ] Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett [abartlet@samba.org backported from commit d7b03394a9012960d71489e775d40d10fd6f5232 due to conflicts in knownfail due to missing tests that crash the MIT KDC] --- selftest/knownfail_heimdal_kdc | 54 +-- source4/heimdal/kdc/kerberos5.c | 69 +--- source4/heimdal/kdc/krb5tgs.c | 354 +++++++++--------- source4/heimdal/kdc/windc.c | 15 +- source4/heimdal/kdc/windc_plugin.h | 5 +- source4/heimdal/lib/krb5/authdata.c | 124 +++++++ source4/heimdal/lib/krb5/pac.c | 376 ++++++++++++++++++-- source4/heimdal/lib/krb5/version-script.map | 5 + source4/heimdal_build/wscript_build | 2 +- source4/selftest/tests.py | 2 +- 10 files changed, 687 insertions(+), 319 deletions(-) create mode 100644 source4/heimdal/lib/krb5/authdata.c diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index f0263133eee..4ec682c01d0 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -1,7 +1,7 @@ # # We expect all the MIT specific compatability tests to fail on heimdal # kerberos -^samba.tests.krb5.compatability_tests.samba.tests.krb5.compatability_tests.SimpleKerberosTests.test_mit_(?!ticket_signature) +^samba.tests.krb5.compatability_tests.samba.tests.krb5.compatability_tests.SimpleKerberosTests.test_mit_ # # Heimdal currently fails the following MS-KILE client principal lookup # tests @@ -122,14 +122,9 @@ ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_tgs_outer_no_sname.ad_dc ^samba.tests.krb5.fast_tests.samba.tests.krb5.fast_tests.FAST_Tests.test_fast_no_sname.ad_dc # -# Heimdal currently does not generate ticket signatures -# -^samba.tests.krb5.compatability_tests.samba.tests.krb5.compatability_tests.SimpleKerberosTests.test_heimdal_ticket_signature -# # S4U tests # ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_rbcd_old_checksum -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_missing_client_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_service_pac ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_unkeyed_client_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_unkeyed_service_checksum @@ -143,60 +138,13 @@ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_unkeyed_service_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_client_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_service_checksum -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_client_not_delegated ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_forwardable ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_nonempty_allowed -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_trusted_empty_allowed -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_trusted_nonempty_allowed -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_without_forwardable # # RODC tests # ^samba.tests.krb5.rodc_tests.samba.tests.krb5.rodc_tests.RodcKerberosTests.test_rodc_ticket_signature # -# 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.STEP1 remote.pac verification.ad_dc_ntvfs:local -^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc:local -^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc_ntvfs:local -^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc:local -^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc_ntvfs:local -^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc:local -^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc_ntvfs:local -^samba4.blackbox.pkinit_pac.netr-mem-arcfour.s4u2proxy-arcfour.ad_dc:local -^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 -# # The lack of KRB5SignedPath means we no longer return # KRB5KRB_ERR_RESPONSE_TOO_BIG in this specific case # diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c index a400b21a652..7b17d2539ce 100644 --- a/source4/heimdal/kdc/kerberos5.c +++ b/source4/heimdal/kdc/kerberos5.c @@ -1712,6 +1712,7 @@ _kdc_as_rep(krb5_context context, if (send_pac_p(context, req)) { krb5_pac p = NULL; krb5_data data; + uint16_t rodc_id; ret = _kdc_pac_generate(context, client, pk_reply_key, &p); if (ret) { @@ -1720,10 +1721,13 @@ _kdc_as_rep(krb5_context context, goto out; } if (p != NULL) { + rodc_id = server->entry.kvno >> 16; + ret = _krb5_pac_sign(context, p, et.authtime, client->entry.principal, &skey->key, /* Server key */ &skey->key, /* FIXME: should be krbtgt key */ + rodc_id, &data); krb5_pac_free(context, p); if (ret) { @@ -1732,9 +1736,7 @@ _kdc_as_rep(krb5_context context, goto out; } - ret = _kdc_tkt_add_if_relevant_ad(context, &et, - KRB5_AUTHDATA_WIN2K_PAC, - &data); + ret = _kdc_tkt_insert_pac(context, &et, &data); krb5_data_free(&data); if (ret) goto out; @@ -1888,64 +1890,3 @@ prepare_enc_data(krb5_context context, return TRUE; } - -/* - * Add the AuthorizationData `data´ of `type´ to the last element in - * the sequence of authorization_data in `tkt´ wrapped in an IF_RELEVANT - */ - -krb5_error_code -_kdc_tkt_add_if_relevant_ad(krb5_context context, - EncTicketPart *tkt, - int type, - const krb5_data *data) -{ - krb5_error_code ret; - size_t size = 0; - - if (tkt->authorization_data == NULL) { - tkt->authorization_data = calloc(1, sizeof(*tkt->authorization_data)); - if (tkt->authorization_data == NULL) { - krb5_set_error_message(context, ENOMEM, "out of memory"); - return ENOMEM; - } - } - - /* add the entry to the last element */ - { - AuthorizationData ad = { 0, NULL }; - AuthorizationDataElement ade; - - ade.ad_type = type; - ade.ad_data = *data; - - ret = add_AuthorizationData(&ad, &ade); - if (ret) { - krb5_set_error_message(context, ret, "add AuthorizationData failed"); - return ret; - } - - ade.ad_type = KRB5_AUTHDATA_IF_RELEVANT; - - ASN1_MALLOC_ENCODE(AuthorizationData, - ade.ad_data.data, ade.ad_data.length, - &ad, &size, ret); - free_AuthorizationData(&ad); - if (ret) { - krb5_set_error_message(context, ret, "ASN.1 encode of " - "AuthorizationData failed"); - return ret; - } - if (ade.ad_data.length != size) - krb5_abortx(context, "internal asn.1 encoder error"); - - ret = add_AuthorizationData(tkt->authorization_data, &ade); - der_free_octet_string(&ade.ad_data); - if (ret) { - krb5_set_error_message(context, ret, "add AuthorizationData failed"); - return ret; - } - } - - return 0; -} diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c index c6bab82f517..3b35b828402 100644 --- a/source4/heimdal/kdc/krb5tgs.c +++ b/source4/heimdal/kdc/krb5tgs.c @@ -59,85 +59,64 @@ check_PAC(krb5_context context, hdb_entry_ex *client, hdb_entry_ex *server, hdb_entry_ex *krbtgt, + hdb_entry_ex *ticket_server, const EncryptionKey *server_check_key, - const EncryptionKey *server_sign_key, - const EncryptionKey *krbtgt_sign_key, + const EncryptionKey *krbtgt_check_key, EncTicketPart *tkt, - krb5_data *rspac, - int *signedpath) + krb5_boolean *kdc_issued, + krb5_pac *ppac) { - AuthorizationData *ad = tkt->authorization_data; - unsigned i, j; + krb5_pac pac = NULL; krb5_error_code ret; + krb5_boolean signedticket; - if (ad == NULL || ad->len == 0) - return 0; - - for (i = 0; i < ad->len; i++) { - AuthorizationData child; - - if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT) - continue; - - ret = decode_AuthorizationData(ad->val[i].ad_data.data, - ad->val[i].ad_data.length, - &child, - NULL); - if (ret) { - krb5_set_error_message(context, ret, "Failed to decode " - "IF_RELEVANT with %d", ret); - return ret; - } - for (j = 0; j < child.len; j++) { - - if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) { - int signed_pac = 0; - krb5_pac pac; - - /* Found PAC */ - ret = krb5_pac_parse(context, - child.val[j].ad_data.data, - child.val[j].ad_data.length, - &pac); - free_AuthorizationData(&child); - if (ret) - return ret; + *kdc_issued = FALSE; + *ppac = NULL; - ret = krb5_pac_verify(context, pac, tkt->authtime, - client_principal, - server_check_key, NULL); - if (ret) { - krb5_pac_free(context, pac); - return ret; - } + ret = _krb5_kdc_pac_ticket_parse(context, tkt, &signedticket, &pac); + if (ret || pac == NULL) + return ret; - ret = _kdc_pac_verify(context, client_principal, - delegated_proxy_principal, - client, server, krbtgt, &pac, &signed_pac); - if (ret) { - krb5_pac_free(context, pac); - return ret; - } + /* Verify the server signature. */ + ret = krb5_pac_verify(context, pac, tkt->authtime, client_principal, + server_check_key, NULL); + if (ret) { + krb5_pac_free(context, pac); + return ret; + } - /* - * Only re-sign PAC if we could verify it with the PAC - * function. The no-verify case happens when we get in - * a PAC from cross realm from a Windows domain and - * that there is no PAC verification function. - */ - if (signed_pac) { - *signedpath = 1; - ret = _krb5_pac_sign(context, pac, tkt->authtime, - client_principal, - server_sign_key, krbtgt_sign_key, rspac); - } + /* Verify the KDC signatures. */ + ret = _kdc_pac_verify(context, client_principal, delegated_proxy_principal, + client, server, krbtgt, &pac); + if (ret == KRB5_PLUGIN_NO_HANDLE) { + /* + * We can't verify the KDC signatures if the ticket was issued by + * another realm's KDC. + */ + if (krb5_realm_compare(context, server->entry.principal, + ticket_server->entry.principal)) { + ret = krb5_pac_verify(context, pac, 0, NULL, NULL, + krbtgt_check_key); + if (ret) { krb5_pac_free(context, pac); - return ret; } } - free_AuthorizationData(&child); + /* Discard the PAC if the plugin didn't handle it */ + krb5_pac_free(context, pac); + ret = krb5_pac_init(context, &pac); + if (ret) + return ret; + } else if (ret) { + krb5_pac_free(context, pac); + return ret; } + + *kdc_issued = signedticket || + krb5_principal_is_krbtgt(context, + ticket_server->entry.principal); + *ppac = pac; + return 0; } @@ -499,11 +478,12 @@ static krb5_error_code tgs_make_reply(krb5_context context, krb5_kdc_configuration *config, KDC_REQ_BODY *b, - krb5_const_principal tgt_name, + krb5_principal tgt_name, const EncTicketPart *tgt, const krb5_keyblock *replykey, int rk_is_subkey, const EncryptionKey *serverkey, + const EncryptionKey *krbtgtkey, const krb5_keyblock *sessionkey, krb5_kvno kvno, AuthorizationData *auth_data, @@ -513,8 +493,9 @@ tgs_make_reply(krb5_context context, hdb_entry_ex *client, krb5_principal client_principal, hdb_entry_ex *krbtgt, - krb5_enctype krbtgt_etype, - const krb5_data *rspac, + krb5_pac mspac, + uint16_t rodc_id, + krb5_boolean add_ticket_sig, const METHOD_DATA *enc_pa_data, const char **e_text, krb5_data *reply) @@ -647,17 +628,6 @@ tgs_make_reply(krb5_context context, if (!server->entry.flags.proxiable) et.flags.proxiable = 0; - if(rspac->length) { - /* - * No not need to filter out the any PAC from the - * auth_data since it's signed by the KDC. - */ - ret = _kdc_tkt_add_if_relevant_ad(context, &et, - KRB5_AUTHDATA_WIN2K_PAC, rspac); - if (ret) - goto out; - } - if (auth_data) { unsigned int i = 0; @@ -724,6 +694,11 @@ tgs_make_reply(krb5_context context, is_weak = 1; } + /* The PAC should be the last change to the ticket. */ + ret = _krb5_kdc_pac_sign_ticket(context, mspac, tgt_name, serverkey, + krbtgtkey, rodc_id, add_ticket_sig, &et); + if (ret) + goto out; /* It is somewhat unclear where the etype in the following encryption should come from. What we have is a session @@ -910,6 +885,7 @@ tgs_parse_request(krb5_context context, int **cusec, AuthorizationData **auth_data, krb5_keyblock **replykey, + Key **header_key, int *rk_is_subkey) { static char failed[] = ""; @@ -1047,6 +1023,8 @@ tgs_parse_request(krb5_context context, goto out; } + *header_key = tkey; + { krb5_authenticator auth; @@ -1236,6 +1214,57 @@ eout: return ENOMEM; } +static krb5_error_code +db_fetch_client(krb5_context context, + krb5_kdc_configuration *config, + int flags, + krb5_principal cp, + const char *cpn, + const char *krbtgt_realm, + HDB **clientdb, + hdb_entry_ex **client_out) +{ + krb5_error_code ret; + hdb_entry_ex *client = NULL; + + *client_out = NULL; + + ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags, + NULL, clientdb, &client); + if (ret == HDB_ERR_NOT_FOUND_HERE) { + /* + * This is OK, we are just trying to find out if they have + * been disabled or deleted in the meantime; missing secrets + * are OK. + */ + } else if (ret) { + /* + * If the client belongs to the same realm as our TGS, it + * should exist in the local database. + */ + const char *msg; + + if (strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) { + if (ret == HDB_ERR_NOENTRY) + ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + kdc_log(context, config, 4, "Client no longer in database: %s", cpn); + return ret; + } + + msg = krb5_get_error_message(context, ret); + kdc_log(context, config, 4, "Client not found in database: %s", msg); + krb5_free_error_message(context, msg); + } else if (client->entry.flags.invalid || !client->entry.flags.client) { + kdc_log(context, config, 4, "Client has invalid bit set"); + _kdc_free_ent(context, client); + return KRB5KDC_ERR_POLICY; + } + + *client_out = client; + + return 0; +} + static krb5_error_code tgs_build_reply(krb5_context context, krb5_kdc_configuration *config, @@ -1243,6 +1272,7 @@ tgs_build_reply(krb5_context context, KDC_REQ_BODY *b, hdb_entry_ex *krbtgt, krb5_enctype krbtgt_etype, + Key *tkey_check, const krb5_keyblock *replykey, int rk_is_subkey, krb5_ticket *ticket, @@ -1263,7 +1293,9 @@ tgs_build_reply(krb5_context context, const EncryptionKey *ekey; krb5_keyblock sessionkey; krb5_kvno kvno; - krb5_data rspac; + krb5_pac mspac = NULL; + uint16_t rodc_id; + krb5_boolean add_ticket_sig = FALSE; hdb_entry_ex *krbtgt_out = NULL; @@ -1274,15 +1306,13 @@ tgs_build_reply(krb5_context context, int nloop = 0; EncTicketPart adtkt; char opt_str[128]; - int signedpath = 0; + krb5_boolean kdc_issued = FALSE; - Key *tkey_check; Key *tkey_sign; int flags = HDB_F_FOR_TGS_REQ; memset(&sessionkey, 0, sizeof(sessionkey)); memset(&adtkt, 0, sizeof(adtkt)); - krb5_data_zero(&rspac); memset(&enc_pa_data, 0, sizeof(enc_pa_data)); s = b->sname; @@ -1517,18 +1547,6 @@ server_lookup: * backward. */ - /* - * Validate authoriation data - */ - - ret = hdb_enctype2key(context, &krbtgt->entry, - krbtgt_etype, &tkey_check); - if(ret) { - kdc_log(context, config, 0, - "Failed to find key for krbtgt PAC check"); - goto out; - } - /* Now refetch the primary krbtgt, and get the current kvno (the * sign check may have been on an old kvno, and the server may * have been an incoming trust) */ @@ -1589,41 +1607,14 @@ server_lookup: goto out; } - ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags, - NULL, &clientdb, &client); - if(ret == HDB_ERR_NOT_FOUND_HERE) { - /* This is OK, we are just trying to find out if they have - * been disabled or deleted in the meantime, missing secrets - * is OK */ - } else if(ret){ - const char *krbtgt_realm, *msg; - - /* - * If the client belongs to the same realm as our krbtgt, it - * should exist in the local database. - * - */ - - krbtgt_realm = krb5_principal_get_realm(context, krbtgt_out->entry.principal); - - if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) { - if (ret == HDB_ERR_NOENTRY) - ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; - kdc_log(context, config, 1, "Client no longer in database: %s", - cpn); - goto out; - } - - msg = krb5_get_error_message(context, ret); - kdc_log(context, config, 1, "Client not found in database: %s", msg); - krb5_free_error_message(context, msg); - } + ret = db_fetch_client(context, config, flags, cp, cpn, + krb5_principal_get_realm(context, krbtgt_out->entry.principal), + &clientdb, &client); + if (ret) + goto out; - ret = check_PAC(context, config, cp, NULL, - client, server, krbtgt, - &tkey_check->key, - ekey, &tkey_sign->key, - tgt, &rspac, &signedpath); + ret = check_PAC(context, config, cp, NULL, client, server, krbtgt, krbtgt, + &tkey_check->key, &tkey_check->key, tgt, &kdc_issued, &mspac); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, @@ -1760,27 +1751,15 @@ server_lookup: goto out; /* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */ - if(rspac.data) { - krb5_pac p = NULL; - krb5_data_free(&rspac); - ret = _kdc_pac_generate(context, s4u2self_impersonated_client, NULL, &p); + if (mspac) { + krb5_pac_free(context, mspac); + mspac = NULL; + ret = _kdc_pac_generate(context, s4u2self_impersonated_client, NULL, &mspac); if (ret) { kdc_log(context, config, 0, "PAC generation failed for -- %s", tpn); goto out; } - if (p != NULL) { - ret = _krb5_pac_sign(context, p, ticket->ticket.authtime, - s4u2self_impersonated_client->entry.principal, - ekey, &tkey_sign->key, - &rspac); - krb5_pac_free(context, p); - if (ret) { - kdc_log(context, config, 0, "PAC signing failed for -- %s", - tpn); - goto out; - } - } } /* @@ -1823,22 +1802,25 @@ server_lookup: && b->additional_tickets->len != 0 && b->kdc_options.enc_tkt_in_skey == 0) { - int ad_signedpath = 0; + hdb_entry_ex *adclient = NULL; + krb5_boolean ad_kdc_issued = FALSE; Key *clientkey; Ticket *t; /* - * Require that the KDC have issued the service's krbtgt (not - * self-issued ticket with kimpersonate(1). + * We require that the service's krbtgt has a PAC. */ - if (!signedpath) { + if (mspac == NULL) { ret = KRB5KDC_ERR_BADOPTION; kdc_log(context, config, 0, - "Constrained delegation done on service ticket %s/%s", + "Constrained delegation without PAC %s/%s", cpn, spn); goto out; } + krb5_pac_free(context, mspac); + mspac = NULL; + t = &b->additional_tickets->val[0]; ret = hdb_enctype2key(context, &client->entry, @@ -1902,19 +1884,32 @@ server_lookup: goto out; } - krb5_data_free(&rspac); + /* Try lookup the delegated client in DB */ + ret = db_fetch_client(context, config, flags, tp, tpn, + krb5_principal_get_realm(context, krbtgt_out->entry.principal), + NULL, &adclient); + if (ret) + goto out; + + if (adclient != NULL) { + ret = kdc_check_flags(context, config, + adclient, tpn, + server, spn, + FALSE); + if (ret) { + _kdc_free_ent(context, adclient); + goto out; + } + } /* - * generate the PAC for the user. - * * TODO: pass in t->sname and t->realm and build * a S4U_DELEGATION_INFO blob to the PAC. */ - ret = check_PAC(context, config, tp, dp, - client, server, krbtgt, - &clientkey->key, - ekey, &tkey_sign->key, - &adtkt, &rspac, &ad_signedpath); + ret = check_PAC(context, config, tp, dp, adclient, server, krbtgt, client, + &clientkey->key, &tkey_check->key, &adtkt, &ad_kdc_issued, &mspac); + if (adclient) + _kdc_free_ent(context, adclient); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, @@ -1925,13 +1920,12 @@ server_lookup: goto out; } - if (!ad_signedpath) { + if (mspac == NULL || !ad_kdc_issued) { ret = KRB5KDC_ERR_BADOPTION; kdc_log(context, config, 0, - "Ticket not signed with PAC nor SignedPath service %s failed " - "for delegation to %s for client %s (%s)" - "from %s", - spn, tpn, dpn, cpn, from); + "Ticket not signed with PAC; service %s failed for " + "for delegation to %s for client %s (%s) from %s; (%s).", + spn, tpn, dpn, cpn, from, mspac ? "Ticket unsigned" : "No PAC"); goto out; } @@ -2000,6 +1994,25 @@ server_lookup: } } + /* + * Only add ticket signature if the requested server is not krbtgt, and + * either the header server is krbtgt or, in the case of renewal/validation + * if it was signed with PAC ticket signature and we verified it. + * Currently Heimdal only allows renewal of krbtgt anyway but that might + * change one day (see issue #763) so make sure to check for it. + */ + + if (kdc_issued && + !krb5_principal_is_krbtgt(context, server->entry.principal)) + add_ticket_sig = TRUE; + + /* + * Active-Directory implementations use the high part of the kvno as the + * read-only-dc identifier, we need to embed it in the PAC KDC signatures. + */ + + rodc_id = krbtgt_out->entry.kvno >> 16; + /* * */ @@ -2012,6 +2025,7 @@ server_lookup: replykey, rk_is_subkey, ekey, + &tkey_sign->key, &sessionkey, kvno, *auth_data, @@ -2021,8 +2035,9 @@ server_lookup: client, cp, krbtgt_out, - krbtgt_etype, - &rspac, + mspac, + rodc_id, + add_ticket_sig, &enc_pa_data, e_text, reply); @@ -2035,7 +2050,6 @@ out: if (dpn) free(dpn); - krb5_data_free(&rspac); krb5_free_keyblock_contents(context, &sessionkey); if(krbtgt_out) _kdc_free_ent(context, krbtgt_out); @@ -2060,6 +2074,9 @@ out: free_EncTicketPart(&adtkt); + if (mspac) + krb5_pac_free(context, mspac); + return ret; } @@ -2080,6 +2097,7 @@ _kdc_tgs_rep(krb5_context context, krb5_error_code ret; int i = 0; const PA_DATA *tgs_req; + Key *header_key = NULL; hdb_entry_ex *krbtgt = NULL; krb5_ticket *ticket = NULL; @@ -2117,6 +2135,7 @@ _kdc_tgs_rep(krb5_context context, &csec, &cusec, &auth_data, &replykey, + &header_key, &rk_is_subkey); if (ret == HDB_ERR_NOT_FOUND_HERE) { /* kdc_log() is called in tgs_parse_request() */ @@ -2134,6 +2153,7 @@ _kdc_tgs_rep(krb5_context context, &req->req_body, krbtgt, krbtgt_etype, + header_key, replykey, rk_is_subkey, ticket, diff --git a/source4/heimdal/kdc/windc.c b/source4/heimdal/kdc/windc.c index fb1c8a6a993..43dc89e2bc0 100644 --- a/source4/heimdal/kdc/windc.c +++ b/source4/heimdal/kdc/windc.c @@ -77,8 +77,14 @@ _kdc_pac_generate(krb5_context context, krb5_pac *pac) { *pac = NULL; - if (windcft == NULL) + if (krb5_config_get_bool_default(context, NULL, FALSE, "realms", + client->entry.principal->realm, + "disable_pac", NULL)) return 0; + if (windcft == NULL) { + return krb5_pac_init(context, pac); + } + if (windcft->pac_pk_generate != NULL && pk_reply_key != NULL) return (windcft->pac_pk_generate)(windcctx, context, client, pk_reply_key, pac); @@ -92,20 +98,17 @@ _kdc_pac_verify(krb5_context context, hdb_entry_ex *client, hdb_entry_ex *server, hdb_entry_ex *krbtgt, - krb5_pac *pac, - int *verified) + krb5_pac *pac) { krb5_error_code ret; if (windcft == NULL) - return 0; + return KRB5_PLUGIN_NO_HANDLE; ret = windcft->pac_verify(windcctx, context, client_principal, delegated_proxy_principal, client, server, krbtgt, pac); - if (ret == 0) - *verified = 1; return ret; } diff --git a/source4/heimdal/kdc/windc_plugin.h b/source4/heimdal/kdc/windc_plugin.h index bf90826cb06..dda258da3d1 100644 --- a/source4/heimdal/kdc/windc_plugin.h +++ b/source4/heimdal/kdc/windc_plugin.h @@ -43,8 +43,9 @@ * krb5_pac_init and fill in the PAC structure for the principal using * krb5_pac_add_buffer. * - * The PAC verify function should verify all components in the PAC - * using krb5_pac_get_types and krb5_pac_get_buffer for all types. + * The PAC verify function should verify the PAC KDC signatures by fetching + * the right KDC key and calling krb5_pac_verify() with that KDC key. + * Optionally, update the PAC buffers upon success. * * Check client access function check if the client is authorized. */ diff --git a/source4/heimdal/lib/krb5/authdata.c b/source4/heimdal/lib/krb5/authdata.c new file mode 100644 index 00000000000..ac426618f6e --- /dev/null +++ b/source4/heimdal/lib/krb5/authdata.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1997-2021 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * Copyright (c) 2021 Isaac Boukris + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/* + * Add the AuthorizationData `data´ of `type´ to the last element in + * the sequence of authorization_data in `tkt´ wrapped in an IF_RELEVANT + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_kdc_tkt_add_if_relevant_ad(krb5_context context, + EncTicketPart *tkt, + int type, + const krb5_data *data) +{ + krb5_error_code ret; + size_t size = 0; + + if (tkt->authorization_data == NULL) { + tkt->authorization_data = calloc(1, sizeof(*tkt->authorization_data)); + if (tkt->authorization_data == NULL) { + return krb5_enomem(context); + } + } + + /* add the entry to the last element */ + { + AuthorizationData ad = { 0, NULL }; + AuthorizationDataElement ade; + + ade.ad_type = type; + ade.ad_data = *data; + + ret = add_AuthorizationData(&ad, &ade); + if (ret) { + krb5_set_error_message(context, ret, "add AuthorizationData failed"); + return ret; + } + + ade.ad_type = KRB5_AUTHDATA_IF_RELEVANT; + + ASN1_MALLOC_ENCODE(AuthorizationData, + ade.ad_data.data, ade.ad_data.length, + &ad, &size, ret); + free_AuthorizationData(&ad); + if (ret) { + krb5_set_error_message(context, ret, "ASN.1 encode of " + "AuthorizationData failed"); + return ret; + } + if (ade.ad_data.length != size) + krb5_abortx(context, "internal asn.1 encoder error"); + + ret = add_AuthorizationData(tkt->authorization_data, &ade); + der_free_octet_string(&ade.ad_data); + if (ret) { + krb5_set_error_message(context, ret, "add AuthorizationData failed"); + return ret; + } + } + + return 0; +} + +/* + * Insert a PAC wrapped in AD-IF-RELEVANT container as the first AD element, + * as some clients such as Windows may fail to parse it otherwise. + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_kdc_tkt_insert_pac(krb5_context context, + EncTicketPart *tkt, + const krb5_data *data) +{ + AuthorizationDataElement ade; + unsigned int i; + krb5_error_code ret; + + ret = _kdc_tkt_add_if_relevant_ad(context, tkt, KRB5_AUTHDATA_WIN2K_PAC, + data); + if (ret) + return ret; + + heim_assert(tkt->authorization_data->len != 0, "No authorization_data!"); + ade = tkt->authorization_data->val[tkt->authorization_data->len - 1]; + for (i = 0; i < tkt->authorization_data->len - 1; i++) { + tkt->authorization_data->val[i + 1] = tkt->authorization_data->val[i]; + } + tkt->authorization_data->val[0] = ade; + + return 0; +} diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c index 26aae107b32..eec1e84c7bd 100644 --- a/source4/heimdal/lib/krb5/pac.c +++ b/source4/heimdal/lib/krb5/pac.c @@ -53,6 +53,8 @@ struct krb5_pac_data { struct PAC_INFO_BUFFER *server_checksum; struct PAC_INFO_BUFFER *privsvr_checksum; struct PAC_INFO_BUFFER *logon_name; + struct PAC_INFO_BUFFER *ticket_checksum; + krb5_data ticket_sign_data; }; #define PAC_ALIGNMENT 8 @@ -64,6 +66,7 @@ struct krb5_pac_data { #define PAC_PRIVSVR_CHECKSUM 7 #define PAC_LOGON_NAME 10 #define PAC_CONSTRAINED_DELEGATION 11 +#define PAC_TICKET_CHECKSUM 16 #define CHECK(r,f,l) \ do { \ @@ -142,13 +145,13 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len, CHECK(ret, krb5_ret_uint32(sp, &tmp2), out); if (tmp < 1) { ret = EINVAL; /* Too few buffers */ - krb5_set_error_message(context, ret, N_("PAC have too few buffer", "")); + krb5_set_error_message(context, ret, N_("PAC has too few buffers", "")); goto out; } if (tmp2 != 0) { ret = EINVAL; /* Wrong version */ krb5_set_error_message(context, ret, - N_("PAC have wrong version %d", ""), + N_("PAC has wrong version %d", ""), (int)tmp2); goto out; } @@ -191,7 +194,7 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len, if (p->pac->buffers[i].offset_lo > len) { ret = EINVAL; krb5_set_error_message(context, ret, - N_("PAC offset off end", "")); + N_("PAC offset overflow", "")); goto out; } if (p->pac->buffers[i].offset_lo < header_end) { @@ -204,7 +207,7 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len, } if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){ ret = EINVAL; - krb5_set_error_message(context, ret, N_("PAC length off end", "")); + krb5_set_error_message(context, ret, N_("PAC length overflow", "")); goto out; } @@ -213,7 +216,7 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len, if (p->server_checksum) { ret = EINVAL; krb5_set_error_message(context, ret, - N_("PAC have two server checksums", "")); + N_("PAC has multiple server checksums", "")); goto out; } p->server_checksum = &p->pac->buffers[i]; @@ -221,7 +224,7 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len, if (p->privsvr_checksum) { ret = EINVAL; krb5_set_error_message(context, ret, - N_("PAC have two KDC checksums", "")); + N_("PAC has multiple KDC checksums", "")); goto out; } p->privsvr_checksum = &p->pac->buffers[i]; @@ -229,10 +232,18 @@ krb5_pac_parse(krb5_context context, const void *ptr, size_t len, if (p->logon_name) { ret = EINVAL; krb5_set_error_message(context, ret, - N_("PAC have two logon names", "")); + N_("PAC has multiple logon names", "")); goto out; } p->logon_name = &p->pac->buffers[i]; + } else if (p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) { + if (p->ticket_checksum) { + ret = EINVAL; + krb5_set_error_message(context, ret, + N_("PAC has multiple ticket checksums", "")); + goto out; + } + p->ticket_checksum = &p->pac->buffers[i]; } } @@ -425,6 +436,7 @@ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_pac_free(krb5_context context, krb5_pac pac) { krb5_data_free(&pac->data); + krb5_data_free(&pac->ticket_sign_data); free(pac->pac); free(pac); } @@ -444,6 +456,7 @@ verify_checksum(krb5_context context, uint32_t type; krb5_error_code ret; Checksum cksum; + size_t cksumsize; memset(&cksum, 0, sizeof(cksum)); @@ -456,8 +469,17 @@ verify_checksum(krb5_context context, CHECK(ret, krb5_ret_uint32(sp, &type), out); cksum.cksumtype = type; - cksum.checksum.length = - sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR); + + ret = krb5_checksumsize(context, type, &cksumsize); + if (ret) + goto out; + + /* Allow for RODCIdentifier trailer, see MS-PAC 2.8 */ + if (cksumsize > (sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR))) { + ret = EINVAL; + goto out; + } + cksum.checksum.length = cksumsize; cksum.checksum.data = malloc(cksum.checksum.length); if (cksum.checksum.data == NULL) { ret = krb5_enomem(context); @@ -804,7 +826,6 @@ out: return ret; } - /** * Verify the PAC. * @@ -844,18 +865,22 @@ krb5_pac_verify(krb5_context context, return EINVAL; } - ret = verify_logonname(context, - pac->logon_name, - &pac->data, - authtime, - principal); - if (ret) - return ret; + if (principal != NULL) { + ret = verify_logonname(context, pac->logon_name, &pac->data, authtime, + principal); + if (ret) + return ret; + } + + if (pac->server_checksum->buffersize < 4 || + 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) { krb5_data *copy; @@ -897,6 +922,20 @@ krb5_pac_verify(krb5_context context, privsvr); if (ret) return ret; + + if (pac->ticket_sign_data.length != 0) { + if (pac->ticket_checksum == NULL) { + krb5_set_error_message(context, EINVAL, + "PAC missing ticket checksum"); + return EINVAL; + } + + ret = verify_checksum(context, pac->ticket_checksum, &pac->data, + pac->ticket_sign_data.data, + pac->ticket_sign_data.length, privsvr); + if (ret) + return ret; + } } return 0; @@ -965,13 +1004,14 @@ _krb5_pac_sign(krb5_context context, krb5_principal principal, const krb5_keyblock *server_key, const krb5_keyblock *priv_key, + uint16_t rodc_id, krb5_data *data) { krb5_error_code ret; krb5_storage *sp = NULL, *spdata = NULL; uint32_t end; size_t server_size, priv_size; - uint32_t server_offset = 0, priv_offset = 0; + uint32_t server_offset = 0, priv_offset = 0, ticket_offset = 0; uint32_t server_cksumtype = 0, priv_cksumtype = 0; int num = 0; size_t i; @@ -985,9 +1025,9 @@ _krb5_pac_sign(krb5_context context, p->server_checksum = &p->pac->buffers[i]; } if (p->server_checksum != &p->pac->buffers[i]) { - ret = EINVAL; + ret = KRB5KDC_ERR_BADOPTION; krb5_set_error_message(context, ret, - N_("PAC have two server checksums", "")); + N_("PAC has multiple server checksums", "")); goto out; } } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { @@ -995,9 +1035,9 @@ _krb5_pac_sign(krb5_context context, p->privsvr_checksum = &p->pac->buffers[i]; } if (p->privsvr_checksum != &p->pac->buffers[i]) { - ret = EINVAL; + ret = KRB5KDC_ERR_BADOPTION; krb5_set_error_message(context, ret, - N_("PAC have two KDC checksums", "")); + N_("PAC has multiple KDC checksums", "")); goto out; } } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { @@ -1005,9 +1045,19 @@ _krb5_pac_sign(krb5_context context, p->logon_name = &p->pac->buffers[i]; } if (p->logon_name != &p->pac->buffers[i]) { - ret = EINVAL; + ret = KRB5KDC_ERR_BADOPTION; + krb5_set_error_message(context, ret, + N_("PAC has multiple logon names", "")); + goto out; + } + } else if (p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) { + if (p->ticket_checksum == NULL) { + p->ticket_checksum = &p->pac->buffers[i]; + } + if (p->ticket_checksum != &p->pac->buffers[i]) { + ret = KRB5KDC_ERR_BADOPTION; krb5_set_error_message(context, ret, - N_("PAC have two logon names", "")); + N_("PAC has multiple ticket checksums", "")); goto out; } } @@ -1019,6 +1069,8 @@ _krb5_pac_sign(krb5_context context, num++; if (p->privsvr_checksum == NULL) num++; + if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) + num++; if (num) { void *ptr; @@ -1044,6 +1096,11 @@ _krb5_pac_sign(krb5_context context, memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum)); p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM; } + if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) { + p->ticket_checksum = &p->pac->buffers[p->pac->numbuffers++]; + memset(p->ticket_checksum, 0, sizeof(*p->privsvr_checksum)); + p->ticket_checksum->type = PAC_TICKET_CHECKSUM; + } } /* Calculate LOGON NAME */ @@ -1055,6 +1112,7 @@ _krb5_pac_sign(krb5_context context, ret = pac_checksum(context, server_key, &server_cksumtype, &server_size); if (ret) goto out; + ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size); if (ret) goto out; @@ -1095,10 +1153,24 @@ _krb5_pac_sign(krb5_context context, priv_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) { + len += sizeof(rodc_id); + CHECK(ret, fill_zeros(context, spdata, sizeof(rodc_id)), out); + } + } else if (p->ticket_sign_data.length != 0 && + p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) { + len = priv_size + 4; + ticket_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) { + len += sizeof(rodc_id); + CHECK(ret, krb5_store_uint16(spdata, 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) { - ret = EINVAL; + ret = KRB5KDC_ERR_BADOPTION; goto out; } } else { @@ -1156,6 +1228,16 @@ _krb5_pac_sign(krb5_context context, } /* sign */ + if (p->ticket_sign_data.length) { + ret = create_checksum(context, priv_key, priv_cksumtype, + p->ticket_sign_data.data, + p->ticket_sign_data.length, + (char *)d.data + ticket_offset, priv_size); + if (ret) { + krb5_data_free(&d); + goto out; + } + } ret = create_checksum(context, server_key, server_cksumtype, d.data, d.length, (char *)d.data + server_offset, server_size); @@ -1171,6 +1253,32 @@ _krb5_pac_sign(krb5_context context, goto out; } + if (rodc_id != 0) { + krb5_data rd; + krb5_storage *rs = krb5_storage_emem(); + if (rs == NULL) { + krb5_data_free(&d); + ret = krb5_enomem(context); + goto out; + } + krb5_storage_set_flags(rs, KRB5_STORAGE_BYTEORDER_LE); + ret = krb5_store_uint16(rs, rodc_id); + if (ret) { + krb5_storage_free(rs); + krb5_data_free(&d); + goto out; + } + ret = krb5_storage_to_data(rs, &rd); + krb5_storage_free(rs); + if (ret) { + krb5_data_free(&d); + 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); + } + /* done */ *data = d; @@ -1187,3 +1295,221 @@ out: krb5_storage_free(spdata); return ret; } + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_pac_get_kdc_checksum_info(krb5_context context, + krb5_pac pac, + krb5_cksumtype *cstype, + uint16_t *rodc_id) +{ + krb5_error_code ret; + krb5_storage *sp = NULL; + const struct PAC_INFO_BUFFER *sig; + size_t cksumsize, prefix; + uint32_t type = 0; + + *cstype = 0; + *rodc_id = 0; + + sig = pac->privsvr_checksum; + if (sig == NULL) { + krb5_set_error_message(context, KRB5KDC_ERR_BADOPTION, + "PAC missing kdc checksum"); + return KRB5KDC_ERR_BADOPTION; + } + + sp = krb5_storage_from_mem((char *)pac->data.data + sig->offset_lo, + sig->buffersize); + if (sp == NULL) + return krb5_enomem(context); + + krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); + + ret = krb5_ret_uint32(sp, &type); + if (ret) + goto out; + + ret = krb5_checksumsize(context, type, &cksumsize); + if (ret) + goto out; + + prefix = krb5_storage_seek(sp, 0, SEEK_CUR); + + if ((sig->buffersize - prefix) >= cksumsize + 2) { + krb5_storage_seek(sp, cksumsize, SEEK_CUR); + ret = krb5_ret_uint16(sp, rodc_id); + if (ret) + goto out; + } + + *cstype = type; + +out: + krb5_storage_free(sp); + + return ret; +} + +static unsigned char single_zero = '\0'; +static krb5_data single_zero_pac = { 1, &single_zero }; + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_kdc_pac_ticket_parse(krb5_context context, + EncTicketPart *tkt, + krb5_boolean *signedticket, + krb5_pac *ppac) +{ + AuthorizationData *ad = tkt->authorization_data; + krb5_boolean pac_found = FALSE; + krb5_pac pac = NULL; + unsigned i, j; + size_t len = 0; + krb5_error_code ret; + + *signedticket = FALSE; + *ppac = NULL; + + if (ad == NULL || ad->len == 0) + return 0; + + for (i = 0; i < ad->len; i++) { + AuthorizationData child; + + if (ad->val[i].ad_type == KRB5_AUTHDATA_WIN2K_PAC) + return KRB5KDC_ERR_BADOPTION; + + if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT) + continue; + + ret = decode_AuthorizationData(ad->val[i].ad_data.data, + ad->val[i].ad_data.length, + &child, + NULL); + if (ret) { + krb5_set_error_message(context, ret, "Failed to decode " + "AD-IF-RELEVANT with %d", ret); + return ret; + } + + for (j = 0; j < child.len; j++) { + if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) { + krb5_data adifr_data = ad->val[i].ad_data; + krb5_data pac_data = child.val[j].ad_data; + krb5_data recoded_adifr; + + if (pac_found) { + free_AuthorizationData(&child); + return KRB5KDC_ERR_BADOPTION; + } + pac_found = TRUE; + + ret = krb5_pac_parse(context, + pac_data.data, + pac_data.length, + &pac); + if (ret) { + free_AuthorizationData(&child); + return ret; + } + + if (pac->ticket_checksum == NULL) { + free_AuthorizationData(&child); + *ppac = pac; + continue; + } + + /* + * Encode the ticket with the PAC replaced with a single zero + * byte, to be used as input data to the ticket signature. + */ + + child.val[j].ad_data = single_zero_pac; + + ASN1_MALLOC_ENCODE(AuthorizationData, recoded_adifr.data, + recoded_adifr.length, &child, &len, ret); + if (recoded_adifr.length != len) + krb5_abortx(context, "Internal error in ASN.1 encoder"); + + child.val[j].ad_data = pac_data; + free_AuthorizationData(&child); + + if (ret) { + krb5_pac_free(context, pac); + return ret; + } + + ad->val[i].ad_data = recoded_adifr; + + ASN1_MALLOC_ENCODE(EncTicketPart, + pac->ticket_sign_data.data, + pac->ticket_sign_data.length, tkt, &len, + ret); + if(pac->ticket_sign_data.length != len) + krb5_abortx(context, "Internal error in ASN.1 encoder"); + + ad->val[i].ad_data = adifr_data; + krb5_data_free(&recoded_adifr); + + if (ret) { + krb5_pac_free(context, pac); + return ret; + } + + *signedticket = TRUE; + *ppac = pac; + } + } + free_AuthorizationData(&child); + } + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_kdc_pac_sign_ticket(krb5_context context, + const krb5_pac pac, + krb5_principal client, + const krb5_keyblock *server_key, + const krb5_keyblock *kdc_key, + uint16_t rodc_id, + krb5_boolean add_ticket_sig, + EncTicketPart *tkt) +{ + krb5_error_code ret; + krb5_data tkt_data; + krb5_data rspac; + + krb5_data_zero(&rspac); + krb5_data_zero(&tkt_data); + + krb5_data_free(&pac->ticket_sign_data); + + if (add_ticket_sig) { + size_t len = 0; + + ret = _kdc_tkt_insert_pac(context, tkt, &single_zero_pac); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(EncTicketPart, tkt_data.data, tkt_data.length, + tkt, &len, ret); + if(tkt_data.length != len) + krb5_abortx(context, "Internal error in ASN.1 encoder"); + if (ret) + return ret; + + ret = remove_AuthorizationData(tkt->authorization_data, 0); + if (ret) { + krb5_data_free(&tkt_data); + return ret; + } + + pac->ticket_sign_data = tkt_data; + } + + ret = _krb5_pac_sign(context, pac, tkt->authtime, client, server_key, + kdc_key, rodc_id, &rspac); + if (ret) + return ret; + + return _kdc_tkt_insert_pac(context, tkt, &rspac); +} diff --git a/source4/heimdal/lib/krb5/version-script.map b/source4/heimdal/lib/krb5/version-script.map index b95ba92f4f6..e04e02aace9 100644 --- a/source4/heimdal/lib/krb5/version-script.map +++ b/source4/heimdal/lib/krb5/version-script.map @@ -751,6 +751,11 @@ HEIMDAL_KRB5_2.0 { _krb5_get_host_realm_int; _krb5_get_int; _krb5_pac_sign; + _krb5_kdc_pac_sign_ticket; + _krb5_kdc_pac_ticket_parse; + _krb5_pac_get_kdc_checksum_info; + _kdc_tkt_insert_pac; + _kdc_tkt_add_if_relevant_ad; _krb5_parse_moduli; _krb5_pk_kdf; _krb5_pk_load_id; diff --git a/source4/heimdal_build/wscript_build b/source4/heimdal_build/wscript_build index 09c525c2957..39e3f5d56e8 100644 --- a/source4/heimdal_build/wscript_build +++ b/source4/heimdal_build/wscript_build @@ -617,7 +617,7 @@ if not bld.CONFIG_SET("USING_SYSTEM_KRB5"): KRB5_SOURCE = [os.path.join('lib/krb5/', x) for x in to_list( '''acache.c add_et_list.c addr_families.c appdefault.c - asn1_glue.c auth_context.c + asn1_glue.c auth_context.c authdata.c build_ap_req.c build_auth.c cache.c changepw.c codec.c config_file.c constants.c convert_creds.c diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 5d3c4ac4bfd..c2a6256029f 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -810,7 +810,7 @@ planoldpythontestsuite("ad_dc:local", "samba.tests.gpo", extra_args=['-U"$USERNA planoldpythontestsuite("ad_dc:local", "samba.tests.dckeytab", extra_args=['-U"$USERNAME%$PASSWORD"']) have_fast_support = int('SAMBA_USES_MITKDC' in config_hash) -tkt_sig_support = 0 +tkt_sig_support = int('SAMBA4_USES_HEIMDAL' in config_hash) planoldpythontestsuite("none", "samba.tests.krb5.kcrypto") planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.simple_tests", environ={'SERVICE_USERNAME':'$SERVER', -- 2.25.1 From 96e065369c397c3b2a0910f3c12fce6bd07dbfd4 Mon Sep 17 00:00:00 2001 From: Isaac Boukris Date: Sun, 19 Sep 2021 15:04:14 +0300 Subject: [PATCH 122/159] krb5: allow NULL parameter to krb5_pac_free() BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 [jsutton@samba.org Cherry-picked from Heimdal commit b295167208a96e68515902138f6ce93972892ec5] Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 2d09de5c41e729bccc2d7949d8a3568a95e80e76) --- source4/heimdal/kdc/krb5tgs.c | 3 +-- source4/heimdal/lib/krb5/pac.c | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c index 3b35b828402..d0483a3903b 100644 --- a/source4/heimdal/kdc/krb5tgs.c +++ b/source4/heimdal/kdc/krb5tgs.c @@ -2074,8 +2074,7 @@ out: free_EncTicketPart(&adtkt); - if (mspac) - krb5_pac_free(context, mspac); + krb5_pac_free(context, mspac); return ret; } diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c index eec1e84c7bd..18f385fac1f 100644 --- a/source4/heimdal/lib/krb5/pac.c +++ b/source4/heimdal/lib/krb5/pac.c @@ -435,6 +435,8 @@ krb5_pac_get_types(krb5_context context, KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_pac_free(krb5_context context, krb5_pac pac) { + if (pac == NULL) + return; krb5_data_free(&pac->data); krb5_data_free(&pac->ticket_sign_data); free(pac->pac); -- 2.25.1 From f5e8a2f16a89a2d477bdd4387aa0dd122af998bf Mon Sep 17 00:00:00 2001 From: Isaac Boukris Date: Sun, 19 Sep 2021 15:16:58 +0300 Subject: [PATCH 123/159] krb5: rework PAC validation loop Avoid allocating the PAC on error. Closes: #836 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 [jsutton@samba.org Cherry-picked from Heimdal commit 6df8be5091363a1c9a9165465ab8292f817bec81] Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 2773379603a5a625c5d1c6e62f29c442942ff570) --- source4/heimdal/lib/krb5/pac.c | 132 +++++++++++++++++---------------- 1 file changed, 69 insertions(+), 63 deletions(-) diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c index 18f385fac1f..922a8710eda 100644 --- a/source4/heimdal/lib/krb5/pac.c +++ b/source4/heimdal/lib/krb5/pac.c @@ -1362,11 +1362,10 @@ _krb5_kdc_pac_ticket_parse(krb5_context context, krb5_pac *ppac) { AuthorizationData *ad = tkt->authorization_data; - krb5_boolean pac_found = FALSE; krb5_pac pac = NULL; unsigned i, j; size_t len = 0; - krb5_error_code ret; + krb5_error_code ret = 0; *signedticket = FALSE; *ppac = NULL; @@ -1377,8 +1376,10 @@ _krb5_kdc_pac_ticket_parse(krb5_context context, for (i = 0; i < ad->len; i++) { AuthorizationData child; - if (ad->val[i].ad_type == KRB5_AUTHDATA_WIN2K_PAC) - return KRB5KDC_ERR_BADOPTION; + if (ad->val[i].ad_type == KRB5_AUTHDATA_WIN2K_PAC) { + ret = KRB5KDC_ERR_BADOPTION; + goto out; + } if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT) continue; @@ -1390,79 +1391,84 @@ _krb5_kdc_pac_ticket_parse(krb5_context context, if (ret) { krb5_set_error_message(context, ret, "Failed to decode " "AD-IF-RELEVANT with %d", ret); - return ret; + goto out; } for (j = 0; j < child.len; j++) { - if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) { - krb5_data adifr_data = ad->val[i].ad_data; - krb5_data pac_data = child.val[j].ad_data; - krb5_data recoded_adifr; - - if (pac_found) { - free_AuthorizationData(&child); - return KRB5KDC_ERR_BADOPTION; - } - pac_found = TRUE; - - ret = krb5_pac_parse(context, - pac_data.data, - pac_data.length, - &pac); - if (ret) { - free_AuthorizationData(&child); - return ret; - } - - if (pac->ticket_checksum == NULL) { - free_AuthorizationData(&child); - *ppac = pac; - continue; - } - - /* - * Encode the ticket with the PAC replaced with a single zero - * byte, to be used as input data to the ticket signature. - */ - - child.val[j].ad_data = single_zero_pac; - - ASN1_MALLOC_ENCODE(AuthorizationData, recoded_adifr.data, - recoded_adifr.length, &child, &len, ret); - if (recoded_adifr.length != len) - krb5_abortx(context, "Internal error in ASN.1 encoder"); - - child.val[j].ad_data = pac_data; + krb5_data adifr_data = ad->val[i].ad_data; + krb5_data pac_data = child.val[j].ad_data; + krb5_data recoded_adifr; + + if (child.val[j].ad_type != KRB5_AUTHDATA_WIN2K_PAC) + continue; + + if (pac != NULL) { + free_AuthorizationData(&child); + ret = KRB5KDC_ERR_BADOPTION; + goto out; + } + + ret = krb5_pac_parse(context, + pac_data.data, + pac_data.length, + &pac); + if (ret) { free_AuthorizationData(&child); + goto out; + } - if (ret) { - krb5_pac_free(context, pac); - return ret; - } + if (pac->ticket_checksum == NULL) + continue; - ad->val[i].ad_data = recoded_adifr; + /* + * Encode the ticket with the PAC replaced with a single zero + * byte, to be used as input data to the ticket signature. + */ - ASN1_MALLOC_ENCODE(EncTicketPart, - pac->ticket_sign_data.data, - pac->ticket_sign_data.length, tkt, &len, - ret); - if(pac->ticket_sign_data.length != len) - krb5_abortx(context, "Internal error in ASN.1 encoder"); + child.val[j].ad_data = single_zero_pac; - ad->val[i].ad_data = adifr_data; - krb5_data_free(&recoded_adifr); + ASN1_MALLOC_ENCODE(AuthorizationData, recoded_adifr.data, + recoded_adifr.length, &child, &len, ret); + if (recoded_adifr.length != len) + krb5_abortx(context, "Internal error in ASN.1 encoder"); - if (ret) { - krb5_pac_free(context, pac); - return ret; - } + child.val[j].ad_data = pac_data; - *signedticket = TRUE; - *ppac = pac; + if (ret) { + free_AuthorizationData(&child); + goto out; } + + ad->val[i].ad_data = recoded_adifr; + + ASN1_MALLOC_ENCODE(EncTicketPart, + pac->ticket_sign_data.data, + pac->ticket_sign_data.length, tkt, &len, + ret); + if (pac->ticket_sign_data.length != len) + krb5_abortx(context, "Internal error in ASN.1 encoder"); + + ad->val[i].ad_data = adifr_data; + krb5_data_free(&recoded_adifr); + + if (ret) { + free_AuthorizationData(&child); + goto out; + } + + *signedticket = TRUE; } free_AuthorizationData(&child); } + +out: + if (ret) { + krb5_pac_free(context, pac); + return ret; + } + + *ppac = pac; + return 0; } -- 2.25.1 From 5c9787564497ba26ac3ed3e607adbbf34308df52 Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Fri, 17 Sep 2021 13:57:57 +1000 Subject: [PATCH 124/159] krb5: return KRB5KRB_AP_ERR_INAPP_CKSUM if PAC checksum fails Return KRB5KRB_AP_ERR_INAPP_CKSUM instead of EINVAL when verifying a PAC, if the checksum is absent or unkeyed. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 [jsutton@samba.org Cherry-picked from Heimdal commit c4b99b48c4b18f30d504b427bc1961d7a71f631e] Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit d6a472e953545ec3858ca969c1a4191e4f27ba63) --- source4/heimdal/lib/krb5/pac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c index 922a8710eda..3e45125d35e 100644 --- a/source4/heimdal/lib/krb5/pac.c +++ b/source4/heimdal/lib/krb5/pac.c @@ -489,13 +489,13 @@ verify_checksum(krb5_context context, } ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length); if (ret != (int)cksum.checksum.length) { - ret = EINVAL; + ret = KRB5KRB_AP_ERR_INAPP_CKSUM; krb5_set_error_message(context, ret, "PAC checksum missing checksum"); goto out; } if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) { - ret = EINVAL; + ret = KRB5KRB_AP_ERR_INAPP_CKSUM; krb5_set_error_message(context, ret, "Checksum type %d not keyed", cksum.cksumtype); goto out; -- 2.25.1 From 3c8070cc255f3fee2dee2fffadf54989e91fde19 Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Sun, 6 Jan 2019 17:54:58 +1100 Subject: [PATCH 125/159] kdc: only set HDB_F_GET_KRBTGT when requesting TGS principal BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 [jsutton@samba.org Backported from Heimdal commit f1dd2b818aa0866960945edea02a6bc782ed697c - Removed change to _kdc_find_etype() use_strongest_session_key parameter since Samba's Heimdal version uses different logic ] Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit db30b71f79864a20b38a1f812a5df833f3a92de8) --- source4/heimdal/kdc/kerberos5.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c index 7b17d2539ce..d1fd201113b 100644 --- a/source4/heimdal/kdc/kerberos5.c +++ b/source4/heimdal/kdc/kerberos5.c @@ -983,6 +983,7 @@ _kdc_as_rep(krb5_context context, pk_client_params *pkp = NULL; #endif const EncryptionKey *pk_reply_key = NULL; + krb5_boolean is_tgs; memset(&rep, 0, sizeof(rep)); memset(&session_key, 0, sizeof(session_key)); @@ -1033,6 +1034,8 @@ _kdc_as_rep(krb5_context context, kdc_log(context, config, 0, "AS-REQ %s from %s for %s", client_name, from, server_name); + is_tgs = krb5_principal_is_krbtgt(context, server_princ); + /* * */ @@ -1101,7 +1104,7 @@ _kdc_as_rep(krb5_context context, goto out; } ret = _kdc_db_fetch(context, config, server_princ, - HDB_F_GET_SERVER|HDB_F_GET_KRBTGT | flags, + HDB_F_GET_SERVER | flags | (is_tgs ? HDB_F_GET_KRBTGT : 0), NULL, NULL, &server); if(ret == HDB_ERR_NOT_FOUND_HERE) { kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", server_name); -- 2.25.1 From b60f9da0c3225dc3101d938e68d33ac638125f67 Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Thu, 23 Sep 2021 14:39:35 +1000 Subject: [PATCH 126/159] kdc: use ticket client name when signing PAC The principal in the PAC_LOGON_NAME buffer is expected to match the client name in the ticket. Previously we were setting this to the canonical client name, which would have broken PAC validation if the client did not request name canonicalization BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 [jsutton@samba.org Backported from Heimdal commit 3b0856cab2b25624deb1f6e0e67637ba96a647ac - Renamed variable to avoid shadowing existing variable ] Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 75d1a7cd14b134506061ed64ddb9b99856231d2c) --- source4/heimdal/kdc/kerberos5.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c index d1fd201113b..6dc945b134a 100644 --- a/source4/heimdal/kdc/kerberos5.c +++ b/source4/heimdal/kdc/kerberos5.c @@ -1716,6 +1716,7 @@ _kdc_as_rep(krb5_context context, krb5_pac p = NULL; krb5_data data; uint16_t rodc_id; + krb5_principal client_pac; ret = _kdc_pac_generate(context, client, pk_reply_key, &p); if (ret) { @@ -1726,12 +1727,21 @@ _kdc_as_rep(krb5_context context, if (p != NULL) { rodc_id = server->entry.kvno >> 16; + /* libkrb5 expects ticket and PAC client names to match */ + ret = _krb5_principalname2krb5_principal(context, &client_pac, + et.cname, et.crealm); + if (ret) { + krb5_pac_free(context, p); + goto out; + } + ret = _krb5_pac_sign(context, p, et.authtime, - client->entry.principal, + client_pac, &skey->key, /* Server key */ &skey->key, /* FIXME: should be krbtgt key */ rodc_id, &data); + krb5_free_principal(context, client_pac); krb5_pac_free(context, p); if (ret) { kdc_log(context, config, 0, "PAC signing failed for -- %s", -- 2.25.1 From 1d95a5fc6e904426ad683e9b7058ffaff9b320b8 Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Thu, 23 Sep 2021 17:51:51 +1000 Subject: [PATCH 127/159] kdc: correctly generate PAC TGS signature When generating an AS-REQ, the TGS signature was incorrectly generated using the server key, which would fail to validate if the server was not also the TGS. Fix this. Patch from Isaac Bourkis . BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 [jsutton@samba.org Backported from Heimdal commit e7863e2af922809dad25a2e948e98c408944d551 - Samba's Heimdal version does not have the generate_pac() helper function. - Samba's Heimdal version does not use the 'r' context variable. ] Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 91e684f5dcb48b76e6a322c15acb53cbce5c275a) --- source4/heimdal/kdc/kerberos5.c | 49 ++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c index 6dc945b134a..a131f1af08e 100644 --- a/source4/heimdal/kdc/kerberos5.c +++ b/source4/heimdal/kdc/kerberos5.c @@ -948,6 +948,33 @@ _kdc_is_anonymous(krb5_context context, krb5_principal principal) return 1; } +static krb5_error_code +get_local_tgs(krb5_context context, + krb5_kdc_configuration *config, + krb5_const_realm realm, + hdb_entry_ex **krbtgt) +{ + krb5_error_code ret; + krb5_principal tgs_name; + + *krbtgt = NULL; + + ret = krb5_make_principal(context, + &tgs_name, + realm, + KRB5_TGS_NAME, + realm, + NULL); + if (ret) + return ret; + + ret = _kdc_db_fetch(context, config, tgs_name, + HDB_F_GET_KRBTGT, NULL, NULL, krbtgt); + krb5_free_principal(context, tgs_name); + + return ret; +} + /* * */ @@ -984,6 +1011,8 @@ _kdc_as_rep(krb5_context context, #endif const EncryptionKey *pk_reply_key = NULL; krb5_boolean is_tgs; + hdb_entry_ex *krbtgt = NULL; + Key *krbtgt_key = NULL; memset(&rep, 0, sizeof(rep)); memset(&session_key, 0, sizeof(session_key)); @@ -1466,6 +1495,22 @@ _kdc_as_rep(krb5_context context, if(ret) goto out; + /* If server is not krbtgt, fetch local krbtgt key for signing authdata */ + if (is_tgs) { + krbtgt_key = skey; + } else { + ret = get_local_tgs(context, config, server_princ->realm, + &krbtgt); + if (ret) + goto out; + + ret = _kdc_get_preferred_key(context, config, krbtgt, + server_princ->realm, + NULL, &krbtgt_key); + if (ret) + goto out; + } + if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey || (f.request_anonymous && !config->allow_anonymous)) { ret = KRB5KDC_ERR_BADOPTION; @@ -1738,7 +1783,7 @@ _kdc_as_rep(krb5_context context, ret = _krb5_pac_sign(context, p, et.authtime, client_pac, &skey->key, /* Server key */ - &skey->key, /* FIXME: should be krbtgt key */ + &krbtgt_key->key, /* TGS key */ rodc_id, &data); krb5_free_principal(context, client_pac); @@ -1807,6 +1852,8 @@ out: _kdc_free_ent(context, client); if(server) _kdc_free_ent(context, server); + if (krbtgt) + _kdc_free_ent(context, krbtgt); return ret; } -- 2.25.1 From 79db1253d80255fc48994971cb147fefff058b3e Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 11 Aug 2021 13:27:11 +1200 Subject: [PATCH 128/159] s4/heimdal/lib/krb5/pac.c: Align PAC buffers to match Windows BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 28a5a586c8e9cd155d676dcfcb81a2587ace99d1) --- selftest/knownfail_heimdal_kdc | 1 + source4/heimdal/lib/krb5/pac.c | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 4ec682c01d0..20eea7f2d7e 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -124,6 +124,7 @@ # # S4U tests # +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_constrained_delegation_old_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_rbcd_old_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_service_pac ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_unkeyed_client_checksum diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c index 3e45125d35e..6535a9bdcc4 100644 --- a/source4/heimdal/lib/krb5/pac.c +++ b/source4/heimdal/lib/krb5/pac.c @@ -62,10 +62,12 @@ struct krb5_pac_data { #define PACTYPE_SIZE 8 #define PAC_INFO_BUFFER_SIZE 16 +#define PAC_LOGON_INFO 1 #define PAC_SERVER_CHECKSUM 6 #define PAC_PRIVSVR_CHECKSUM 7 #define PAC_LOGON_NAME 10 #define PAC_CONSTRAINED_DELEGATION 11 +#define PAC_UPN_DNS_INFO 12 #define PAC_TICKET_CHECKSUM 16 #define CHECK(r,f,l) \ @@ -1184,7 +1186,17 @@ _krb5_pac_sign(krb5_context context, ret = krb5_enomem(context); goto out; } - /* XXX if not aligned, fill_zeros */ + + if (p->pac->buffers[i].type == PAC_LOGON_INFO + || p->pac->buffers[i].type == PAC_UPN_DNS_INFO) + { + uint32_t rounded = (len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT + * PAC_ALIGNMENT; + uint32_t remaining = rounded - len; + CHECK(ret, fill_zeros(context, spdata, remaining), out); + + len = rounded; + } } /* write header */ -- 2.25.1 From 797dd463c02733c899a0925b28147c7c4914aade Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 8 Oct 2021 15:43:41 +1300 Subject: [PATCH 129/159] heimdal: Make _krb5_pac_get_kdc_checksum_info() into a global function This lets us call it from Samba. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 3bdce12789af1e7a7aba56691f184625a432410d) --- source4/heimdal/lib/krb5/pac.c | 8 ++++---- source4/heimdal/lib/krb5/version-script.map | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c index 6535a9bdcc4..f6d38178a88 100644 --- a/source4/heimdal/lib/krb5/pac.c +++ b/source4/heimdal/lib/krb5/pac.c @@ -1311,10 +1311,10 @@ out: } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL -_krb5_pac_get_kdc_checksum_info(krb5_context context, - krb5_pac pac, - krb5_cksumtype *cstype, - uint16_t *rodc_id) +krb5_pac_get_kdc_checksum_info(krb5_context context, + krb5_pac pac, + krb5_cksumtype *cstype, + uint16_t *rodc_id) { krb5_error_code ret; krb5_storage *sp = NULL; diff --git a/source4/heimdal/lib/krb5/version-script.map b/source4/heimdal/lib/krb5/version-script.map index e04e02aace9..2359001e9da 100644 --- a/source4/heimdal/lib/krb5/version-script.map +++ b/source4/heimdal/lib/krb5/version-script.map @@ -470,6 +470,7 @@ HEIMDAL_KRB5_2.0 { krb5_pac_add_buffer; krb5_pac_free; krb5_pac_get_buffer; + krb5_pac_get_kdc_checksum_info; krb5_pac_get_types; krb5_pac_init; krb5_pac_parse; @@ -753,7 +754,6 @@ HEIMDAL_KRB5_2.0 { _krb5_pac_sign; _krb5_kdc_pac_sign_ticket; _krb5_kdc_pac_ticket_parse; - _krb5_pac_get_kdc_checksum_info; _kdc_tkt_insert_pac; _kdc_tkt_add_if_relevant_ad; _krb5_parse_moduli; -- 2.25.1 From b9672524f8eaf4bebf7bc742638734e51031144d Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 8 Oct 2021 16:08:39 +1300 Subject: [PATCH 130/159] s4:kdc: Check ticket signature BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 02fa69c6c73c01d82807be4370e838f3e7c66f35) --- selftest/knownfail_heimdal_kdc | 9 -- source4/kdc/wdc-samba4.c | 270 ++++++++++++++++++++++++++------- 2 files changed, 215 insertions(+), 64 deletions(-) diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 20eea7f2d7e..683dbacb979 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -124,13 +124,8 @@ # # S4U tests # -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_constrained_delegation_old_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_rbcd_old_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_service_pac -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_unkeyed_client_checksum -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_unkeyed_service_checksum -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_zeroed_client_checksum -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_zeroed_service_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_existing_delegation_info ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_missing_client_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_a @@ -142,10 +137,6 @@ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed # -# RODC tests -# -^samba.tests.krb5.rodc_tests.samba.tests.krb5.rodc_tests.RodcKerberosTests.test_rodc_ticket_signature -# # The lack of KRB5SignedPath means we no longer return # KRB5KRB_ERR_RESPONSE_TOO_BIG in this specific case # diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c index 037db40ce46..589df8a651d 100644 --- a/source4/kdc/wdc-samba4.c +++ b/source4/kdc/wdc-samba4.c @@ -23,7 +23,10 @@ #include "includes.h" #include "kdc/kdc-glue.h" +#include "kdc/db-glue.h" #include "kdc/pac-glue.h" +#include "sdb.h" +#include "sdb_hdb.h" /* * Given the right private pointer from hdb_samba4, @@ -94,15 +97,13 @@ static krb5_error_code samba_wdc_get_pac_compat(void *priv, krb5_context context return samba_wdc_get_pac(priv, context, client, NULL, pac); } -/* Resign (and reform, including possibly new groups) a PAC */ - -static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, - const krb5_principal client_principal, - const krb5_principal delegated_proxy_principal, - struct hdb_entry_ex *client, - struct hdb_entry_ex *server, - struct hdb_entry_ex *krbtgt, - krb5_pac *pac) +static krb5_error_code samba_wdc_reget_pac2(krb5_context context, + const krb5_principal delegated_proxy_principal, + struct hdb_entry_ex *client, + struct hdb_entry_ex *server, + struct hdb_entry_ex *krbtgt, + krb5_pac *pac, + krb5_cksumtype ctype) { struct samba_kdc_entry *p = talloc_get_type_abort(server->ctx, @@ -110,15 +111,13 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, struct samba_kdc_entry *krbtgt_skdc_entry = talloc_get_type_abort(krbtgt->ctx, struct samba_kdc_entry); - TALLOC_CTX *mem_ctx = talloc_named(p, 0, "samba_kdc_reget_pac context"); + TALLOC_CTX *mem_ctx = talloc_named(p, 0, "samba_kdc_reget_pac2 context"); krb5_pac new_pac = NULL; DATA_BLOB *pac_blob = NULL; DATA_BLOB *upn_blob = NULL; DATA_BLOB *deleg_blob = NULL; krb5_error_code ret; NTSTATUS nt_status; - struct PAC_SIGNATURE_DATA *pac_srv_sig; - struct PAC_SIGNATURE_DATA *pac_kdc_sig; bool is_in_db, is_untrusted; size_t num_types = 0; uint32_t *types = NULL; @@ -130,6 +129,7 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, 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; if (!mem_ctx) { return ENOMEM; @@ -150,6 +150,71 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, return ret; } + if (delegated_proxy_principal != NULL) { + krb5_enctype etype; + Key *key = NULL; + + if (!is_in_db) { + /* + * The RODC-issued PAC was signed by a KDC entry that we + * don't have a key for. The server signature is not + * trustworthy, since it could have been created by the + * server we got the ticket from. We must not proceed as + * otherwise the ticket signature is unchecked. + */ + talloc_free(mem_ctx); + return HDB_ERR_NOT_FOUND_HERE; + } + + /* Fetch the correct key depending on the checksum type. */ + if (ctype == CKSUMTYPE_HMAC_MD5) { + etype = ENCTYPE_ARCFOUR_HMAC; + } else { + ret = krb5_cksumtype_to_enctype(context, + ctype, + &etype); + if (ret != 0) { + talloc_free(mem_ctx); + return ret; + } + } + ret = hdb_enctype2key(context, &krbtgt->entry, etype, &key); + if (ret != 0) { + return ret; + } + + /* Check the KDC and ticket signatures. */ + ret = krb5_pac_verify(context, + *pac, + 0, + NULL, + NULL, + &key->key); + if (ret != 0) { + DEBUG(1, ("PAC KDC signature failed to verify\n")); + talloc_free(mem_ctx); + return ret; + } + + deleg_blob = talloc_zero(mem_ctx, DATA_BLOB); + if (!deleg_blob) { + talloc_free(mem_ctx); + return ENOMEM; + } + + nt_status = samba_kdc_update_delegation_info_blob(mem_ctx, + context, *pac, + server->entry.principal, + delegated_proxy_principal, + deleg_blob); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Building PAC failed: %s\n", + nt_errstr(nt_status))); + talloc_free(mem_ctx); + return EINVAL; + } + } + if (is_untrusted) { struct samba_kdc_entry *client_skdc_entry = NULL; @@ -173,52 +238,10 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, return ENOMEM; } - pac_srv_sig = talloc_zero(mem_ctx, struct PAC_SIGNATURE_DATA); - if (!pac_srv_sig) { - talloc_free(mem_ctx); - return ENOMEM; - } - - pac_kdc_sig = talloc_zero(mem_ctx, struct PAC_SIGNATURE_DATA); - if (!pac_kdc_sig) { - talloc_free(mem_ctx); - return ENOMEM; - } - nt_status = samba_kdc_update_pac_blob(mem_ctx, context, krbtgt_skdc_entry->kdc_db_ctx->samdb, *pac, pac_blob, - pac_srv_sig, pac_kdc_sig); - if (!NT_STATUS_IS_OK(nt_status)) { - DEBUG(0, ("Building PAC failed: %s\n", - nt_errstr(nt_status))); - talloc_free(mem_ctx); - return EINVAL; - } - - if (is_in_db) { - /* Now check the KDC signature, fetching the correct key based on the enc type */ - ret = kdc_check_pac(context, pac_srv_sig->signature, pac_kdc_sig, krbtgt); - if (ret != 0) { - DEBUG(1, ("PAC KDC signature failed to verify\n")); - talloc_free(mem_ctx); - return ret; - } - } - } - - if (delegated_proxy_principal) { - deleg_blob = talloc_zero(mem_ctx, DATA_BLOB); - if (!deleg_blob) { - talloc_free(mem_ctx); - return ENOMEM; - } - - nt_status = samba_kdc_update_delegation_info_blob(mem_ctx, - context, *pac, - server->entry.principal, - delegated_proxy_principal, - deleg_blob); + NULL, NULL); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(0, ("Building PAC failed: %s\n", nt_errstr(nt_status))); @@ -308,6 +331,18 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, } kdc_checksum_idx = i; break; + case PAC_TYPE_TICKET_CHECKSUM: + if (tkt_checksum_idx != -1) { + DEBUG(1, ("ticket checksum type[%"PRIu32"] twice [%zd] and [%zu]: \n", + types[i], + tkt_checksum_idx, + i)); + SAFE_FREE(types); + talloc_free(mem_ctx); + return EINVAL; + } + tkt_checksum_idx = i; + break; default: continue; } @@ -471,6 +506,131 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, return ret; } +/* Resign (and reform, including possibly new groups) a PAC */ + +static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, + const krb5_principal client_principal, + const krb5_principal delegated_proxy_principal, + struct hdb_entry_ex *client, + struct hdb_entry_ex *server, + struct hdb_entry_ex *krbtgt, + krb5_pac *pac) +{ + struct samba_kdc_entry *krbtgt_skdc_entry = + talloc_get_type_abort(krbtgt->ctx, + struct samba_kdc_entry); + krb5_error_code ret; + krb5_cksumtype ctype = CKSUMTYPE_NONE; + struct hdb_entry_ex signing_krbtgt_hdb; + + if (delegated_proxy_principal) { + uint16_t rodc_id; + unsigned int my_krbtgt_number; + + /* + * We're using delegated_proxy_principal for the moment to + * indicate cases where the ticket was encrypted with the server + * key, and not a krbtgt key. This cannot be trusted, so we need + * to find a krbtgt key that signs the PAC in order to trust the + * ticket. + * + * The krbtgt passed in to this function refers to the krbtgt + * used to decrypt the ticket of the server requesting + * S4U2Proxy. + * + * When we implement service ticket renewal, we need to check + * the PAC, and this will need to be updated. + */ + ret = krb5_pac_get_kdc_checksum_info(context, + *pac, + &ctype, + &rodc_id); + if (ret != 0) { + DEBUG(1, ("Failed to get PAC checksum info\n")); + return ret; + } + + /* + * We need to check the KDC and ticket signatures, fetching the + * correct key based on the enctype. + */ + + my_krbtgt_number = krbtgt_skdc_entry->kdc_db_ctx->my_krbtgt_number; + + if (my_krbtgt_number != 0) { + /* + * If we are an RODC, and we are not the KDC that signed + * the evidence ticket, then we need to proxy the + * request. + */ + if (rodc_id != my_krbtgt_number) { + return HDB_ERR_NOT_FOUND_HERE; + } + } else { + /* + * If we are a DC, the ticket may have been signed by a + * different KDC than the one that issued the header + * ticket. + */ + if (rodc_id != krbtgt->entry.kvno >> 16) { + struct sdb_entry_ex signing_krbtgt_sdb; + + /* + * If we didn't sign the ticket, then return an + * error. + */ + if (rodc_id != 0) { + return KRB5KRB_AP_ERR_MODIFIED; + } + + /* + * Fetch our key from the database. To support + * key rollover, we're going to need to try + * multiple keys by trial and error. For now, + * krbtgt keys aren't assumed to change. + */ + ret = samba_kdc_fetch(context, + krbtgt_skdc_entry->kdc_db_ctx, + krbtgt->entry.principal, + SDB_F_GET_KRBTGT | SDB_F_CANON, + 0, + &signing_krbtgt_sdb); + if (ret != 0) { + return ret; + } + + ret = sdb_entry_ex_to_hdb_entry_ex(context, + &signing_krbtgt_sdb, + &signing_krbtgt_hdb); + sdb_free_entry(&signing_krbtgt_sdb); + if (ret != 0) { + return ret; + } + + /* + * Replace the krbtgt entry with our own entry + * for further processing. + */ + krbtgt = &signing_krbtgt_hdb; + } + } + } + + ret = samba_wdc_reget_pac2(context, + delegated_proxy_principal, + client, + server, + krbtgt, + pac, + ctype); + + if (krbtgt == &signing_krbtgt_hdb) { + hdb_free_entry(context, &signing_krbtgt_hdb); + } + + return ret; +} + static char *get_netbios_name(TALLOC_CTX *mem_ctx, HostAddresses *addrs) { char *nb_name = NULL; -- 2.25.1 From d052d76ea7cdd77ff9648441649efef0cd3d72c4 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Sun, 10 Oct 2021 21:55:59 -0500 Subject: [PATCH 131/159] krb5: Fix PAC signature leak affecting KDC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 [jsutton@samba.org Cherry-picked from Heimdal commit 54581d2d52443a9a07ed5980df331f660b397dcf] Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit f6adfefbbb41b9100736134d0f975f1ec0c33c42) --- source4/heimdal/lib/krb5/pac.c | 136 +++++++++++++++------------------ 1 file changed, 61 insertions(+), 75 deletions(-) diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c index f6d38178a88..05bcc523080 100644 --- a/source4/heimdal/lib/krb5/pac.c +++ b/source4/heimdal/lib/krb5/pac.c @@ -1018,9 +1018,10 @@ _krb5_pac_sign(krb5_context context, uint32_t server_offset = 0, priv_offset = 0, ticket_offset = 0; uint32_t server_cksumtype = 0, priv_cksumtype = 0; int num = 0; - size_t i; + size_t i, sz; krb5_data logon, d; + krb5_data_zero(&d); krb5_data_zero(&logon); for (i = 0; i < p->pac->numbuffers; i++) { @@ -1080,8 +1081,10 @@ _krb5_pac_sign(krb5_context context, void *ptr; ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1))); - if (ptr == NULL) - return krb5_enomem(context); + if (ptr == NULL) { + ret = krb5_enomem(context); + goto out; + } p->pac = ptr; @@ -1109,30 +1112,33 @@ _krb5_pac_sign(krb5_context context, /* Calculate LOGON NAME */ ret = build_logon_name(context, authtime, principal, &logon); - if (ret) - goto out; /* Set lengths for checksum */ - ret = pac_checksum(context, server_key, &server_cksumtype, &server_size); - if (ret) - goto out; + if (ret == 0) + ret = pac_checksum(context, server_key, &server_cksumtype, &server_size); - ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size); - if (ret) - goto out; + if (ret == 0) + ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size); /* Encode PAC */ - sp = krb5_storage_emem(); - if (sp == NULL) - return krb5_enomem(context); - - krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); + if (ret == 0) { + sp = krb5_storage_emem(); + if (sp == NULL) + ret = krb5_enomem(context); + } - spdata = krb5_storage_emem(); - if (spdata == NULL) { - krb5_storage_free(sp); - return krb5_enomem(context); + if (ret == 0) { + krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); + spdata = krb5_storage_emem(); + if (spdata == NULL) { + krb5_storage_free(sp); + ret = krb5_enomem(context); + } } + + if (ret) + goto out; + krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out); @@ -1222,77 +1228,56 @@ _krb5_pac_sign(krb5_context context, /* assert (server_offset != 0 && priv_offset != 0); */ /* export PAC */ - ret = krb5_storage_to_data(spdata, &d); - if (ret) { - krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); - goto out; - } - ret = krb5_storage_write(sp, d.data, d.length); - if (ret != (int)d.length) { - krb5_data_free(&d); - ret = krb5_enomem(context); - goto out; + if (ret == 0) + ret = krb5_storage_to_data(spdata, &d); + if (ret == 0) { + sz = krb5_storage_write(sp, d.data, d.length); + if (sz != d.length) { + krb5_data_free(&d); + ret = krb5_enomem(context); + goto out; + } } krb5_data_free(&d); - ret = krb5_storage_to_data(sp, &d); - if (ret) { - ret = krb5_enomem(context); - goto out; - } + if (ret == 0) + ret = krb5_storage_to_data(sp, &d); /* sign */ - if (p->ticket_sign_data.length) { + if (ret == 0 && p->ticket_sign_data.length) ret = create_checksum(context, priv_key, priv_cksumtype, p->ticket_sign_data.data, p->ticket_sign_data.length, (char *)d.data + ticket_offset, priv_size); - if (ret) { - krb5_data_free(&d); - goto out; - } - } - ret = create_checksum(context, server_key, server_cksumtype, - d.data, d.length, - (char *)d.data + server_offset, server_size); - if (ret) { - krb5_data_free(&d); - goto out; - } - ret = create_checksum(context, priv_key, priv_cksumtype, - (char *)d.data + server_offset, server_size, - (char *)d.data + priv_offset, priv_size); - if (ret) { - krb5_data_free(&d); - goto out; - } - - if (rodc_id != 0) { + if (ret == 0) + ret = create_checksum(context, server_key, server_cksumtype, + d.data, d.length, + (char *)d.data + server_offset, server_size); + if (ret == 0) + ret = create_checksum(context, priv_key, priv_cksumtype, + (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(); - if (rs == NULL) { - krb5_data_free(&d); + if (rs == NULL) ret = krb5_enomem(context); - goto out; - } krb5_storage_set_flags(rs, KRB5_STORAGE_BYTEORDER_LE); - ret = krb5_store_uint16(rs, rodc_id); - if (ret) { - krb5_storage_free(rs); - krb5_data_free(&d); - goto out; - } - ret = krb5_storage_to_data(rs, &rd); + 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) { - krb5_data_free(&d); + 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) + goto out; + /* done */ *data = d; @@ -1302,6 +1287,7 @@ _krb5_pac_sign(krb5_context context, return 0; out: + krb5_data_free(&d); krb5_data_free(&logon); if (sp) krb5_storage_free(sp); @@ -1528,8 +1514,8 @@ _krb5_kdc_pac_sign_ticket(krb5_context context, ret = _krb5_pac_sign(context, pac, tkt->authtime, client, server_key, kdc_key, rodc_id, &rspac); - if (ret) - return ret; - - return _kdc_tkt_insert_pac(context, tkt, &rspac); + if (ret == 0) + ret = _kdc_tkt_insert_pac(context, tkt, &rspac); + krb5_data_free(&rspac); + return ret; } -- 2.25.1 From bbe816ff36ba6230abad147e7f1e106faacee9aa Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 15 Oct 2021 13:09:20 +1300 Subject: [PATCH 132/159] selftest/dbcheck: Fix up RODC one-way links (use correct dbcheck rule) The previous commit was correct on intention, but it was not noticed as there is a race, that the incorrect rule was appended to. These links are removed by remove_plausible_deleted_DN_links not fix_all_old_dn_string_component_mismatch BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Andrew Bartlett Reviewed-by: Joseph Sutton Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Fri Oct 15 10:00:47 UTC 2021 on sn-devel-184 (cherry picked from commit a7ad665e65f0701eb75cac5bc10a366ccd9689f4) --- testprogs/blackbox/dbcheck.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testprogs/blackbox/dbcheck.sh b/testprogs/blackbox/dbcheck.sh index e2ba987e2de..5462441005e 100755 --- a/testprogs/blackbox/dbcheck.sh +++ b/testprogs/blackbox/dbcheck.sh @@ -19,12 +19,12 @@ dbcheck() { # This list of attributes can be freely extended dbcheck_fix_one_way_links() { - $PYTHON $BINDIR/samba-tool dbcheck --quiet --fix --yes fix_all_old_dn_string_component_mismatch --attrs="lastKnownParent defaultObjectCategory fromServer rIDSetReferences msDS-RevealOnDemandGroup msDS-NeverRevealGroup" --cross-ncs $ARGS + $PYTHON $BINDIR/samba-tool dbcheck --quiet --fix --yes fix_all_old_dn_string_component_mismatch --attrs="lastKnownParent defaultObjectCategory fromServer rIDSetReferences" --cross-ncs $ARGS } # This list of attributes can be freely extended dbcheck_fix_stale_links() { - $PYTHON $BINDIR/samba-tool dbcheck --quiet --fix --yes remove_plausible_deleted_DN_links --attrs="member msDS-NC-Replica-Locations msDS-NC-RO-Replica-Locations" --cross-ncs $ARGS + $PYTHON $BINDIR/samba-tool dbcheck --quiet --fix --yes remove_plausible_deleted_DN_links --attrs="member msDS-NC-Replica-Locations msDS-NC-RO-Replica-Locations msDS-RevealOnDemandGroup msDS-NeverRevealGroup" --cross-ncs $ARGS } # This list of attributes can be freely extended -- 2.25.1 From 911c18e64a6fa6e19323fc21c325e1eb3a2418d6 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 15 Oct 2021 12:12:30 +1300 Subject: [PATCH 133/159] heimdal:kdc: Fix ticket signing without a PAC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit d23d8e859357b0fac4d1f4a49f1dce6cf60d6216) --- source4/heimdal/kdc/krb5tgs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c index d0483a3903b..2de3b099199 100644 --- a/source4/heimdal/kdc/krb5tgs.c +++ b/source4/heimdal/kdc/krb5tgs.c @@ -695,10 +695,12 @@ tgs_make_reply(krb5_context context, } /* The PAC should be the last change to the ticket. */ - ret = _krb5_kdc_pac_sign_ticket(context, mspac, tgt_name, serverkey, - krbtgtkey, rodc_id, add_ticket_sig, &et); + if (mspac != NULL) { + ret = _krb5_kdc_pac_sign_ticket(context, mspac, tgt_name, serverkey, + krbtgtkey, rodc_id, add_ticket_sig, &et); if (ret) goto out; + } /* It is somewhat unclear where the etype in the following encryption should come from. What we have is a session -- 2.25.1 From 577aaab559853f6178f9b78d47034bc50b667bbc Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 15 Oct 2021 14:26:40 +1300 Subject: [PATCH 134/159] tests/krb5: Allow get_tgt() to request including or omitting a PAC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit e086c6193f6da6fcb5d0bcada2199e9bc7ad25f5) --- python/samba/tests/krb5/kdc_base_test.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 87160f675ae..1fc15315b0b 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -1306,9 +1306,9 @@ class KDCBaseTest(RawKerberosTest): def get_tgt(self, creds, to_rodc=False, kdc_options=None, expected_flags=None, unexpected_flags=None, - fresh=False): + pac_request=True, expect_pac=True, fresh=False): user_name = creds.get_username() - cache_key = (user_name, to_rodc, kdc_options) + cache_key = (user_name, to_rodc, kdc_options, pac_request) if not fresh: tgt = self.tkt_cache.get(cache_key) @@ -1363,7 +1363,7 @@ class KDCBaseTest(RawKerberosTest): kdc_options=kdc_options, preauth_key=None, ticket_decryption_key=ticket_decryption_key, - pac_request=True, + pac_request=pac_request, pac_options=pac_options, to_rodc=to_rodc) self.check_pre_authentication(rep) @@ -1405,8 +1405,9 @@ class KDCBaseTest(RawKerberosTest): kdc_options=kdc_options, preauth_key=preauth_key, ticket_decryption_key=ticket_decryption_key, - pac_request=True, + pac_request=pac_request, pac_options=pac_options, + expect_pac=expect_pac, to_rodc=to_rodc) self.check_as_reply(rep) -- 2.25.1 From 6dda5a9cbe4c9eaa7ca71cebd04775033d3f5f65 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 15 Oct 2021 14:27:15 +1300 Subject: [PATCH 135/159] tests/krb5: Allow specifying whether to expect a PAC with _test_as_exchange() BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 0dc69c1327f72384628a869a00482f6528b8671b) --- python/samba/tests/krb5/raw_testcase.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index e008223eb23..29dc5f397b6 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -3530,6 +3530,7 @@ class RawKerberosTest(TestCaseInTempDir): ticket_decryption_key=None, pac_request=None, pac_options=None, + expect_pac=True, to_rodc=False): def _generate_padata_copy(_kdc_exchange_dict, @@ -3569,6 +3570,7 @@ class RawKerberosTest(TestCaseInTempDir): kdc_options=str(kdc_options), pac_request=pac_request, pac_options=pac_options, + expect_pac=expect_pac, to_rodc=to_rodc) rep = self._generic_kdc_exchange(kdc_exchange_dict, -- 2.25.1 From bc9e321e39bb2fc042bf020bacc103334dbdbc27 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 15 Oct 2021 14:27:25 +1300 Subject: [PATCH 136/159] tests/krb5: Add method to get the PAC from a ticket BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett (cherry picked from commit 288355896a2b6f460c42559ec46ff980ab57782e) --- python/samba/tests/krb5/raw_testcase.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 29dc5f397b6..0790ac13f99 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -3447,6 +3447,15 @@ class RawKerberosTest(TestCaseInTempDir): _, pac = self.replace_pac(auth_data, None, expect_pac) return pac + def get_ticket_pac(self, ticket, expect_pac=True): + auth_data = ticket.ticket_private.get('authorization-data') + if expect_pac: + self.assertIsNotNone(auth_data) + elif auth_data is None: + return None + + return self.get_pac(auth_data, expect_pac=expect_pac) + def get_krbtgt_checksum_key(self): krbtgt_creds = self.get_krbtgt_creds() krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) -- 2.25.1 From 6e69925b32675462f7a6cba0d14a4578898a26db Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 15 Oct 2021 14:29:26 +1300 Subject: [PATCH 137/159] tests/krb5: Add tests for requesting a service ticket without a PAC BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Andrew Bartlett Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Sun Oct 17 23:40:33 UTC 2021 on sn-devel-184 [abartlet@samba.org backported from commit 9d3a691920205f8a9dc05d0e173e25e6a335f139 as the MIT KDC 1.16 seen on the reference Ubuntu 18.04 does not fail test_remove_pac] --- python/samba/tests/krb5/kdc_tgs_tests.py | 120 +++++++++++++++++++++++ selftest/knownfail_heimdal_kdc | 5 + selftest/knownfail_mit_kdc | 4 + 3 files changed, 129 insertions(+) diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py index 3075cc6b0a9..9d846a2c3ad 100755 --- a/python/samba/tests/krb5/kdc_tgs_tests.py +++ b/python/samba/tests/krb5/kdc_tgs_tests.py @@ -23,15 +23,18 @@ import os sys.path.insert(0, "bin/python") os.environ["PYTHONUNBUFFERED"] = "1" +import samba.tests.krb5.kcrypto as kcrypto from samba.tests.krb5.kdc_base_test import KDCBaseTest from samba.tests.krb5.rfc4120_constants import ( AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5, KRB_ERROR, + KRB_TGS_REP, KDC_ERR_BADMATCH, NT_PRINCIPAL, NT_SRV_INST, ) +import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1 global_asn1_print = False global_hexdump = False @@ -209,6 +212,123 @@ class KdcTgsTests(KDCBaseTest): pac_data.account_sid, "rep = {%s},%s" % (rep, pac_data)) + def _make_tgs_request(self, client_creds, service_creds, tgt, + expect_pac=True): + client_account = client_creds.get_username() + cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, + names=[client_account]) + + service_account = service_creds.get_username() + sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, + names=[service_account]) + + realm = service_creds.get_realm() + + expected_crealm = realm + expected_cname = cname + expected_srealm = realm + expected_sname = sname + + expected_supported_etypes = service_creds.tgs_supported_enctypes + + etypes = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) + + kdc_options = str(krb5_asn1.KDCOptions('canonicalize')) + + target_decryption_key = self.TicketDecryptionKey_from_creds( + service_creds) + + authenticator_subkey = self.RandomKey(kcrypto.Enctype.AES256) + + kdc_exchange_dict = self.tgs_exchange_dict( + expected_crealm=expected_crealm, + expected_cname=expected_cname, + expected_srealm=expected_srealm, + expected_sname=expected_sname, + expected_supported_etypes=expected_supported_etypes, + ticket_decryption_key=target_decryption_key, + check_rep_fn=self.generic_check_kdc_rep, + check_kdc_private_fn=self.generic_check_kdc_private, + expected_error_mode=0, + tgt=tgt, + authenticator_subkey=authenticator_subkey, + kdc_options=kdc_options, + expect_pac=expect_pac) + + rep = self._generic_kdc_exchange(kdc_exchange_dict, + cname=cname, + realm=realm, + sname=sname, + etypes=etypes) + self.check_reply(rep, KRB_TGS_REP) + + return kdc_exchange_dict['rep_ticket_creds'] + + def test_request_no_pac(self): + client_creds = self.get_client_creds() + service_creds = self.get_service_creds() + + tgt = self.get_tgt(client_creds, pac_request=False, + expect_pac=False) + + pac = self.get_ticket_pac(tgt, expect_pac=False) + self.assertIsNone(pac) + + ticket = self._make_tgs_request(client_creds, service_creds, tgt, + expect_pac=False) + + pac = self.get_ticket_pac(ticket, expect_pac=False) + self.assertIsNone(pac) + + def test_client_no_auth_data_required(self): + client_creds = self.get_cached_creds( + machine_account=False, + opts={'no_auth_data_required': True}) + service_creds = self.get_service_creds() + + tgt = self.get_tgt(client_creds) + + pac = self.get_ticket_pac(tgt) + self.assertIsNotNone(pac) + + ticket = self._make_tgs_request(client_creds, service_creds, tgt) + + pac = self.get_ticket_pac(ticket) + self.assertIsNotNone(pac) + + def test_service_no_auth_data_required(self): + client_creds = self.get_client_creds() + service_creds = self.get_cached_creds( + machine_account=True, + opts={'no_auth_data_required': True}) + + tgt = self.get_tgt(client_creds) + + pac = self.get_ticket_pac(tgt) + self.assertIsNotNone(pac) + + ticket = self._make_tgs_request(client_creds, service_creds, tgt, + expect_pac=False) + + pac = self.get_ticket_pac(ticket, expect_pac=False) + self.assertIsNone(pac) + + def test_remove_pac(self): + client_creds = self.get_client_creds() + service_creds = self.get_service_creds() + + tgt = self.modified_ticket(self.get_tgt(client_creds), + exclude_pac=True) + + pac = self.get_ticket_pac(tgt, expect_pac=False) + self.assertIsNone(pac) + + ticket = self._make_tgs_request(client_creds, service_creds, tgt, + expect_pac=False) + + pac = self.get_ticket_pac(ticket, expect_pac=False) + self.assertIsNone(pac) + if __name__ == "__main__": global_asn1_print = False diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 683dbacb979..32cfa2afa88 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -141,3 +141,8 @@ # KRB5KRB_ERR_RESPONSE_TOO_BIG in this specific case # ^samba4.krb5.kdc with machine account.as-req-pac-request.fl2000dc:local +# +# TGS tests +# +^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_client_no_auth_data_required +^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_service_no_auth_data_required diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc index 09efbc7b590..00f652db14a 100644 --- a/selftest/knownfail_mit_kdc +++ b/selftest/knownfail_mit_kdc @@ -276,6 +276,10 @@ 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_ldap_service_ticket\(ad_dc\) ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_get_ticket_for_host_service_of_machine_account\(ad_dc\) # +^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_request_no_pac\(ad_dc\) +^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_service_no_auth_data_required\(ad_dc\) +# # MIT currently fails the following MS-KILE tests. # ^samba.tests.krb5.ms_kile_client_principal_lookup_tests.samba.tests.krb5.ms_kile_client_principal_lookup_tests.MS_Kile_Client_Principal_Lookup_Tests.test_enterprise_principal_step_1_3 -- 2.25.1 From c2d7ee5d37dff8fe8141104e31c47e67f104ae5a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 18 Oct 2021 15:21:50 +1300 Subject: [PATCH 138/159] kdc: Remove UF_NO_AUTH_DATA_REQUIRED from client principals Tests against Windows 2019 show that UF_NO_AUTH_DATA_REQUIRED applies to services only, not to clients. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14871 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher [abartlet@samba.org backported from commit 92e8ce18a79e88c9b961dc20e39436c4cf653013 as there was a knownfail conflict with the test_remove_pac case which succeeds on this branch] --- selftest/knownfail_heimdal_kdc | 1 - selftest/knownfail_mit_kdc | 1 - source4/kdc/mit_samba.c | 7 ------- source4/kdc/pac-glue.c | 5 ----- 4 files changed, 14 deletions(-) diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 32cfa2afa88..4d058bad3da 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -144,5 +144,4 @@ # # TGS tests # -^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_client_no_auth_data_required ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_service_no_auth_data_required diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc index 00f652db14a..0f845fb9b1c 100644 --- a/selftest/knownfail_mit_kdc +++ b/selftest/knownfail_mit_kdc @@ -276,7 +276,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_ldap_service_ticket\(ad_dc\) ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_get_ticket_for_host_service_of_machine_account\(ad_dc\) # -^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_request_no_pac\(ad_dc\) ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_service_no_auth_data_required\(ad_dc\) # diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c index 2936fe2d18a..689e14e1c38 100644 --- a/source4/kdc/mit_samba.c +++ b/source4/kdc/mit_samba.c @@ -495,18 +495,11 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, ssize_t srv_checksum_idx = -1; ssize_t kdc_checksum_idx = -1; krb5_pac new_pac = NULL; - bool ok; if (client != NULL) { client_skdc_entry = talloc_get_type_abort(client->e_data, struct samba_kdc_entry); - - /* The user account may be set not to want the PAC */ - ok = samba_princ_needs_pac(client_skdc_entry); - if (!ok) { - return EINVAL; - } } if (server == NULL) { diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c index 88bcb734fc5..688103d8477 100644 --- a/source4/kdc/pac-glue.c +++ b/source4/kdc/pac-glue.c @@ -651,11 +651,6 @@ NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx, } *_upn_info_blob = NULL; - /* The user account may be set not to want the PAC */ - if ( ! samba_princ_needs_pac(p)) { - return NT_STATUS_OK; - } - logon_blob = talloc_zero(mem_ctx, DATA_BLOB); if (logon_blob == NULL) { return NT_STATUS_NO_MEMORY; -- 2.25.1 From 4b19b3713a9590dc78e4be7ed7cc4d97c1430c2a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 18 Oct 2021 16:00:45 +1300 Subject: [PATCH 139/159] kdc: Correctly strip PAC, rather than error on UF_NO_AUTH_DATA_REQUIRED for servers UF_NO_AUTH_DATA_REQUIRED on a server/service account should cause the PAC to be stripped not to given an error if the PAC was still present. Tested against Windows 2019 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14871 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher (cherry picked from commit 031a8287642e3c4b9d0b7c6b51f3b1d79b227542) --- selftest/knownfail_heimdal_kdc | 4 ---- source4/kdc/wdc-samba4.c | 38 +++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 4d058bad3da..683dbacb979 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -141,7 +141,3 @@ # KRB5KRB_ERR_RESPONSE_TOO_BIG in this specific case # ^samba4.krb5.kdc with machine account.as-req-pac-request.fl2000dc:local -# -# TGS tests -# -^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_service_no_auth_data_required diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c index 589df8a651d..ac9d7d51733 100644 --- a/source4/kdc/wdc-samba4.c +++ b/source4/kdc/wdc-samba4.c @@ -105,13 +105,15 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context, krb5_pac *pac, krb5_cksumtype ctype) { - struct samba_kdc_entry *p = + struct samba_kdc_entry *server_skdc_entry = talloc_get_type_abort(server->ctx, struct samba_kdc_entry); struct samba_kdc_entry *krbtgt_skdc_entry = talloc_get_type_abort(krbtgt->ctx, struct samba_kdc_entry); - TALLOC_CTX *mem_ctx = talloc_named(p, 0, "samba_kdc_reget_pac2 context"); + TALLOC_CTX *mem_ctx = talloc_named(server_skdc_entry, + 0, + "samba_kdc_reget_pac2 context"); krb5_pac new_pac = NULL; DATA_BLOB *pac_blob = NULL; DATA_BLOB *upn_blob = NULL; @@ -135,12 +137,6 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context, return ENOMEM; } - /* The user account may be set not to want the PAC */ - if (!samba_princ_needs_pac(p)) { - talloc_free(mem_ctx); - return EINVAL; - } - /* 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 */ @@ -373,12 +369,28 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context, return EINVAL; } - /* Build an updated PAC */ + /* + * 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)) { + ret = 0; + new_pac = NULL; + goto out; + } + + /* Otherwise build an updated PAC */ ret = krb5_pac_init(context, &new_pac); if (ret != 0) { - SAFE_FREE(types); - talloc_free(mem_ctx); - return ret; + new_pac = NULL; + goto out; } for (i = 0;;) { @@ -496,6 +508,8 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context, } } +out: + SAFE_FREE(types); /* We now replace the pac */ -- 2.25.1 From 12b500a11b887588455aac5b7288a573b61a481e Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 18 Oct 2021 16:05:19 +1300 Subject: [PATCH 140/159] tests/krb5: Ensure PAC is not present if expect_pac is false BUG: https://bugzilla.samba.org/show_bug.cgi?id=14871 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Stefan Metzmacher (cherry picked from commit cc3d27596b9e8a8a46e8ba9c3c1a445477d458cf) --- python/samba/tests/krb5/raw_testcase.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 0790ac13f99..0b9fe8e7a04 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -2385,13 +2385,6 @@ class RawKerberosTest(TestCaseInTempDir): self.assertElementPresent(ticket_private, 'authorization-data', expect_empty=not expect_pac) - if expect_pac: - authorization_data = self.getElementValue(ticket_private, - 'authorization-data') - pac_data = self.get_pac(authorization_data) - - self.check_pac_buffers(pac_data, kdc_exchange_dict) - encpart_session_key = None if encpart_private is not None: self.assertElementPresent(encpart_private, 'key') @@ -2493,6 +2486,13 @@ class RawKerberosTest(TestCaseInTempDir): ticket_private=ticket_private, encpart_private=encpart_private) + if ticket_private is not None: + pac_data = self.get_ticket_pac(ticket_creds, expect_pac=expect_pac) + if expect_pac: + self.check_pac_buffers(pac_data, kdc_exchange_dict) + else: + self.assertIsNone(pac_data) + expect_ticket_checksum = kdc_exchange_dict['expect_ticket_checksum'] if expect_ticket_checksum: self.assertIsNotNone(ticket_decryption_key) -- 2.25.1 From 5fc8d65cffd859c5d72f53546cc0e9afb72e7a5a Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 18 Oct 2021 16:07:11 +1300 Subject: [PATCH 141/159] tests/krb5: Add tests for constrained delegation to NO_AUTH_DATA_REQUIRED service BUG: https://bugzilla.samba.org/show_bug.cgi?id=14871 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Stefan Metzmacher Autobuild-User(master): Stefan Metzmacher Autobuild-Date(master): Wed Oct 20 09:22:43 UTC 2021 on sn-devel-184 (cherry picked from commit 83a654a4efd39a6e792a6d49e0ecf586e9bc53ef) --- python/samba/tests/krb5/s4u_tests.py | 107 ++++++++++++++++++++++++++- selftest/knownfail_heimdal_kdc | 8 +- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py index 9a25256081a..bbb7135b55b 100755 --- a/python/samba/tests/krb5/s4u_tests.py +++ b/python/samba/tests/krb5/s4u_tests.py @@ -538,6 +538,8 @@ class S4UKerberosTests(KDCBaseTest): transited_service = f'host/{service1_name}@{service1_realm}' expected_transited_services.append(transited_service) + expect_pac = kdc_dict.pop('expect_pac', True) + kdc_exchange_dict = self.tgs_exchange_dict( expected_crealm=client_realm, expected_cname=client_cname, @@ -557,7 +559,8 @@ class S4UKerberosTests(KDCBaseTest): pac_options=pac_options, expect_edata=expect_edata, expected_proxy_target=expected_proxy_target, - expected_transited_services=expected_transited_services) + expected_transited_services=expected_transited_services, + expect_pac=expect_pac) self._generic_kdc_exchange(kdc_exchange_dict, cname=None, @@ -577,6 +580,18 @@ class S4UKerberosTests(KDCBaseTest): 'allow_delegation': True }) + def test_constrained_delegation_no_auth_data_required(self): + # Test constrained delegation. + self._run_delegation_test( + { + 'expected_error_mode': 0, + 'allow_delegation': True, + 'service2_opts': { + 'no_auth_data_required': True + }, + 'expect_pac': False + }) + def test_constrained_delegation_existing_delegation_info(self): # Test constrained delegation with an existing S4U_DELEGATION_INFO # structure in the PAC. @@ -624,6 +639,35 @@ class S4UKerberosTests(KDCBaseTest): 'modify_service_tgt_fn': self.remove_ticket_pac }) + def test_constrained_delegation_no_client_pac_no_auth_data_required(self): + # Test constrained delegation when the client service ticket does not + # contain a PAC. + self._run_delegation_test( + { + 'expected_error_mode': (KDC_ERR_BADOPTION, + KDC_ERR_MODIFIED), + 'allow_delegation': True, + 'modify_client_tkt_fn': self.remove_ticket_pac, + 'expect_edata': False, + 'service2_opts': { + 'no_auth_data_required': True + } + }) + + def test_constrained_delegation_no_service_pac_no_auth_data_required(self): + # Test constrained delegation when the service TGT does not contain a + # PAC. + self._run_delegation_test( + { + 'expected_error_mode': (KDC_ERR_BADOPTION, + KDC_ERR_MODIFIED), + 'allow_delegation': True, + 'modify_service_tgt_fn': self.remove_ticket_pac, + 'service2_opts': { + 'no_auth_data_required': True + } + }) + def test_constrained_delegation_non_forwardable(self): # Test constrained delegation with a non-forwardable ticket. self._run_delegation_test( @@ -645,6 +689,18 @@ class S4UKerberosTests(KDCBaseTest): 'allow_delegation': True }) + def test_rbcd_no_auth_data_required(self): + self._run_delegation_test( + { + 'expected_error_mode': 0, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'service2_opts': { + 'no_auth_data_required': True + }, + 'expect_pac': False + }) + def test_rbcd_existing_delegation_info(self): # Test constrained delegation with an existing S4U_DELEGATION_INFO # structure in the PAC. @@ -712,6 +768,55 @@ class S4UKerberosTests(KDCBaseTest): 'modify_service_tgt_fn': self.remove_ticket_pac }) + def test_rbcd_no_client_pac_no_auth_data_required_a(self): + # Test constrained delegation when the client service ticket does not + # contain a PAC, and an empty msDS-AllowedToDelegateTo attribute. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_MODIFIED, + 'expected_status': ntstatus.NT_STATUS_NOT_SUPPORTED, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_client_tkt_fn': self.remove_ticket_pac, + 'service2_opts': { + 'no_auth_data_required': True + } + }) + + def test_rbcd_no_client_pac_no_auth_data_required_b(self): + # Test constrained delegation when the client service ticket does not + # contain a PAC, and a non-empty msDS-AllowedToDelegateTo attribute. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_MODIFIED, + 'expected_status': ntstatus.NT_STATUS_NO_MATCH, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_client_tkt_fn': self.remove_ticket_pac, + 'service1_opts': { + 'delegation_to_spn': ('host/test') + }, + 'service2_opts': { + 'no_auth_data_required': True + } + }) + + def test_rbcd_no_service_pac_no_auth_data_required(self): + # Test constrained delegation when the service TGT does not contain a + # PAC. + self._run_delegation_test( + { + 'expected_error_mode': KDC_ERR_BADOPTION, + 'expected_status': + ntstatus.NT_STATUS_NOT_FOUND, + 'allow_rbcd': True, + 'pac_options': '0001', # supports RBCD + 'modify_service_tgt_fn': self.remove_ticket_pac, + 'service2_opts': { + 'no_auth_data_required': True + } + }) + def test_rbcd_non_forwardable(self): # Test resource-based constrained delegation with a non-forwardable # ticket. diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 683dbacb979..b1d7a1ebe8f 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -125,7 +125,7 @@ # S4U tests # ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_rbcd_old_checksum -^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_service_pac +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_service_pac\(.*\)$ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_existing_delegation_info ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_missing_client_checksum ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_a @@ -141,3 +141,9 @@ # KRB5KRB_ERR_RESPONSE_TOO_BIG in this specific case # ^samba4.krb5.kdc with machine account.as-req-pac-request.fl2000dc:local +# +# +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_auth_data_required +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_auth_data_required +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_no_auth_data_required_a +^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_no_auth_data_required_b -- 2.25.1 From ec38a49933d42e22d4823a8c4d1005f4a73f80ac Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Wed, 10 Aug 2016 23:31:14 +0000 Subject: [PATCH 142/159] HEIMDAL:kdc: Fix transit path validation CVE-2017-6594 Commit f469fc6 (2010-10-02) inadvertently caused the previous hop realm to not be added to the transit path of issued tickets. This may, in some cases, enable bypass of capath policy in Heimdal versions 1.5 through 7.2. Note, this may break sites that rely on the bug. With the bug some incomplete [capaths] worked, that should not have. These may now break authentication in some cross-realm configurations. (similar to heimdal commit b1e699103f08d6a0ca46a122193c9da65f6cf837) BUG: https://bugzilla.samba.org/show_bug.cgi?id=12998 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Reviewed-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Autobuild-User(master): Stefan Metzmacher Autobuild-Date(master): Wed Oct 20 10:58:37 UTC 2021 on sn-devel-184 (cherry picked from commit 7e961f3f7a815960ae25377d5b7515184d439690) --- source4/heimdal/kdc/krb5tgs.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c index 2de3b099199..7e9379db64a 100644 --- a/source4/heimdal/kdc/krb5tgs.c +++ b/source4/heimdal/kdc/krb5tgs.c @@ -409,8 +409,12 @@ fix_transited_encoding(krb5_context context, "Decoding transited encoding"); return ret; } + + /* + * If the realm of the presented tgt is neither the client nor the server + * realm, it is a transit realm and must be added to transited set. + */ if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) { - /* not us, so add the previous realm to transited set */ if (num_realms + 1 > UINT_MAX/sizeof(*realms)) { ret = ERANGE; goto free_realms; @@ -492,6 +496,7 @@ tgs_make_reply(krb5_context context, const char *server_name, hdb_entry_ex *client, krb5_principal client_principal, + const char *tgt_realm, hdb_entry_ex *krbtgt, krb5_pac mspac, uint16_t rodc_id, @@ -553,7 +558,7 @@ tgs_make_reply(krb5_context context, &tgt->transited, &et, krb5_principal_get_realm(context, client_principal), krb5_principal_get_realm(context, server->entry.principal), - krb5_principal_get_realm(context, krbtgt->entry.principal)); + tgt_realm); if(ret) goto out; @@ -1292,13 +1297,14 @@ tgs_build_reply(krb5_context context, HDB *clientdb, *s4u2self_impersonated_clientdb; krb5_realm ref_realm = NULL; EncTicketPart *tgt = &ticket->ticket; + const char *tgt_realm = /* Realm of TGT issuer */ + krb5_principal_get_realm(context, krbtgt->entry.principal); const EncryptionKey *ekey; krb5_keyblock sessionkey; krb5_kvno kvno; krb5_pac mspac = NULL; uint16_t rodc_id; krb5_boolean add_ticket_sig = FALSE; - hdb_entry_ex *krbtgt_out = NULL; METHOD_DATA enc_pa_data; @@ -2036,6 +2042,7 @@ server_lookup: spn, client, cp, + tgt_realm, krbtgt_out, mspac, rodc_id, -- 2.25.1 From 5618149ada61adb3ae5858e191096c03e7b1828f Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Wed, 8 Sep 2021 17:01:26 +1200 Subject: [PATCH 143/159] pytest/rodc_rwdc: try to avoid race. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14868 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Douglas Bagnall Reviewed-by: Stefan Metzmacher (cherry picked from commit a169e013e66bab15e594ce49b805edebfcd503cf) --- source4/dsdb/tests/python/rodc_rwdc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source4/dsdb/tests/python/rodc_rwdc.py b/source4/dsdb/tests/python/rodc_rwdc.py index a09d16a0163..b9ea18e5b68 100644 --- a/source4/dsdb/tests/python/rodc_rwdc.py +++ b/source4/dsdb/tests/python/rodc_rwdc.py @@ -251,6 +251,10 @@ class RodcRwdcCachedTests(password_lockout_base.BasePasswordTestCase): res = ldb_system.search(userdn, attrs=['unicodePwd']) self.assertTrue('unicodePwd' in res[0]) + # force replication here to flush any pending preloads (this + # was a racy test). + self.force_replication() + newpass = userpass + '!' # Forcing replication should blank out password (when changed) -- 2.25.1 From d191e00a5fc476781054b12c0370b6740922f8a7 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Mon, 20 Sep 2021 16:27:40 +1200 Subject: [PATCH 144/159] selftest: Increase account lockout windows to make test more realiable BUG: https://bugzilla.samba.org/show_bug.cgi?id=14868 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Stefan Metzmacher (cherry picked from commit 6292f0597f208d7953382341380921cf0fd0a8a8) --- source4/dsdb/tests/python/rodc_rwdc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source4/dsdb/tests/python/rodc_rwdc.py b/source4/dsdb/tests/python/rodc_rwdc.py index b9ea18e5b68..0340042e19d 100644 --- a/source4/dsdb/tests/python/rodc_rwdc.py +++ b/source4/dsdb/tests/python/rodc_rwdc.py @@ -289,14 +289,14 @@ class RodcRwdcCachedTests(password_lockout_base.BasePasswordTestCase): m = ldb.Message() m.dn = ldb.Dn(self.ldb, self.base_dn) - self.account_lockout_duration = 10 + self.account_lockout_duration = 15 account_lockout_duration_ticks = -int(self.account_lockout_duration * (1e7)) m["lockoutDuration"] = ldb.MessageElement(str(account_lockout_duration_ticks), ldb.FLAG_MOD_REPLACE, "lockoutDuration") - self.lockout_observation_window = 10 + self.lockout_observation_window = 15 lockout_observation_window_ticks = -int(self.lockout_observation_window * (1e7)) m["lockOutObservationWindow"] = ldb.MessageElement(str(lockout_observation_window_ticks), -- 2.25.1 From 7c0e2643f585bcdbc5bd4fc3056df969c7c158f7 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 6 Aug 2021 11:08:10 +1200 Subject: [PATCH 145/159] pytest: dynamic tests optionally add __doc__ BUG: https://bugzilla.samba.org/show_bug.cgi?id=14869 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Douglas Bagnall Reviewed-by: Stefan Metzmacher (cherry picked from commit aacb18f920349e13b562c7c97901a0be7b273137) --- python/samba/tests/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py index a5a8acdcc41..c75f9c1ebe7 100644 --- a/python/samba/tests/__init__.py +++ b/python/samba/tests/__init__.py @@ -69,7 +69,7 @@ class TestCase(unittest.TestCase): """A Samba test case.""" @classmethod - def generate_dynamic_test(cls, fnname, suffix, *args): + def generate_dynamic_test(cls, fnname, suffix, *args, doc=None): """ fnname is something like "test_dynamic_sum" suffix is something like "1plus2" @@ -82,6 +82,7 @@ class TestCase(unittest.TestCase): """ def fn(self): getattr(self, "_%s_with_args" % fnname)(*args) + fn.__doc__ = doc setattr(cls, "%s_%s" % (fnname, suffix), fn) @classmethod -- 2.25.1 From ff3f84525837d5fa0fe0372212a33db6a7f6ff52 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Fri, 8 Oct 2021 15:40:09 +1300 Subject: [PATCH 146/159] selftest: krb5 account creation: clarify account type as an enum This makes the code clearer with a symbolic constant rather than a True/False boolean. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14869 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Stefan Metzmacher (cherry picked from commit 49306f74eb29a2192019fab9260f9d242f9d5fd9) --- .../tests/krb5/as_canonicalization_tests.py | 7 ++- python/samba/tests/krb5/kdc_base_test.py | 63 ++++++++++++------- python/samba/tests/krb5/kdc_tgs_tests.py | 7 ++- .../ms_kile_client_principal_lookup_tests.py | 36 +++++++---- python/samba/tests/krb5/rodc_tests.py | 4 +- python/samba/tests/krb5/s4u_tests.py | 35 ++++++----- python/samba/tests/krb5/test_ccache.py | 11 ++-- 7 files changed, 100 insertions(+), 63 deletions(-) diff --git a/python/samba/tests/krb5/as_canonicalization_tests.py b/python/samba/tests/krb5/as_canonicalization_tests.py index 9538d0ae3cf..674fcb37101 100755 --- a/python/samba/tests/krb5/as_canonicalization_tests.py +++ b/python/samba/tests/krb5/as_canonicalization_tests.py @@ -171,9 +171,10 @@ class KerberosASCanonicalizationTests(KDCBaseTest): def machine_account_creds(self): if self.machine_creds is None: samdb = self.get_samdb() - self.machine_creds, _ = self.create_account(samdb, - MACHINE_NAME, - machine_account=True) + self.machine_creds, _ = self.create_account( + samdb, + MACHINE_NAME, + account_type=self.AccountType.COMPUTER) self.machine_creds.set_secure_channel_type(SEC_CHAN_WKSTA) self.machine_creds.set_kerberos_state(DONT_USE_KERBEROS) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 1fc15315b0b..7cd3c5255f2 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -23,6 +23,7 @@ import tempfile import binascii import collections import secrets +from enum import Enum, auto from collections import namedtuple import ldb @@ -90,6 +91,10 @@ class KDCBaseTest(RawKerberosTest): """ Base class for KDC tests. """ + class AccountType(Enum): + USER = auto() + COMPUTER = auto() + @classmethod def setUpClass(cls): super().setUpClass() @@ -230,7 +235,7 @@ class KDCBaseTest(RawKerberosTest): return default_enctypes - def create_account(self, samdb, name, machine_account=False, + def create_account(self, samdb, name, account_type=AccountType.USER, spn=None, upn=None, additional_details=None, ou=None, account_control=0): '''Create an account for testing. @@ -238,8 +243,10 @@ class KDCBaseTest(RawKerberosTest): which is used by tearDownClass to clean up the created accounts. ''' if ou is None: - guid = (DS_GUID_COMPUTERS_CONTAINER if machine_account - else DS_GUID_USERS_CONTAINER) + if account_type is account_type.COMPUTER: + guid = DS_GUID_COMPUTERS_CONTAINER + else: + guid = DS_GUID_USERS_CONTAINER ou = samdb.get_wellknown_dn(samdb.get_default_basedn(), guid) @@ -248,14 +255,17 @@ class KDCBaseTest(RawKerberosTest): # remove the account if it exists, this will happen if a previous test # run failed delete_force(samdb, dn) - if machine_account: - object_class = "computer" - account_name = "%s$" % name - account_control |= UF_WORKSTATION_TRUST_ACCOUNT - else: + if account_type is self.AccountType.USER: object_class = "user" account_name = name account_control |= UF_NORMAL_ACCOUNT + else: + object_class = "computer" + account_name = "%s$" % name + if account_type is self.AccountType.COMPUTER: + account_control |= UF_WORKSTATION_TRUST_ACCOUNT + else: + self.fail() password = generate_random_password(32, 32) utf16pw = ('"%s"' % password).encode('utf-16-le') @@ -267,6 +277,10 @@ class KDCBaseTest(RawKerberosTest): "userAccountControl": str(account_control), "unicodePwd": utf16pw} if spn is not None: + if isinstance(spn, str): + spn = spn.format(account=account_name) + else: + spn = tuple(s.format(account=account_name) for s in spn) details["servicePrincipalName"] = spn if upn is not None: details["userPrincipalName"] = upn @@ -280,10 +294,10 @@ class KDCBaseTest(RawKerberosTest): creds.set_domain(samdb.domain_netbios_name().upper()) creds.set_password(password) creds.set_username(account_name) - if machine_account: - creds.set_workstation(name) - else: + if account_type is self.AccountType.USER: creds.set_workstation('') + else: + creds.set_workstation(name) creds.set_dn(ldb.Dn(samdb, dn)) creds.set_spn(spn) # @@ -609,13 +623,14 @@ class KDCBaseTest(RawKerberosTest): return cleanup def get_cached_creds(self, *, - machine_account, + account_type, opts=None, use_cache=True): if opts is None: opts = {} opts_default = { + 'spn': None, 'allowed_replication': False, 'allowed_replication_mock': False, 'denied_replication': False, @@ -632,7 +647,7 @@ class KDCBaseTest(RawKerberosTest): } account_opts = { - 'machine_account': machine_account, + 'account_type': account_type, **opts_default, **opts } @@ -651,7 +666,8 @@ class KDCBaseTest(RawKerberosTest): return creds def create_account_opts(self, *, - machine_account, + account_type, + spn, allowed_replication, allowed_replication_mock, denied_replication, @@ -665,12 +681,13 @@ class KDCBaseTest(RawKerberosTest): delegation_from_dn, trusted_to_auth_for_delegation, fast_support): - if machine_account: - self.assertFalse(not_delegated) - else: + if account_type is self.AccountType.USER: + self.assertIsNone(spn) self.assertIsNone(delegation_to_spn) self.assertIsNone(delegation_from_dn) self.assertFalse(trusted_to_auth_for_delegation) + else: + self.assertFalse(not_delegated) samdb = self.get_samdb() rodc_samdb = self.get_rodc_samdb() @@ -707,13 +724,11 @@ class KDCBaseTest(RawKerberosTest): details['msDS-AllowedToActOnBehalfOfOtherIdentity'] = ( security_descriptor) - if machine_account: + if spn is None and account_type is not self.AccountType.USER: spn = 'host/' + user_name - else: - spn = None creds, dn = self.create_account(samdb, user_name, - machine_account=machine_account, + account_type=account_type, spn=spn, additional_details=details, account_control=user_account_control) @@ -787,7 +802,7 @@ class KDCBaseTest(RawKerberosTest): allow_missing_password=False, allow_missing_keys=True): def create_client_account(): - return self.get_cached_creds(machine_account=False) + return self.get_cached_creds(account_type=self.AccountType.USER) c = self._get_krb5_creds(prefix='CLIENT', allow_missing_password=allow_missing_password, @@ -799,7 +814,7 @@ class KDCBaseTest(RawKerberosTest): allow_missing_password=False, allow_missing_keys=True): def create_mach_account(): - return self.get_cached_creds(machine_account=True, + return self.get_cached_creds(account_type=self.AccountType.COMPUTER, opts={'fast_support': True}) c = self._get_krb5_creds(prefix='MAC', @@ -813,7 +828,7 @@ class KDCBaseTest(RawKerberosTest): allow_missing_keys=True): def create_service_account(): return self.get_cached_creds( - machine_account=True, + account_type=self.AccountType.COMPUTER, opts={ 'trusted_to_auth_for_delegation': True, 'fast_support': True diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py index 9d846a2c3ad..f36704f998c 100755 --- a/python/samba/tests/krb5/kdc_tgs_tests.py +++ b/python/samba/tests/krb5/kdc_tgs_tests.py @@ -148,7 +148,8 @@ class KdcTgsTests(KDCBaseTest): samdb = self.get_samdb() user_name = "tsttktusr" (uc, dn) = self.create_account(samdb, user_name) - (mc, _) = self.create_account(samdb, "tsttktmac", machine_account=True) + (mc, _) = self.create_account(samdb, "tsttktmac", + account_type=self.AccountType.COMPUTER) realm = uc.get_realm().lower() # Do the initial AS-REQ, should get a pre-authentication required @@ -282,7 +283,7 @@ class KdcTgsTests(KDCBaseTest): def test_client_no_auth_data_required(self): client_creds = self.get_cached_creds( - machine_account=False, + account_type=self.AccountType.USER, opts={'no_auth_data_required': True}) service_creds = self.get_service_creds() @@ -299,7 +300,7 @@ class KdcTgsTests(KDCBaseTest): def test_service_no_auth_data_required(self): client_creds = self.get_client_creds() service_creds = self.get_cached_creds( - machine_account=True, + account_type=self.AccountType.COMPUTER, opts={'no_auth_data_required': True}) tgt = self.get_tgt(client_creds) diff --git a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py b/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py index 2ee3d4a2a83..0aa3309b814 100755 --- a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py +++ b/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py @@ -95,7 +95,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): realm = uc.get_realm().lower() mach_name = "mskilemac" - (mc, _) = self.create_account(samdb, mach_name, machine_account=True) + (mc, _) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) # Do the initial AS-REQ, should get a pre-authentication required # response @@ -151,7 +152,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): # samdb = self.get_samdb() mach_name = "mskilemac" - (mc, dn) = self.create_account(samdb, mach_name, machine_account=True) + (mc, dn) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) realm = mc.get_realm().lower() # Do the initial AS-REQ, should get a pre-authentication required @@ -215,7 +217,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): realm = uc.get_realm().lower() mach_name = "mskilemac" - (mc, _) = self.create_account(samdb, mach_name, machine_account=True) + (mc, _) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) # Do the initial AS-REQ, should get a pre-authentication required # response @@ -286,7 +289,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): self.add_attribute(samdb, dn, "altSecurityIdentities", alt_sec) mach_name = "mskilemac" - (mc, _) = self.create_account(samdb, mach_name, machine_account=True) + (mc, _) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) # Do the initial AS-REQ, as we've set UF_DONT_REQUIRE_PREAUTH # we should get a valid AS-RESP @@ -351,7 +355,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): self.add_attribute(samdb, dn, "altSecurityIdentities", alt_sec) mach_name = "mskilemac" - (mc, _) = self.create_account(samdb, mach_name, machine_account=True) + (mc, _) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) # Do the initial AS-REQ, should get a pre-authentication required # response @@ -420,7 +425,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): self.add_attribute(samdb, dn, "altSecurityIdentities", alt_sec) mach_name = "mskilemac" - (mc, _) = self.create_account(samdb, mach_name, machine_account=True) + (mc, _) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) # Do the initial AS-REQ, should get a pre-authentication required # response @@ -459,7 +465,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): realm = uc.get_realm().lower() mach_name = "mskilemac" - (mc, _) = self.create_account(samdb, mach_name, machine_account=True) + (mc, _) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) # Do the initial AS-REQ, should get a pre-authentication required # response @@ -523,7 +530,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): ename = user_name + "@" + realm mach_name = "mskilemac" - (mc, _) = self.create_account(samdb, mach_name, machine_account=True) + (mc, _) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) # Do the initial AS-REQ, should get a pre-authentication required # response @@ -586,7 +594,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): realm = uc.get_realm().lower() mach_name = "mskilemac" - (mc, dn) = self.create_account(samdb, mach_name, machine_account=True) + (mc, dn) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) ename = mach_name + "@" + realm uname = mach_name + "$@" + realm @@ -661,7 +670,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): ename = alt_name + "@" + realm mach_name = "mskilemac" - (mc, _) = self.create_account(samdb, mach_name, machine_account=True) + (mc, _) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) # Do the initial AS-REQ, as we've set UF_DONT_REQUIRE_PREAUTH # we should get a valid AS-RESP @@ -728,7 +738,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): uname = user_name + "@" + realm mach_name = "mskilemac" - (mc, _) = self.create_account(samdb, mach_name, machine_account=True) + (mc, _) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) # Do the initial AS-REQ, should get a pre-authentication required # response @@ -798,7 +809,8 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest): ename = alt_name + "@" + realm mach_name = "mskilemac" - (mc, _) = self.create_account(samdb, mach_name, machine_account=True) + (mc, _) = self.create_account(samdb, mach_name, + account_type=self.AccountType.COMPUTER) # Do the initial AS-REQ, should get a pre-authentication required # response diff --git a/python/samba/tests/krb5/rodc_tests.py b/python/samba/tests/krb5/rodc_tests.py index 4579f9eb552..302ae865cf1 100755 --- a/python/samba/tests/krb5/rodc_tests.py +++ b/python/samba/tests/krb5/rodc_tests.py @@ -39,12 +39,12 @@ class RodcKerberosTests(KDCBaseTest): # and including the RODCIdentifier. def test_rodc_ticket_signature(self): user_creds = self.get_cached_creds( - machine_account=False, + account_type=self.AccountType.USER, opts={ 'revealed_to_rodc': True }) target_creds = self.get_cached_creds( - machine_account=True, + account_type=self.AccountType.COMPUTER, opts={ 'revealed_to_rodc': True }) diff --git a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py index bbb7135b55b..ea629d29706 100755 --- a/python/samba/tests/krb5/s4u_tests.py +++ b/python/samba/tests/krb5/s4u_tests.py @@ -220,12 +220,14 @@ class S4UKerberosTests(KDCBaseTest): def _run_s4u2self_test(self, kdc_dict): client_opts = kdc_dict.pop('client_opts', None) - client_creds = self.get_cached_creds(machine_account=False, - opts=client_opts) + client_creds = self.get_cached_creds( + account_type=self.AccountType.USER, + opts=client_opts) service_opts = kdc_dict.pop('service_opts', None) - service_creds = self.get_cached_creds(machine_account=True, - opts=service_opts) + service_creds = self.get_cached_creds( + account_type=self.AccountType.COMPUTER, + opts=service_opts) service_tgt = self.get_tgt(service_creds) modify_service_tgt_fn = kdc_dict.pop('modify_service_tgt_fn', None) @@ -432,8 +434,9 @@ class S4UKerberosTests(KDCBaseTest): def _run_delegation_test(self, kdc_dict): client_opts = kdc_dict.pop('client_opts', None) - client_creds = self.get_cached_creds(machine_account=False, - opts=client_opts) + client_creds = self.get_cached_creds( + account_type=self.AccountType.USER, + opts=client_opts) service1_opts = kdc_dict.pop('service1_opts', {}) service2_opts = kdc_dict.pop('service2_opts', {}) @@ -443,24 +446,28 @@ class S4UKerberosTests(KDCBaseTest): self.assertFalse(allow_delegation and allow_rbcd) if allow_rbcd: - service1_creds = self.get_cached_creds(machine_account=True, - opts=service1_opts) + service1_creds = self.get_cached_creds( + account_type=self.AccountType.COMPUTER, + opts=service1_opts) self.assertNotIn('delegation_from_dn', service2_opts) service2_opts['delegation_from_dn'] = str(service1_creds.get_dn()) - service2_creds = self.get_cached_creds(machine_account=True, - opts=service2_opts) + service2_creds = self.get_cached_creds( + account_type=self.AccountType.COMPUTER, + opts=service2_opts) else: - service2_creds = self.get_cached_creds(machine_account=True, - opts=service2_opts) + service2_creds = self.get_cached_creds( + account_type=self.AccountType.COMPUTER, + opts=service2_opts) if allow_delegation: self.assertNotIn('delegation_to_spn', service1_opts) service1_opts['delegation_to_spn'] = service2_creds.get_spn() - service1_creds = self.get_cached_creds(machine_account=True, - opts=service1_opts) + service1_creds = self.get_cached_creds( + account_type=self.AccountType.COMPUTER, + opts=service1_opts) client_tkt_options = kdc_dict.pop('client_tkt_options', 'forwardable') expected_flags = krb5_asn1.TicketFlags(client_tkt_options) diff --git a/python/samba/tests/krb5/test_ccache.py b/python/samba/tests/krb5/test_ccache.py index c44ea02d504..6a2b78398ac 100755 --- a/python/samba/tests/krb5/test_ccache.py +++ b/python/samba/tests/krb5/test_ccache.py @@ -55,11 +55,12 @@ class CcacheTests(KDCBaseTest): (user_credentials, _) = self.create_account(samdb, user_name) # Create the machine account. - (mach_credentials, _) = self.create_account(samdb, - mach_name, - machine_account=True, - spn="%s/%s" % (service, - mach_name)) + (mach_credentials, _) = self.create_account( + samdb, + mach_name, + account_type=self.AccountType.COMPUTER, + spn="%s/%s" % (service, + mach_name)) # Talk to the KDC to obtain the service ticket, which gets placed into # the cache. The machine account name has to match the name in the -- 2.25.1 From 60683736be7e7d27a6e68c6708b1e89612c49f5d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 18 Oct 2021 20:44:54 +1300 Subject: [PATCH 147/159] selftest: Remove duplicate setup of $base_dn and $ldbmodify These are already set up to the same values above for the full DC and correct values for the (strange) s4member environment. By not setting $base_dn again we avoid an error once we start checking for them. Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 (cherry picked from commit 2c0658d408f17af2abc223b0cb18d8d33e0ecd1a) --- selftest/target/Samba4.pm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index f58190706b1..09cedcb05f1 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -1108,10 +1108,6 @@ servicePrincipalName: http/testupnspn.$ctx->{dnsname} } # Change the userPrincipalName for jane - $ldbmodify = ${cmd_env}; - $ldbmodify .= Samba::bindir_path($self, "ldbmodify"); - $ldbmodify .= " --configfile=$ctx->{smb_conf}"; - $base_dn = "DC=".join(",DC=", split(/\./, $ctx->{realm})); $user_dn = "cn=jane,cn=users,$base_dn"; open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); -- 2.25.1 From 2fea719852110483c9af0fc55ba2ff240e9248a8 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 18 Oct 2021 11:55:14 +1300 Subject: [PATCH 148/159] selftest: Improve error handling and perl style when setting up users in Samba4.pm This catches errors and avoids using global varibles (the old style file handles are global). BUG: https://bugzilla.samba.org/show_bug.cgi?id=14869 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher (cherry picked from commit 459200caba04fd83ed650b9cdfe5b158cf9a149f) --- selftest/target/Samba4.pm | 72 ++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index 09cedcb05f1..615e8b240b4 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -239,12 +239,19 @@ sub wait_for_start($$) sub write_ldb_file($$$) { - my ($self, $file, $ldif) = @_; + my ($self, $file, $ldif_in) = @_; my $ldbadd = Samba::bindir_path($self, "ldbadd"); - open(LDIF, "|$ldbadd -H $file >/dev/null"); - print LDIF $ldif; - return(close(LDIF)); + open(my $ldif, "|$ldbadd -H $file > /dev/null") + or die "Failed to run $ldbadd: $!"; + print $ldif $ldif_in; + close($ldif); + + unless ($? == 0) { + warn("$ldbadd failed: $?"); + return undef; + } + return 1; } sub add_wins_config($$) @@ -946,6 +953,8 @@ sub provision_raw_step2($$$) { my ($self, $ctx, $ret) = @_; + my $ldif; + my $provision_cmd = join(" ", @{$ctx->{provision_options}}); unless (system($provision_cmd) == 0) { warn("Unable to provision: \n$provision_cmd\n"); @@ -991,17 +1000,23 @@ sub provision_raw_step2($$$) my $user_dn = "cn=$testallowed_account,cn=users,$base_dn"; $testallowed_account = "testallowed account"; - open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); - print LDIF "dn: $user_dn + open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb") + or die "Failed to run $ldbmodify: $!"; + print $ldif "dn: $user_dn changetype: modify replace: samAccountName samAccountName: $testallowed_account - "; - close(LDIF); + close($ldif); + unless ($? == 0) { + warn("$ldbmodify failed: $?"); + return undef; + } - open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); - print LDIF "dn: $user_dn + open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb") + or die "Failed to run $ldbmodify: $!"; + print $ldif "dn: $user_dn changetype: modify replace: userPrincipalName userPrincipalName: testallowed upn\@$ctx->{realm} @@ -1009,7 +1024,11 @@ replace: servicePrincipalName servicePrincipalName: host/testallowed - "; - close(LDIF); + close($ldif); + unless ($? == 0) { + warn("$ldbmodify failed: $?"); + return undef; + } $samba_tool_cmd = ${cmd_env}; $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") @@ -1020,14 +1039,19 @@ servicePrincipalName: host/testallowed } $user_dn = "cn=testdenied,cn=users,$base_dn"; - open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); - print LDIF "dn: $user_dn + open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb") + or die "Failed to run $ldbmodify: $!"; + print $ldif "dn: $user_dn changetype: modify replace: userPrincipalName userPrincipalName: testdenied_upn\@$ctx->{realm}.upn - "; - close(LDIF); + close($ldif); + unless ($? == 0) { + warn("$ldbmodify failed: $?"); + return undef; + } $samba_tool_cmd = ${cmd_env}; $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") @@ -1038,8 +1062,9 @@ userPrincipalName: testdenied_upn\@$ctx->{realm}.upn } $user_dn = "cn=testupnspn,cn=users,$base_dn"; - open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); - print LDIF "dn: $user_dn + open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb") + or die "Failed to run $ldbmodify: $!"; + print $ldif "dn: $user_dn changetype: modify replace: userPrincipalName userPrincipalName: http/testupnspn.$ctx->{dnsname}\@$ctx->{realm} @@ -1047,7 +1072,11 @@ replace: servicePrincipalName servicePrincipalName: http/testupnspn.$ctx->{dnsname} - "; - close(LDIF); + close($ldif); + unless ($? == 0) { + warn("$ldbmodify failed: $?"); + return undef; + } $samba_tool_cmd = ${cmd_env}; $samba_tool_cmd .= Samba::bindir_path($self, "samba-tool") @@ -1110,14 +1139,19 @@ servicePrincipalName: http/testupnspn.$ctx->{dnsname} # Change the userPrincipalName for jane $user_dn = "cn=jane,cn=users,$base_dn"; - open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); - print LDIF "dn: $user_dn + open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb") + or die "Failed to run $ldbmodify: $!"; + print $ldif "dn: $user_dn changetype: modify replace: userPrincipalName userPrincipalName: jane.doe\@$ctx->{realm} - "; - close(LDIF); + close($ldif); + unless ($? == 0) { + warn("$ldbmodify failed: $?"); + return undef; + } return $ret; } -- 2.25.1 From 1460a095fc895cc28d0a3bd4adb69d5f8dad4be1 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 4 Oct 2021 13:02:35 +0200 Subject: [PATCH 149/159] waf: Allow building with MIT KRB5 >= 1.20 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gssrpc/xdr.h:105:1: error: function declaration isn’t a prototype [-Werror=strict-prototypes] 105 | typedef bool_t (*xdrproc_t)(); | ^~~~~~~ This can't be fixed, as the protoype is variadic. It can take up to three arguments. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14870 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Andreas Schneider Reviewed-by: Stefan Metzmacher (cherry picked from commit 5d8e794551b5df835f07e2bd8348fef746144601) --- source4/kdc/wscript_build | 1 + 1 file changed, 1 insertion(+) diff --git a/source4/kdc/wscript_build b/source4/kdc/wscript_build index c1f9a478582..0edca94e75f 100644 --- a/source4/kdc/wscript_build +++ b/source4/kdc/wscript_build @@ -29,6 +29,7 @@ if bld.CONFIG_SET('SAMBA4_USES_HEIMDAL'): if bld.CONFIG_GET('SAMBA_USES_MITKDC'): bld.SAMBA_MODULE('service_kdc', source='kdc-service-mit.c', + cflags_end='-Wno-strict-prototypes', subsystem='service', init_function='server_service_mitkdc_init', deps=''' -- 2.25.1 From 5534e6e04e3d4c9cd4aaffe04286fd2d4b062d41 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 Oct 2021 18:04:55 +0200 Subject: [PATCH 150/159] selftest/Samba3: remove unused close(USERMAP); calls BUG: https://bugzilla.samba.org/show_bug.cgi?id=14869 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett [abartlet@samba.org backported from commit d998f7f8df215866ab32e05be772e24fc0b2131c as offline login tests are not in Samba 4.14] --- selftest/target/Samba3.pm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index 4afcc47b82b..f781fe7bf57 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -771,7 +771,6 @@ sub provision_ad_member mkdir($_, 0777) foreach(@dirs); - close(USERMAP); $ret->{DOMAIN} = $dcvars->{DOMAIN}; $ret->{REALM} = $dcvars->{REALM}; $ret->{DOMSID} = $dcvars->{DOMSID}; @@ -920,7 +919,6 @@ sub setup_ad_member_rfc2307 $ret or return undef; - close(USERMAP); $ret->{DOMAIN} = $dcvars->{DOMAIN}; $ret->{REALM} = $dcvars->{REALM}; $ret->{DOMSID} = $dcvars->{DOMSID}; @@ -1018,7 +1016,6 @@ sub setup_ad_member_idmap_rid $ret or return undef; - close(USERMAP); $ret->{DOMAIN} = $dcvars->{DOMAIN}; $ret->{REALM} = $dcvars->{REALM}; $ret->{DOMSID} = $dcvars->{DOMSID}; @@ -1118,7 +1115,6 @@ sub setup_ad_member_idmap_ad $ret or return undef; - close(USERMAP); $ret->{DOMAIN} = $dcvars->{DOMAIN}; $ret->{REALM} = $dcvars->{REALM}; $ret->{DOMSID} = $dcvars->{DOMSID}; -- 2.25.1 From 5ce5053e969781b90f328aec11ab1ff800d2cd34 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 5 Oct 2021 16:42:00 +0200 Subject: [PATCH 151/159] selftest/Samba3: replace (winbindd => "yes", skip_wait => 1) with (winbindd => "offline") This is much more flexible and concentrates the logic in a single place. We'll use winbindd => "offline" in other places soon. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14870 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett (cherry picked from commit 4dc3c68c9a28f71888e3d6dd3b1f0bcdb8fa45de) --- selftest/target/Samba3.pm | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index f781fe7bf57..4ec95e237a7 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -827,7 +827,7 @@ sub provision_ad_member nmbd => "yes", winbindd => "yes", smbd => "yes")) { - return undef; + return undef; } $ret->{DC_SERVER} = $dcvars->{SERVER}; @@ -1909,7 +1909,7 @@ sub check_or_start($$) { LOG_FILE => $env_vars->{WINBINDD_TEST_LOG}, PCAP_FILE => "env-$ENV{ENVNAME}-winbindd", }; - if ($winbindd ne "yes") { + if ($winbindd ne "yes" and $winbindd ne "offline") { $daemon_ctx->{SKIP_DAEMON} = 1; } @@ -3131,13 +3131,17 @@ sub wait_for_start($$$$$) } } - if ($winbindd eq "yes") { + if ($winbindd eq "yes" or $winbindd eq "offline") { print "checking for winbindd\n"; my $count = 0; $cmd = "SELFTEST_WINBINDD_SOCKET_DIR='$envvars->{SELFTEST_WINBINDD_SOCKET_DIR}' "; $cmd .= "NSS_WRAPPER_PASSWD='$envvars->{NSS_WRAPPER_PASSWD}' "; $cmd .= "NSS_WRAPPER_GROUP='$envvars->{NSS_WRAPPER_GROUP}' "; - $cmd .= Samba::bindir_path($self, "wbinfo") . " --ping-dc"; + if ($winbindd eq "yes") { + $cmd .= Samba::bindir_path($self, "wbinfo") . " --ping-dc"; + } elsif ($winbindd eq "offline") { + $cmd .= Samba::bindir_path($self, "wbinfo") . " --ping"; + } do { $ret = system($cmd); -- 2.25.1 From 2208433a58c2ab12fef297002d1b56b2da8f9438 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 20 Oct 2021 12:39:05 +1300 Subject: [PATCH 152/159] tests/krb5: Decrease length of test account prefix This allows us more room to test with different account names. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14874 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Stefan Metzmacher (cherry picked from commit a5a6296e57cab2b53617d997c37b4e92d4124cc7) --- python/samba/tests/krb5/kdc_base_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 7cd3c5255f2..8f453b464fd 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -108,7 +108,7 @@ class KDCBaseTest(RawKerberosTest): # An identifier to ensure created accounts have unique names. Windows # caches accounts based on usernames, so account names being different # across test runs avoids previous test runs affecting the results. - cls.account_base = f'krb5_{secrets.token_hex(5)}_' + cls.account_base = f'{secrets.token_hex(4)}_' cls.account_id = 0 # A set containing DNs of accounts created as part of testing. -- 2.25.1 From 3865de6879342e283835bb3b7cf2d7b58e222b2d Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 20 Oct 2021 12:41:39 +1300 Subject: [PATCH 153/159] tests/krb5: Allow specifying prefix or suffix for test account names BUG: https://bugzilla.samba.org/show_bug.cgi?id=14874 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Stefan Metzmacher (cherry picked from commit 7e39994ed341883ac4c8c257220c19dbf70c7bc5) --- python/samba/tests/krb5/kdc_base_test.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 8f453b464fd..d8f3969d228 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -630,6 +630,8 @@ class KDCBaseTest(RawKerberosTest): opts = {} opts_default = { + 'name_prefix': None, + 'name_suffix': None, 'spn': None, 'allowed_replication': False, 'allowed_replication_mock': False, @@ -667,6 +669,8 @@ class KDCBaseTest(RawKerberosTest): def create_account_opts(self, *, account_type, + name_prefix, + name_suffix, spn, allowed_replication, allowed_replication_mock, @@ -696,6 +700,10 @@ class KDCBaseTest(RawKerberosTest): user_name = self.account_base + str(self.account_id) type(self).account_id += 1 + if name_prefix is not None: + user_name = name_prefix + user_name + if name_suffix is not None: + user_name += name_suffix user_account_control = 0 if trusted_to_auth_for_delegation: -- 2.25.1 From 0585dd5f180220957717ff00918d92eb98c1af5a Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 20 Oct 2021 12:44:19 +1300 Subject: [PATCH 154/159] tests/krb5: Allow creating machine accounts without a trailing dollar BUG: https://bugzilla.samba.org/show_bug.cgi?id=14874 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Stefan Metzmacher (cherry picked from commit f4785ccfefe7c89f84ad847ca3c12f604172b321) --- python/samba/tests/krb5/kdc_base_test.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index d8f3969d228..35f168a3c83 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -237,7 +237,7 @@ class KDCBaseTest(RawKerberosTest): def create_account(self, samdb, name, account_type=AccountType.USER, spn=None, upn=None, additional_details=None, - ou=None, account_control=0): + ou=None, account_control=0, add_dollar=True): '''Create an account for testing. The dn of the created account is added to self.accounts, which is used by tearDownClass to clean up the created accounts. @@ -255,13 +255,14 @@ class KDCBaseTest(RawKerberosTest): # remove the account if it exists, this will happen if a previous test # run failed delete_force(samdb, dn) + account_name = name if account_type is self.AccountType.USER: object_class = "user" - account_name = name account_control |= UF_NORMAL_ACCOUNT else: object_class = "computer" - account_name = "%s$" % name + if add_dollar: + account_name += '$' if account_type is self.AccountType.COMPUTER: account_control |= UF_WORKSTATION_TRUST_ACCOUNT else: @@ -632,6 +633,7 @@ class KDCBaseTest(RawKerberosTest): opts_default = { 'name_prefix': None, 'name_suffix': None, + 'add_dollar': True, 'spn': None, 'allowed_replication': False, 'allowed_replication_mock': False, @@ -671,6 +673,7 @@ class KDCBaseTest(RawKerberosTest): account_type, name_prefix, name_suffix, + add_dollar, spn, allowed_replication, allowed_replication_mock, @@ -739,7 +742,8 @@ class KDCBaseTest(RawKerberosTest): account_type=account_type, spn=spn, additional_details=details, - account_control=user_account_control) + account_control=user_account_control, + add_dollar=add_dollar) keys = self.get_keys(samdb, dn) self.creds_set_keys(creds, keys) -- 2.25.1 From 4dfd80c056f615131382ea477d3050b299fb3c36 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 20 Oct 2021 12:45:08 +1300 Subject: [PATCH 155/159] tests/krb5: Allow specifying the UPN for test accounts BUG: https://bugzilla.samba.org/show_bug.cgi?id=14874 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Stefan Metzmacher (cherry picked from commit 889476d1754f8ce2a41557ed3bf5242c1293584e) --- python/samba/tests/krb5/kdc_base_test.py | 4 ++++ python/samba/tests/krb5/raw_testcase.py | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index 35f168a3c83..b24c6376ab0 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -300,6 +300,7 @@ class KDCBaseTest(RawKerberosTest): else: creds.set_workstation(name) creds.set_dn(ldb.Dn(samdb, dn)) + creds.set_upn(upn) creds.set_spn(spn) # # Save the account name so it can be deleted in tearDownClass @@ -634,6 +635,7 @@ class KDCBaseTest(RawKerberosTest): 'name_prefix': None, 'name_suffix': None, 'add_dollar': True, + 'upn': None, 'spn': None, 'allowed_replication': False, 'allowed_replication_mock': False, @@ -674,6 +676,7 @@ class KDCBaseTest(RawKerberosTest): name_prefix, name_suffix, add_dollar, + upn, spn, allowed_replication, allowed_replication_mock, @@ -740,6 +743,7 @@ class KDCBaseTest(RawKerberosTest): creds, dn = self.create_account(samdb, user_name, account_type=account_type, + upn=upn, spn=spn, additional_details=details, account_control=user_account_control, diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 0b9fe8e7a04..619a8d006b2 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -366,6 +366,7 @@ class KerberosCredentials(Credentials): self.forced_salt = None self.dn = None + self.upn = None self.spn = None def set_as_supported_enctypes(self, value): @@ -475,6 +476,12 @@ class KerberosCredentials(Credentials): def get_spn(self): return self.spn + def set_upn(self, upn): + self.upn = upn + + def get_upn(self): + return self.upn + class KerberosTicketCreds: def __init__(self, ticket, session_key, -- 2.25.1 From d4cf26226873f9e38a36d2fa6cb6dd78442ff42d Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 20 Oct 2021 12:45:47 +1300 Subject: [PATCH 156/159] tests/krb5: Fix account salt calculation to match Windows BUG: https://bugzilla.samba.org/show_bug.cgi?id=14874 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Stefan Metzmacher (cherry picked from commit 25bdf4c994e4fdb74abbacb1e22237f3f2cc37fe) --- python/samba/tests/krb5/raw_testcase.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 619a8d006b2..f352615db1f 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -454,13 +454,22 @@ class KerberosCredentials(Credentials): if self.forced_salt is not None: return self.forced_salt + upn = self.get_upn() + if upn is not None: + salt_name = upn.rsplit('@', 1)[0].replace('/', '') + else: + salt_name = self.get_username() + if self.get_workstation(): + salt_name = self.get_username().lower() + if salt_name[-1] == '$': + salt_name = salt_name[:-1] salt_string = '%shost%s.%s' % ( self.get_realm().upper(), - self.get_username().lower().rsplit('$', 1)[0], + salt_name, self.get_realm().lower()) else: - salt_string = self.get_realm().upper() + self.get_username() + salt_string = self.get_realm().upper() + salt_name return salt_string.encode('utf-8') -- 2.25.1 From e96ddf186e906226b94be6077720192f17ae3a1c Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 20 Oct 2021 12:46:36 +1300 Subject: [PATCH 157/159] tests/krb5: Add tests for account salt calculation BUG: https://bugzilla.samba.org/show_bug.cgi?id=14874 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Joseph Sutton Reviewed-by: Stefan Metzmacher [abartlet@samba.org backported from commit 46039baa81377df10e5b134e4bb064ed246795e4 as the no_preauth side of the testsuite shows differences in enctypes in Samba 4.14. The change is only in salt calculation so this is not vital] --- python/samba/tests/krb5/as_req_tests.py | 10 + python/samba/tests/krb5/salt_tests.py | 327 ++++++++++++++++++++++++ python/samba/tests/usage.py | 1 + selftest/knownfail.d/kdc-salt | 12 + selftest/knownfail_heimdal_kdc | 108 ++++++++ source4/selftest/tests.py | 8 + 6 files changed, 466 insertions(+) create mode 100755 python/samba/tests/krb5/salt_tests.py create mode 100644 selftest/knownfail.d/kdc-salt diff --git a/python/samba/tests/krb5/as_req_tests.py b/python/samba/tests/krb5/as_req_tests.py index 7d7baaebf24..08081928363 100755 --- a/python/samba/tests/krb5/as_req_tests.py +++ b/python/samba/tests/krb5/as_req_tests.py @@ -113,6 +113,13 @@ class AsReqKerberosTests(KDCBaseTest): def test_as_req_enc_timestamp(self): client_creds = self.get_client_creds() + self._run_as_req_enc_timestamp(client_creds) + + def test_as_req_enc_timestamp_mac(self): + client_creds = self.get_mach_creds() + self._run_as_req_enc_timestamp(client_creds) + + def _run_as_req_enc_timestamp(self, client_creds): client_account = client_creds.get_username() client_as_etypes = self.get_default_enctypes() client_kvno = client_creds.get_kvno() @@ -197,6 +204,9 @@ class AsReqKerberosTests(KDCBaseTest): pac_request=True) self.assertIsNotNone(as_rep) + return etype_info2 + + if __name__ == "__main__": global_asn1_print = False global_hexdump = False diff --git a/python/samba/tests/krb5/salt_tests.py b/python/samba/tests/krb5/salt_tests.py new file mode 100755 index 00000000000..ecbf618e40e --- /dev/null +++ b/python/samba/tests/krb5/salt_tests.py @@ -0,0 +1,327 @@ +#!/usr/bin/env python3 +# Unix SMB/CIFS implementation. +# Copyright (C) Stefan Metzmacher 2020 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import sys +import os + +import ldb + +from samba.tests.krb5.as_req_tests import AsReqKerberosTests +import samba.tests.krb5.kcrypto as kcrypto + +sys.path.insert(0, "bin/python") +os.environ["PYTHONUNBUFFERED"] = "1" + +global_asn1_print = False +global_hexdump = False + + +class SaltTests(AsReqKerberosTests): + + def setUp(self): + super().setUp() + self.do_asn1_print = global_asn1_print + self.do_hexdump = global_hexdump + + def _get_creds(self, *, + account_type, + opts=None): + try: + return self.get_cached_creds( + account_type=account_type, + opts=opts) + except ldb.LdbError: + self.fail() + + def _run_salt_test(self, client_creds): + expected_salt = self.get_salt(client_creds) + self.assertIsNotNone(expected_salt) + + etype_info2 = self._run_as_req_enc_timestamp(client_creds) + + self.assertEqual(etype_info2[0]['etype'], kcrypto.Enctype.AES256) + self.assertEqual(etype_info2[0]['salt'], expected_salt) + + def test_salt_at_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'name_suffix': 'foo@bar'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_at_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'name_suffix': 'foo@bar'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_at_case_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'name_suffix': 'Foo@bar'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_at_case_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'name_suffix': 'Foo@bar'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_double_at_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'name_suffix': 'foo@@bar'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_double_at_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'name_suffix': 'foo@@bar'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_at_start_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'name_prefix': '@foo'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_at_start_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'name_prefix': '@foo'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_at_end_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'name_suffix': 'foo@'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_at_end_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'name_suffix': 'foo@'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_at_end_no_dollar_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'name_suffix': 'foo@', + 'add_dollar': False}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_no_dollar_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'add_dollar': False}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_dollar_mid_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'name_suffix': 'foo$bar', + 'add_dollar': False}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_dollar_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'name_suffix': 'foo$bar'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_dollar_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'name_suffix': 'foo$bar'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_dollar_end_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'name_suffix': 'foo$'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_dollar_end_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'name_suffix': 'foo$'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'foo0'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'foo1'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_host_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'host/foo2'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_host_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'host/foo3'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_realm_user(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'foo4@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_realm_mac(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'foo5@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_host_realm_user(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'host/foo6@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_host_realm_mac(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'host/foo7@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_dollar_realm_user(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'foo8$@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_dollar_realm_mac(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'foo9$@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_host_dollar_realm_user(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'host/foo10$@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_host_dollar_realm_mac(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'host/foo11$@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_other_realm_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'foo12@other.realm'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_other_realm_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'foo13@other.realm'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_host_other_realm_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'host/foo14@other.realm'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_host_other_realm_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'host/foo15@other.realm'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_case_user(self): + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'Foo16'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_case_mac(self): + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'Foo17'}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_dollar_mid_realm_user(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'foo$18@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_dollar_mid_realm_mac(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'foo$19@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_host_dollar_mid_realm_user(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'host/foo$20@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_host_dollar_mid_realm_mac(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'host/foo$21@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_at_realm_user(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.USER, + opts={'upn': 'foo22@bar@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + def test_salt_upn_at_realm_mac(self): + realm = self.get_samdb().domain_dns_name() + client_creds = self._get_creds( + account_type=self.AccountType.COMPUTER, + opts={'upn': 'foo23@bar@' + realm}) + self._run_as_req_enc_timestamp(client_creds) + + +if __name__ == "__main__": + global_asn1_print = False + global_hexdump = False + import unittest + unittest.main() diff --git a/python/samba/tests/usage.py b/python/samba/tests/usage.py index 0a9aac1f2cc..af118242a1b 100644 --- a/python/samba/tests/usage.py +++ b/python/samba/tests/usage.py @@ -104,6 +104,7 @@ EXCLUDE_USAGE = { 'python/samba/tests/krb5/as_req_tests.py', 'python/samba/tests/krb5/fast_tests.py', 'python/samba/tests/krb5/rodc_tests.py', + 'python/samba/tests/krb5/salt_tests.py', } EXCLUDE_HELP = { diff --git a/selftest/knownfail.d/kdc-salt b/selftest/knownfail.d/kdc-salt new file mode 100644 index 00000000000..1a4ecd44624 --- /dev/null +++ b/selftest/knownfail.d/kdc-salt @@ -0,0 +1,12 @@ +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_case_mac +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_case_user +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_end_mac +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_end_no_dollar_mac +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_end_user +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_mac +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_start_mac +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_start_user +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_user +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_double_at_mac +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_double_at_user +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_upn_at_realm_user diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index b1d7a1ebe8f..b39b11c3c53 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -71,6 +71,114 @@ ^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes256_pac_False.fl2008r2dc ^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes256_pac_None.fl2008r2dc ^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes128_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes128_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes128_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes256_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes256_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes256_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_dummy_rc4_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_aes256_dummy_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_aes256_dummy_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_aes256_dummy_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_dummy_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_dummy_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_dummy_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_dummy_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_dummy_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_dummy_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_aes128_dummy_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_aes128_dummy_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_aes128_dummy_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_dummy_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_dummy_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_dummy_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_dummy_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_dummy_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_dummy_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes128_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes128_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes128_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes256_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes256_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes256_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_as_req_no_preauth_rc4_dummy_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes128_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes128_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes128_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes256_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes256_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes256_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_dummy_rc4_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_aes256_dummy_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_aes256_dummy_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_aes256_dummy_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_dummy_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_dummy_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_dummy_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_dummy_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_dummy_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_dummy_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_aes128_dummy_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_aes128_dummy_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_aes128_dummy_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_dummy_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_dummy_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_dummy_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_dummy_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_dummy_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_dummy_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes128_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes128_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes128_aes256_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes256_aes128_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes256_aes128_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes256_aes128_pac_True.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes256_pac_False.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes256_pac_None.fl2008r2dc +^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_as_req_no_preauth_rc4_dummy_aes256_pac_True.fl2008r2dc # # FAST tests # diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index c2a6256029f..c2175486aae 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -1429,6 +1429,14 @@ for env in ["fl2008r2dc", "fl2003dc"]: 'TKT_SIG_SUPPORT': tkt_sig_support }) +planoldpythontestsuite('fl2008r2dc', 'samba.tests.krb5.salt_tests', + environ={ + 'ADMIN_USERNAME': '$USERNAME', + 'ADMIN_PASSWORD': '$PASSWORD', + 'STRICT_CHECKING': '0', + 'FAST_SUPPORT': have_fast_support, + 'TKT_SIG_SUPPORT': tkt_sig_support + }) for env in ["rodc", "promoted_dc", "fl2000dc", "fl2008r2dc"]: if env == "rodc": -- 2.25.1 From bc7e3d0837d3f0c5910a5d16475410c1d3a2b989 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 19 Oct 2021 16:01:36 +1300 Subject: [PATCH 158/159] dsdb: Allow special chars like "@" in samAccountName when generating the salt BUG: https://bugzilla.samba.org/show_bug.cgi?id=14874 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher Autobuild-User(master): Stefan Metzmacher Autobuild-Date(master): Wed Oct 20 12:54:54 UTC 2021 on sn-devel-184 (cherry picked from commit 5eeb441b771a1ffe1ba1c69b72e8795f525a58ed) --- auth/credentials/credentials_krb5.c | 12 +- lib/krb5_wrap/krb5_samba.c | 192 +++++++++++++++--- lib/krb5_wrap/krb5_samba.h | 13 +- selftest/knownfail.d/kdc-salt | 11 - source3/passdb/machine_account_secrets.c | 10 +- .../dsdb/samdb/ldb_modules/password_hash.c | 23 ++- 6 files changed, 195 insertions(+), 66 deletions(-) diff --git a/auth/credentials/credentials_krb5.c b/auth/credentials/credentials_krb5.c index d7b1c430841..2338d9f114b 100644 --- a/auth/credentials/credentials_krb5.c +++ b/auth/credentials/credentials_krb5.c @@ -1200,12 +1200,12 @@ _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, break; } - ret = smb_krb5_salt_principal(realm, - username, /* sAMAccountName */ - upn, /* userPrincipalName */ - uac_flags, - mem_ctx, - &salt_principal); + ret = smb_krb5_salt_principal_str(realm, + username, /* sAMAccountName */ + upn, /* userPrincipalName */ + uac_flags, + mem_ctx, + &salt_principal); if (ret) { talloc_free(mem_ctx); return ret; diff --git a/lib/krb5_wrap/krb5_samba.c b/lib/krb5_wrap/krb5_samba.c index 20ce86c708d..63a6e951f80 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -456,19 +456,20 @@ int smb_krb5_get_pw_salt(krb5_context context, * * @see smb_krb5_salt_principal2data */ -int smb_krb5_salt_principal(const char *realm, +int smb_krb5_salt_principal(krb5_context krb5_ctx, + const char *realm, const char *sAMAccountName, const char *userPrincipalName, uint32_t uac_flags, - TALLOC_CTX *mem_ctx, - char **_salt_principal) + krb5_principal *salt_princ) { TALLOC_CTX *frame = talloc_stackframe(); char *upper_realm = NULL; const char *principal = NULL; int principal_len = 0; + krb5_error_code krb5_ret; - *_salt_principal = NULL; + *salt_princ = NULL; if (sAMAccountName == NULL) { TALLOC_FREE(frame); @@ -512,7 +513,6 @@ int smb_krb5_salt_principal(const char *realm, */ if (uac_flags & UF_TRUST_ACCOUNT_MASK) { int computer_len = 0; - char *tmp = NULL; computer_len = strlen(sAMAccountName); if (sAMAccountName[computer_len-1] == '$') { @@ -520,60 +520,186 @@ int smb_krb5_salt_principal(const char *realm, } if (uac_flags & UF_INTERDOMAIN_TRUST_ACCOUNT) { - principal = talloc_asprintf(frame, "krbtgt/%*.*s", - computer_len, computer_len, - sAMAccountName); - if (principal == NULL) { + const char *krbtgt = "krbtgt"; + krb5_ret = krb5_build_principal_ext(krb5_ctx, + salt_princ, + strlen(upper_realm), + upper_realm, + strlen(krbtgt), + krbtgt, + computer_len, + sAMAccountName, + 0); + if (krb5_ret != 0) { TALLOC_FREE(frame); - return ENOMEM; + return krb5_ret; } } else { - - tmp = talloc_asprintf(frame, "host/%*.*s.%s", - computer_len, computer_len, - sAMAccountName, realm); + const char *host = "host"; + char *tmp = NULL; + char *tmp_lower = NULL; + + tmp = talloc_asprintf(frame, "%*.*s.%s", + computer_len, + computer_len, + sAMAccountName, + realm); if (tmp == NULL) { TALLOC_FREE(frame); return ENOMEM; } - principal = strlower_talloc(frame, tmp); - TALLOC_FREE(tmp); - if (principal == NULL) { + tmp_lower = strlower_talloc(frame, tmp); + if (tmp_lower == NULL) { TALLOC_FREE(frame); return ENOMEM; } - } - principal_len = strlen(principal); + krb5_ret = krb5_build_principal_ext(krb5_ctx, + salt_princ, + strlen(upper_realm), + upper_realm, + strlen(host), + host, + strlen(tmp_lower), + tmp_lower, + 0); + if (krb5_ret != 0) { + TALLOC_FREE(frame); + return krb5_ret; + } + } } else if (userPrincipalName != NULL) { - char *p; + /* + * We parse the name not only to allow an easy + * replacement of the realm (no matter the realm in + * the UPN, the salt comes from the upper-case real + * realm, but also to correctly provide a salt when + * the UPN is host/foo.bar + * + * This can fail for a UPN of the form foo@bar@REALM + * (which is accepted by windows) however. + */ + krb5_ret = krb5_parse_name(krb5_ctx, + userPrincipalName, + salt_princ); - principal = userPrincipalName; - p = strchr(principal, '@'); - if (p != NULL) { - principal_len = PTR_DIFF(p, principal); - } else { - principal_len = strlen(principal); + if (krb5_ret != 0) { + TALLOC_FREE(frame); + return krb5_ret; + } + + /* + * No matter what realm (including none) in the UPN, + * the realm is replaced with our upper-case realm + */ + smb_krb5_principal_set_realm(krb5_ctx, + *salt_princ, + upper_realm); + if (krb5_ret != 0) { + krb5_free_principal(krb5_ctx, *salt_princ); + TALLOC_FREE(frame); + return krb5_ret; } } else { principal = sAMAccountName; principal_len = strlen(principal); - } - *_salt_principal = talloc_asprintf(mem_ctx, "%*.*s@%s", - principal_len, principal_len, - principal, upper_realm); - if (*_salt_principal == NULL) { - TALLOC_FREE(frame); - return ENOMEM; + krb5_ret = krb5_build_principal_ext(krb5_ctx, + salt_princ, + strlen(upper_realm), + upper_realm, + principal_len, + principal, + 0); + if (krb5_ret != 0) { + TALLOC_FREE(frame); + return krb5_ret; + } } TALLOC_FREE(frame); return 0; } +/** + * @brief This constructs the salt principal used by active directory + * + * Most Kerberos encryption types require a salt in order to + * calculate the long term private key for user/computer object + * based on a password. + * + * The returned _salt_principal is a string in forms like this: + * - host/somehost.example.com@EXAMPLE.COM + * - SomeAccount@EXAMPLE.COM + * - SomePrincipal@EXAMPLE.COM + * + * This is not the form that's used as salt, it's just + * the human readable form. It needs to be converted by + * smb_krb5_salt_principal2data(). + * + * @param[in] realm The realm the user/computer is added too. + * + * @param[in] sAMAccountName The sAMAccountName attribute of the object. + * + * @param[in] userPrincipalName The userPrincipalName attribute of the object + * or NULL is not available. + * + * @param[in] uac_flags UF_ACCOUNT_TYPE_MASKed userAccountControl field + * + * @param[in] mem_ctx The TALLOC_CTX to allocate _salt_principal. + * + * @param[out] _salt_principal The resulting principal as string. + * + * @retval 0 Success; otherwise - Kerberos error codes + * + * @see smb_krb5_salt_principal2data + */ +int smb_krb5_salt_principal_str(const char *realm, + const char *sAMAccountName, + const char *userPrincipalName, + uint32_t uac_flags, + TALLOC_CTX *mem_ctx, + char **_salt_principal_str) +{ + krb5_principal salt_principal = NULL; + char *salt_principal_malloc; + krb5_context krb5_ctx; + krb5_error_code krb5_ret + = smb_krb5_init_context_common(&krb5_ctx); + if (krb5_ret != 0) { + DBG_ERR("kerberos init context failed (%s)\n", + error_message(krb5_ret)); + return krb5_ret; + } + + krb5_ret = smb_krb5_salt_principal(krb5_ctx, + realm, + sAMAccountName, + userPrincipalName, + uac_flags, + &salt_principal); + + krb5_ret = krb5_unparse_name(krb5_ctx, salt_principal, + &salt_principal_malloc); + if (krb5_ret != 0) { + krb5_free_principal(krb5_ctx, salt_principal); + DBG_ERR("kerberos unparse of salt principal failed (%s)\n", + error_message(krb5_ret)); + return krb5_ret; + } + krb5_free_principal(krb5_ctx, salt_principal); + *_salt_principal_str + = talloc_strdup(mem_ctx, salt_principal_malloc); + krb5_free_unparsed_name(krb5_ctx, salt_principal_malloc); + + if (*_salt_principal_str == NULL) { + return ENOMEM; + } + return 0; +} + /** * @brief Converts the salt principal string into the salt data blob * diff --git a/lib/krb5_wrap/krb5_samba.h b/lib/krb5_wrap/krb5_samba.h index ca9a893e4f7..56a2a975278 100644 --- a/lib/krb5_wrap/krb5_samba.h +++ b/lib/krb5_wrap/krb5_samba.h @@ -350,12 +350,19 @@ krb5_error_code ms_suptypes_to_ietf_enctypes(TALLOC_CTX *mem_ctx, int smb_krb5_get_pw_salt(krb5_context context, krb5_const_principal host_princ, krb5_data *psalt); -int smb_krb5_salt_principal(const char *realm, +int smb_krb5_salt_principal(krb5_context krb5_ctx, + const char *realm, const char *sAMAccountName, const char *userPrincipalName, uint32_t uac_flags, - TALLOC_CTX *mem_ctx, - char **_salt_principal); + krb5_principal *salt_princ); + +int smb_krb5_salt_principal_str(const char *realm, + const char *sAMAccountName, + const char *userPrincipalName, + uint32_t uac_flags, + TALLOC_CTX *mem_ctx, + char **_salt_principal); int smb_krb5_salt_principal2data(krb5_context context, const char *salt_principal, TALLOC_CTX *mem_ctx, diff --git a/selftest/knownfail.d/kdc-salt b/selftest/knownfail.d/kdc-salt index 1a4ecd44624..a671e4d93eb 100644 --- a/selftest/knownfail.d/kdc-salt +++ b/selftest/knownfail.d/kdc-salt @@ -1,12 +1 @@ -^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_case_mac -^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_case_user -^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_end_mac -^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_end_no_dollar_mac -^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_end_user -^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_mac -^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_start_mac -^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_start_user -^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_at_user -^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_double_at_mac -^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_double_at_user ^samba.tests.krb5.salt_tests.samba.tests.krb5.salt_tests.SaltTests.test_salt_upn_at_realm_user diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index d81f79c705b..1964eb5a448 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -1574,11 +1574,11 @@ NTSTATUS secrets_store_JoinCtx(const struct libnet_JoinCtx *r) if (info->salt_principal == NULL && r->out.domain_is_ad) { char *p = NULL; - ret = smb_krb5_salt_principal(info->domain_info.dns_domain.string, - info->account_name, - NULL /* userPrincipalName */, - UF_WORKSTATION_TRUST_ACCOUNT, - info, &p); + ret = smb_krb5_salt_principal_str(info->domain_info.dns_domain.string, + info->account_name, + NULL /* userPrincipalName */, + UF_WORKSTATION_TRUST_ACCOUNT, + info, &p); if (ret != 0) { status = krb5_to_nt_status(ret); DBG_ERR("smb_krb5_salt_principal() failed " diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c index 5bdd23c13e9..9f38a31c8dd 100644 --- a/source4/dsdb/samdb/ldb_modules/password_hash.c +++ b/source4/dsdb/samdb/ldb_modules/password_hash.c @@ -688,8 +688,8 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io) { struct ldb_context *ldb; krb5_error_code krb5_ret; - char *salt_principal = NULL; - char *salt_data = NULL; + krb5_principal salt_principal = NULL; + krb5_data salt_data; krb5_data salt; krb5_keyblock key; krb5_data cleartext_data; @@ -700,11 +700,11 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io) cleartext_data.length = io->n.cleartext_utf8->length; uac_flags = io->u.userAccountControl & UF_ACCOUNT_TYPE_MASK; - krb5_ret = smb_krb5_salt_principal(io->ac->status->domain_data.realm, + krb5_ret = smb_krb5_salt_principal(io->smb_krb5_context->krb5_context, + io->ac->status->domain_data.realm, io->u.sAMAccountName, io->u.user_principal_name, uac_flags, - io->ac, &salt_principal); if (krb5_ret) { ldb_asprintf_errstring(ldb, @@ -718,8 +718,10 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io) /* * create salt from salt_principal */ - krb5_ret = smb_krb5_salt_principal2data(io->smb_krb5_context->krb5_context, - salt_principal, io->ac, &salt_data); + krb5_ret = smb_krb5_get_pw_salt(io->smb_krb5_context->krb5_context, + salt_principal, &salt_data); + + krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal); if (krb5_ret) { ldb_asprintf_errstring(ldb, "setup_kerberos_keys: " @@ -728,12 +730,17 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io) krb5_ret, io->ac)); return LDB_ERR_OPERATIONS_ERROR; } - io->g.salt = salt_data; /* now use the talloced copy of the salt */ - salt.data = discard_const(io->g.salt); + salt.data = talloc_strndup(io->ac, + (char *)salt_data.data, + salt_data.length); + io->g.salt = salt.data; salt.length = strlen(io->g.salt); + smb_krb5_free_data_contents(io->smb_krb5_context->krb5_context, + &salt_data); + /* * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of * the salt and the cleartext password -- 2.25.1 From f3eeec100f16186fed7cb7a226ac6a5be2475de2 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 22 Oct 2021 10:50:36 +1300 Subject: [PATCH 159/159] lib/krb5_wrap: Fix missing error check in new salt code CID 1492905: Control flow issues (DEADCODE) This was a regression in 5eeb441b771a1ffe1ba1c69b72e8795f525a58ed. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14874 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14881 Signed-off-by: Andrew Bartlett Reviewed-by: Andreas Schneider Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Sat Oct 23 08:07:13 UTC 2021 on sn-devel-184 (cherry picked from commit 5094d986b7686f057195dcb10764295b88967019) --- lib/krb5_wrap/krb5_samba.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/krb5_wrap/krb5_samba.c b/lib/krb5_wrap/krb5_samba.c index 63a6e951f80..fff5b4e2a22 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -594,9 +594,9 @@ int smb_krb5_salt_principal(krb5_context krb5_ctx, * No matter what realm (including none) in the UPN, * the realm is replaced with our upper-case realm */ - smb_krb5_principal_set_realm(krb5_ctx, - *salt_princ, - upper_realm); + krb5_ret = smb_krb5_principal_set_realm(krb5_ctx, + *salt_princ, + upper_realm); if (krb5_ret != 0) { krb5_free_principal(krb5_ctx, *salt_princ); TALLOC_FREE(frame); -- 2.25.1