diff options
Diffstat (limited to 'python/samba/tests/krb5/etype_tests.py')
-rwxr-xr-x | python/samba/tests/krb5/etype_tests.py | 153 |
1 files changed, 139 insertions, 14 deletions
diff --git a/python/samba/tests/krb5/etype_tests.py b/python/samba/tests/krb5/etype_tests.py index f4456c3cb4d..1a16518df94 100755 --- a/python/samba/tests/krb5/etype_tests.py +++ b/python/samba/tests/krb5/etype_tests.py @@ -32,6 +32,7 @@ from samba.tests.krb5.rfc4120_constants import ( ARCFOUR_HMAC_MD5, KDC_ERR_ETYPE_NOSUPP, ) +import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1 sys.path.insert(0, "bin/python") os.environ["PYTHONUNBUFFERED"] = "1" @@ -39,6 +40,7 @@ os.environ["PYTHONUNBUFFERED"] = "1" global_asn1_print = False global_hexdump = False +des_bits = security.KERB_ENCTYPE_DES_CBC_MD5 | security.KERB_ENCTYPE_DES_CBC_CRC rc4_bit = security.KERB_ENCTYPE_RC4_HMAC_MD5 aes128_bit = security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 aes256_bit = security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 @@ -62,11 +64,15 @@ class EtypeTests(KdcTgsBaseTests): self.default_supported_enctypes = lp.get( 'kdc default domain supported enctypes') - def _server_creds(self, supported=None): + def _server_creds(self, supported=None, force_nt4_hash=False, + account_type=None): + if account_type is None: + account_type= self.AccountType.COMPUTER return self.get_cached_creds( - account_type=self.AccountType.COMPUTER, + account_type=account_type, opts={ 'supported_enctypes': supported, + 'force_nt4_hash': force_nt4_hash, }) def only_non_etype_bits_set(self, bits): @@ -115,14 +121,38 @@ class EtypeTests(KdcTgsBaseTests): fast_bit | aes256_bit, ) - for requested_etypes in requested_etype_cases: - for supported_etypes in supported_etype_cases: - tname = (f'{supported_etypes}_supported_' - f'{requested_etypes}_requested') - targs = supported_etypes, requested_etypes - cls.generate_dynamic_test('test_etype_as', tname, *targs) - - def _test_etype_as_with_args(self, supported_bits, requested_etypes): + for _requested_etypes in requested_etype_cases: + _s = str(_requested_etypes) + _t = _s.maketrans(",", "_", "( )") + requested_etypes = _s.translate(_t) + + for _supported_etypes in supported_etype_cases: + if _supported_etypes is None: + supported_etypes = "None" + else: + supported_etypes = f'0x{_supported_etypes:X}' + + for account_type in ["member", "dc"]: + if account_type == "dc": + _account_type = cls.AccountType.SERVER + elif account_type == "member": + _account_type = cls.AccountType.COMPUTER + + for stored_type in ["aes_rc4", "rc4_only"]: + if stored_type == "aes_rc4": + force_nt4_hash = False + elif stored_type == "rc4_only": + force_nt4_hash = True + + tname = (f'{supported_etypes}_supported_' + f'{requested_etypes}_requested_' + f'{account_type}_account_' + f'stored_{stored_type}') + targs = _supported_etypes, _requested_etypes, _account_type, force_nt4_hash + cls.generate_dynamic_test('test_etype_as', tname, *targs) + cls.generate_dynamic_test('test_etype_tgs', tname, *targs) + + def _test_etype_as_with_args(self, supported_bits, requested_etypes, account_type, force_nt4_hash): # The ticket will be encrypted with the strongest enctype for which the # server explicitly declares support, falling back to RC4 if the server # has no declared supported encryption types. The enctype of the @@ -151,10 +181,17 @@ class EtypeTests(KdcTgsBaseTests): # If our fallback smb.conf option is set, force in RC4 support. virtual_bits |= rc4_bit + if force_nt4_hash and not (virtual_bits & rc4_bit): + virtual_bits |= rc4_bit + if virtual_bits & aes256_sk_bit: # If strong session keys are enabled, force in the AES bits. virtual_bits |= aes256_bit | aes128_bit + if account_type == self.AccountType.SERVER: + virtual_bits |= etype_bits + expected_error = 0 + virtual_etypes = KerberosCredentials.bits_to_etypes(virtual_bits) # The enctype of the session key is the first listed in the request @@ -169,25 +206,113 @@ class EtypeTests(KdcTgsBaseTests): # Get the credentials of the client and server accounts. creds = self.get_client_creds() - target_creds = self._server_creds(supported=supported_bits) + target_creds = self._server_creds(supported=supported_bits, + account_type=account_type, + force_nt4_hash=force_nt4_hash) + if account_type == self.AccountType.SERVER: + target_supported_etypes = target_creds.tgs_supported_enctypes + target_supported_etypes |= des_bits + target_supported_etypes |= etype_bits + target_creds.set_tgs_supported_enctypes(target_supported_etypes) + supported_bits |= (target_supported_etypes & etype_bits) - # Perform the TGS-REQ. + # We expect the ticket etype to be the strongest the server claims to + # support, with a fallback to RC4. + expected_etype = ARCFOUR_HMAC_MD5 + if not force_nt4_hash and supported_bits is not None: + if supported_bits & aes256_bit: + expected_etype = AES256_CTS_HMAC_SHA1_96 + elif supported_bits & aes128_bit: + expected_etype = AES128_CTS_HMAC_SHA1_96 + + # Perform the AS-REQ. ticket = self._as_req(creds, expected_error=expected_error, target_creds=target_creds, - etype=requested_etypes) + etype=requested_etypes, + expected_ticket_etype=expected_etype) if expected_error: # There's no more to check. Return. return + # Check the etypes of the ticket and session key. + self.assertEqual(expected_etype, ticket.decryption_key.etype) + self.assertEqual(expected_session_etype, ticket.session_key.etype) + + def _test_etype_tgs_with_args(self, supported_bits, requested_etypes, account_type, force_nt4_hash): + expected_error = 0 + + if not supported_bits: + # If msDS-SupportedEncryptionTypes is missing or set to zero, the + # default value, provided by smb.conf, is assumed. + supported_bits = self.default_supported_enctypes + + # If msDS-SupportedEncryptionTypes specifies only non-etype bits, we + # expect an error. + if self.only_non_etype_bits_set(supported_bits): + expected_error = KDC_ERR_ETYPE_NOSUPP + + virtual_bits = supported_bits + + if self.forced_rc4 and not (virtual_bits & rc4_bit): + # If our fallback smb.conf option is set, force in RC4 support. + virtual_bits |= rc4_bit + + if force_nt4_hash and not (virtual_bits & rc4_bit): + virtual_bits |= rc4_bit + + if virtual_bits & aes256_sk_bit: + # If strong session keys are enabled, force in the AES bits. + virtual_bits |= aes256_bit | aes128_bit + + if account_type == self.AccountType.SERVER: + virtual_bits |= etype_bits + expected_error = 0 + + virtual_etypes = KerberosCredentials.bits_to_etypes(virtual_bits) + + # The enctype of the session key is the first listed in the request + # that the server supports, implicitly or explicitly. + for requested_etype in requested_etypes: + if requested_etype in virtual_etypes: + expected_session_etype = requested_etype + break + else: + # If there is no such enctype, expect an error. + expected_error = KDC_ERR_ETYPE_NOSUPP + + # Get the credentials of the client and server accounts. + creds = self.get_client_creds() + tgt = self.get_tgt(creds) + target_creds = self._server_creds(supported=supported_bits, + account_type=account_type, + force_nt4_hash=force_nt4_hash) + if account_type == self.AccountType.SERVER: + target_supported_etypes = target_creds.tgs_supported_enctypes + target_supported_etypes |= des_bits + target_supported_etypes |= etype_bits + target_creds.set_tgs_supported_enctypes(target_supported_etypes) + supported_bits |= (target_supported_etypes & etype_bits) + # We expect the ticket etype to be the strongest the server claims to # support, with a fallback to RC4. expected_etype = ARCFOUR_HMAC_MD5 - if supported_bits is not None: + if not force_nt4_hash and supported_bits is not None: if supported_bits & aes256_bit: expected_etype = AES256_CTS_HMAC_SHA1_96 elif supported_bits & aes128_bit: expected_etype = AES128_CTS_HMAC_SHA1_96 + # Perform the TGS-REQ. + ticket = self._tgs_req(tgt, expected_error=expected_error, + target_creds=target_creds, + kdc_options=str(krb5_asn1.KDCOptions('canonicalize')), + expected_supported_etypes=target_creds.tgs_supported_enctypes, + expected_ticket_etype=expected_etype, + etypes=requested_etypes) + if expected_error: + # There's no more to check. Return. + return + # Check the etypes of the ticket and session key. self.assertEqual(expected_etype, ticket.decryption_key.etype) self.assertEqual(expected_session_etype, ticket.session_key.etype) |