diff options
author | Joseph Sutton <josephsutton@catalyst.net.nz> | 2022-11-18 12:11:39 +1300 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2022-12-14 11:39:17 +0000 |
commit | 64bfe0ef7868b23e12f465ca9a37f8a8ee161a70 (patch) | |
tree | d614ec1af20031e5fda913ac2186af4c45782813 /python/samba | |
parent | 123b3c056af8dc3e024e22e49be6d8dd54b29b49 (diff) | |
download | samba-64bfe0ef7868b23e12f465ca9a37f8a8ee161a70.tar.gz |
CVE-2022-37966 selftest: Add tests for Kerberos session key behaviour since ENC_HMAC_SHA1_96_AES256_SK was added
ENC_HMAC_SHA1_96_AES256_SK is a flag introduced for by Microsoft in this CVE
to indicate that additionally, AES session keys are available.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15237
Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
(similar to commit 371d7e63fcb966ab54915a3dedb888d48adbf0c0)
[jsutton@samba.org Removed unneeded fast_tests.py change, added
non_etype_bits in raw_testcase.py, fixed conflicts in knownfails and
tests.py]
Diffstat (limited to 'python/samba')
-rwxr-xr-x | python/samba/tests/krb5/etype_tests.py | 418 | ||||
-rw-r--r-- | python/samba/tests/krb5/kdc_base_test.py | 60 | ||||
-rwxr-xr-x | python/samba/tests/krb5/kdc_tgs_tests.py | 45 | ||||
-rw-r--r-- | python/samba/tests/krb5/raw_testcase.py | 29 | ||||
-rw-r--r-- | python/samba/tests/krb5/rfc4120_constants.py | 1 | ||||
-rwxr-xr-x | python/samba/tests/krb5/s4u_tests.py | 3 |
6 files changed, 424 insertions, 132 deletions
diff --git a/python/samba/tests/krb5/etype_tests.py b/python/samba/tests/krb5/etype_tests.py index 37dab1eab8d..f4456c3cb4d 100755 --- a/python/samba/tests/krb5/etype_tests.py +++ b/python/samba/tests/krb5/etype_tests.py @@ -17,13 +17,17 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # +import itertools import sys import os from samba.dcerpc import security +from samba.tests import DynamicTestCase from samba.tests.krb5.kdc_tgs_tests import KdcTgsBaseTests +from samba.tests.krb5.raw_testcase import KerberosCredentials from samba.tests.krb5.rfc4120_constants import ( + AES128_CTS_HMAC_SHA1_96, AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5, KDC_ERR_ETYPE_NOSUPP, @@ -35,48 +39,165 @@ os.environ["PYTHONUNBUFFERED"] = "1" global_asn1_print = False global_hexdump = False +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 +aes256_sk_bit = security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96_SK +fast_bit = security.KERB_ENCTYPE_FAST_SUPPORTED +etype_bits = rc4_bit | aes128_bit | aes256_bit +extra_bits = aes256_sk_bit | fast_bit + + +@DynamicTestCase class EtypeTests(KdcTgsBaseTests): def setUp(self): super().setUp() self.do_asn1_print = global_asn1_print self.do_hexdump = global_hexdump - # Perform an AS-REQ for a service ticket, specifying AES. The request - # should fail with an error. - def test_as_aes_requested(self): - creds = self.get_mach_creds() - target_creds = self.get_service_creds() + self.default_supported_enctypes = self.default_etypes + if self.default_supported_enctypes is None: + lp = self.get_lp() + self.default_supported_enctypes = lp.get( + 'kdc default domain supported enctypes') - self._as_req(creds, expected_error=KDC_ERR_ETYPE_NOSUPP, - target_creds=target_creds, - etype=(AES256_CTS_HMAC_SHA1_96,)) - - # Perform an AS-REQ for a service ticket, specifying RC4. The resulting - # ticket should be encrypted with RC4, with an RC4 session key. - def test_as_rc4_requested(self): - creds = self.get_mach_creds() - target_creds = self.get_service_creds() + def _server_creds(self, supported=None): + return self.get_cached_creds( + account_type=self.AccountType.COMPUTER, + opts={ + 'supported_enctypes': supported, + }) - ticket = self._as_req(creds, expected_error=0, + def only_non_etype_bits_set(self, bits): + return bits is not None and ( + bits & extra_bits and + not (bits & etype_bits)) + + @classmethod + def setUpDynamicTestCases(cls): + all_etypes = (AES256_CTS_HMAC_SHA1_96, + AES128_CTS_HMAC_SHA1_96, + ARCFOUR_HMAC_MD5) + + # An iterator yielding all permutations consisting of at least one + # etype. + requested_etype_cases = itertools.chain.from_iterable( + itertools.permutations(all_etypes, x) + for x in range(1, len(all_etypes) + 1)) + + # Some combinations of msDS-SupportedEncryptionTypes bits to be set on + # the target server. + supported_etype_cases = ( + # Not set. + None, + # Every possible combination of RC4, AES128, AES256, and AES256-SK. + 0, + rc4_bit, + aes256_sk_bit, + aes256_sk_bit | rc4_bit, + aes256_bit, + aes256_bit | rc4_bit, + aes256_bit | aes256_sk_bit, + aes256_bit | aes256_sk_bit | rc4_bit, + aes128_bit, + aes128_bit | rc4_bit, + aes128_bit | aes256_sk_bit, + aes128_bit | aes256_sk_bit | rc4_bit, + aes128_bit | aes256_bit, + aes128_bit | aes256_bit | rc4_bit, + aes128_bit | aes256_bit | aes256_sk_bit, + aes128_bit | aes256_bit | aes256_sk_bit | rc4_bit, + # Some combinations with an extra bit (the FAST-supported bit) set. + fast_bit, + fast_bit | rc4_bit, + fast_bit | aes256_sk_bit, + 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): + # 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 + # session key is the first enctype listed in the request that the + # server supports, taking the AES-SK bit as an indication of support + # for both AES types. + + # If none of the enctypes in the request are supported by the target + # server, implicitly or explicitly, return ETYPE_NOSUPP. + + 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 virtual_bits & aes256_sk_bit: + # If strong session keys are enabled, force in the AES bits. + virtual_bits |= aes256_bit | aes128_bit + + 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() + target_creds = self._server_creds(supported=supported_bits) + + # Perform the TGS-REQ. + ticket = self._as_req(creds, expected_error=expected_error, target_creds=target_creds, - etype=(ARCFOUR_HMAC_MD5,)) - - self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype) - self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype) + etype=requested_etypes) + if expected_error: + # There's no more to check. Return. + return + + # 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 supported_bits & aes256_bit: + expected_etype = AES256_CTS_HMAC_SHA1_96 + elif supported_bits & aes128_bit: + expected_etype = AES128_CTS_HMAC_SHA1_96 + + # 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) # Perform an AS-REQ for a service ticket, specifying AES, when the target # service only supports AES. The resulting ticket should be encrypted with # AES, with an AES session key. def test_as_aes_supported_aes_requested(self): - creds = self.get_mach_creds() - - target_creds = self.get_cached_creds( - account_type=self.AccountType.COMPUTER, - opts={ - 'supported_enctypes': - security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96, - }) + creds = self.get_client_creds() + target_creds = self._server_creds(supported=aes256_bit) ticket = self._as_req(creds, expected_error=0, target_creds=target_creds, @@ -86,36 +207,71 @@ class EtypeTests(KdcTgsBaseTests): self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype) # Perform an AS-REQ for a service ticket, specifying RC4, when the target - # service only supports AES. The resulting ticket should be encrypted with - # AES, with an RC4 session key. + # service only supports AES. The request should fail with an error. def test_as_aes_supported_rc4_requested(self): - creds = self.get_mach_creds() + creds = self.get_client_creds() + target_creds = self._server_creds(supported=aes256_bit) - target_creds = self.get_cached_creds( - account_type=self.AccountType.COMPUTER, - opts={ - 'supported_enctypes': - security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96, - }) + if self.forced_rc4: + expected_error = 0 + expected_session_etype = ARCFOUR_HMAC_MD5 + else: + expected_error = KDC_ERR_ETYPE_NOSUPP + expected_session_etype = AES256_CTS_HMAC_SHA1_96 - ticket = self._as_req(creds, expected_error=0, + ticket = self._as_req(creds, expected_error=expected_error, target_creds=target_creds, etype=(ARCFOUR_HMAC_MD5,)) + if not self.forced_rc4: + return + self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype) - self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype) + self.assertEqual(expected_session_etype, ticket.session_key.etype) + + # Perform an AS-REQ for a service ticket, specifying AES, when the target + # service only supports AES, and supports AES256 session keys. The + # resulting ticket should be encrypted with AES, with an AES session key. + def test_as_aes_supported_aes_session_aes_requested(self): + creds = self.get_client_creds() + target_creds = self._server_creds(supported=aes256_bit | aes256_sk_bit) + + ticket = self._as_req(creds, expected_error=0, + target_creds=target_creds, + etype=(AES256_CTS_HMAC_SHA1_96,)) + + self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype) + self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype) + + # Perform an AS-REQ for a service ticket, specifying RC4, when the target + # service only supports AES, and supports AES256 session keys. The request + # should fail with an error. + def test_as_aes_supported_aes_session_rc4_requested(self): + creds = self.get_client_creds() + target_creds = self._server_creds(supported=aes256_bit | aes256_sk_bit) + + if self.forced_rc4: + expected_error = 0 + expected_session_etype = ARCFOUR_HMAC_MD5 + else: + expected_error = KDC_ERR_ETYPE_NOSUPP + expected_session_etype = AES256_CTS_HMAC_SHA1_96 + + ticket = self._as_req(creds, expected_error=expected_error, + target_creds=target_creds, + etype=(ARCFOUR_HMAC_MD5,)) + + if not self.forced_rc4: + return + + self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype) + self.assertEqual(expected_session_etype, ticket.session_key.etype) # Perform an AS-REQ for a service ticket, specifying AES, when the target # service only supports RC4. The request should fail with an error. def test_as_rc4_supported_aes_requested(self): - creds = self.get_mach_creds() - - target_creds = self.get_cached_creds( - account_type=self.AccountType.COMPUTER, - opts={ - 'supported_enctypes': - security.KERB_ENCTYPE_RC4_HMAC_MD5, - }) + creds = self.get_client_creds() + target_creds = self._server_creds(supported=rc4_bit) self._as_req(creds, expected_error=KDC_ERR_ETYPE_NOSUPP, target_creds=target_creds, @@ -125,14 +281,8 @@ class EtypeTests(KdcTgsBaseTests): # service only supports RC4. The resulting ticket should be encrypted with # RC4, with an RC4 session key. def test_as_rc4_supported_rc4_requested(self): - creds = self.get_mach_creds() - - target_creds = self.get_cached_creds( - account_type=self.AccountType.COMPUTER, - opts={ - 'supported_enctypes': - security.KERB_ENCTYPE_RC4_HMAC_MD5, - }) + creds = self.get_client_creds() + target_creds = self._server_creds(supported=rc4_bit) ticket = self._as_req(creds, expected_error=0, target_creds=target_creds, @@ -141,29 +291,31 @@ class EtypeTests(KdcTgsBaseTests): self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype) self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype) - # Perform a TGS-REQ for a service ticket, specifying AES. The request - # should fail with an error. - def test_tgs_aes_requested(self): - creds = self.get_mach_creds() - tgt = self.get_tgt(creds) - - target_creds = self.get_mach_creds() + # Perform an AS-REQ for a service ticket, specifying AES, when the target + # service only supports RC4, but supports AES256 session keys. The + # resulting ticket should be encrypted with RC4, with an AES256 session + # key. + def test_as_rc4_supported_aes_session_aes_requested(self): + creds = self.get_client_creds() + target_creds = self._server_creds(supported=rc4_bit | aes256_sk_bit) - self._tgs_req(tgt, expected_error=KDC_ERR_ETYPE_NOSUPP, - target_creds=target_creds, - etypes=(AES256_CTS_HMAC_SHA1_96,)) + ticket = self._as_req(creds, expected_error=0, + target_creds=target_creds, + etype=(AES256_CTS_HMAC_SHA1_96,)) - # Perform a TGS-REQ for a service ticket, specifying RC4. The resulting - # ticket should be encrypted with RC4, with an RC4 session key. - def test_tgs_rc4_requested(self): - creds = self.get_mach_creds() - tgt = self.get_tgt(creds) + self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype) + self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype) - target_creds = self.get_mach_creds() + # Perform an AS-REQ for a service ticket, specifying RC4, when the target + # service only supports RC4, but supports AES256 session keys. The + # resulting ticket should be encrypted with RC4, with an RC4 session key. + def test_as_rc4_supported_aes_session_rc4_requested(self): + creds = self.get_client_creds() + target_creds = self._server_creds(supported=rc4_bit | aes256_sk_bit) - ticket = self._tgs_req(tgt, expected_error=0, - target_creds=target_creds, - etypes=(ARCFOUR_HMAC_MD5,)) + ticket = self._as_req(creds, expected_error=0, + target_creds=target_creds, + etype=(ARCFOUR_HMAC_MD5,)) self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype) self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype) @@ -172,15 +324,10 @@ class EtypeTests(KdcTgsBaseTests): # service only supports AES. The resulting ticket should be encrypted with # AES, with an AES session key. def test_tgs_aes_supported_aes_requested(self): - creds = self.get_mach_creds() + creds = self.get_client_creds() tgt = self.get_tgt(creds) - target_creds = self.get_cached_creds( - account_type=self.AccountType.COMPUTER, - opts={ - 'supported_enctypes': - security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96, - }) + target_creds = self._server_creds(supported=aes256_bit) ticket = self._tgs_req(tgt, expected_error=0, target_creds=target_creds, @@ -190,38 +337,75 @@ class EtypeTests(KdcTgsBaseTests): self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype) # Perform a TGS-REQ for a service ticket, specifying RC4, when the target - # service only supports AES. The resulting ticket should be encrypted with - # AES, with an RC4 session key. + # service only supports AES. The request should fail with an error. def test_tgs_aes_supported_rc4_requested(self): - creds = self.get_mach_creds() + creds = self.get_client_creds() tgt = self.get_tgt(creds) - target_creds = self.get_cached_creds( - account_type=self.AccountType.COMPUTER, - opts={ - 'supported_enctypes': - security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96, - }) + target_creds = self._server_creds(supported=aes256_bit) + + if self.forced_rc4: + expected_error = 0 + else: + expected_error = KDC_ERR_ETYPE_NOSUPP + + ticket = self._tgs_req(tgt, expected_error=expected_error, + target_creds=target_creds, + etypes=(ARCFOUR_HMAC_MD5,)) + + if not self.forced_rc4: + return + + self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype) + self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype) + + # Perform a TGS-REQ for a service ticket, specifying AES, when the target + # service only supports AES, and supports AES256 session keys. The + # resulting ticket should be encrypted with AES, with an AES session key. + def test_tgs_aes_supported_aes_session_aes_requested(self): + creds = self.get_client_creds() + tgt = self.get_tgt(creds) + + target_creds = self._server_creds(supported=aes256_bit | aes256_sk_bit) ticket = self._tgs_req(tgt, expected_error=0, target_creds=target_creds, + etypes=(AES256_CTS_HMAC_SHA1_96,)) + + self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype) + self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype) + + # Perform a TGS-REQ for a service ticket, specifying RC4, when the target + # service only supports AES, and supports AES256 session keys. The request + # should fail with an error. + def test_tgs_aes_supported_aes_session_rc4_requested(self): + creds = self.get_client_creds() + tgt = self.get_tgt(creds) + + target_creds = self._server_creds(supported=aes256_bit | aes256_sk_bit) + + if self.forced_rc4: + expected_error = 0 + else: + expected_error = KDC_ERR_ETYPE_NOSUPP + + ticket = self._tgs_req(tgt, expected_error=expected_error, + target_creds=target_creds, etypes=(ARCFOUR_HMAC_MD5,)) + if not self.forced_rc4: + return + self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype) self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype) # Perform a TGS-REQ for a service ticket, specifying AES, when the target # service only supports RC4. The request should fail with an error. def test_tgs_rc4_supported_aes_requested(self): - creds = self.get_mach_creds() + creds = self.get_client_creds() tgt = self.get_tgt(creds) - target_creds = self.get_cached_creds( - account_type=self.AccountType.COMPUTER, - opts={ - 'supported_enctypes': - security.KERB_ENCTYPE_RC4_HMAC_MD5, - }) + target_creds = self._server_creds(supported=rc4_bit) self._tgs_req(tgt, expected_error=KDC_ERR_ETYPE_NOSUPP, target_creds=target_creds, @@ -231,15 +415,43 @@ class EtypeTests(KdcTgsBaseTests): # service only supports RC4. The resulting ticket should be encrypted with # RC4, with an RC4 session key. def test_tgs_rc4_supported_rc4_requested(self): - creds = self.get_mach_creds() + creds = self.get_client_creds() tgt = self.get_tgt(creds) - target_creds = self.get_cached_creds( - account_type=self.AccountType.COMPUTER, - opts={ - 'supported_enctypes': - security.KERB_ENCTYPE_RC4_HMAC_MD5, - }) + target_creds = self._server_creds(supported=rc4_bit) + + ticket = self._tgs_req(tgt, expected_error=0, + target_creds=target_creds, + etypes=(ARCFOUR_HMAC_MD5,)) + + self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype) + self.assertEqual(ARCFOUR_HMAC_MD5, ticket.session_key.etype) + + # Perform a TGS-REQ for a service ticket, specifying AES, when the target + # service only supports RC4, but supports AES256 session keys. The + # resulting ticket should be encrypted with RC4, with an AES256 session + # key. + def test_tgs_rc4_supported_aes_session_aes_requested(self): + creds = self.get_client_creds() + tgt = self.get_tgt(creds) + + target_creds = self._server_creds(supported=rc4_bit | aes256_sk_bit) + + ticket = self._tgs_req(tgt, expected_error=0, + target_creds=target_creds, + etypes=(AES256_CTS_HMAC_SHA1_96,)) + + self.assertEqual(ARCFOUR_HMAC_MD5, ticket.decryption_key.etype) + self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.session_key.etype) + + # Perform a TGS-REQ for a service ticket, specifying RC4, when the target + # service only supports RC4, but supports AES256 session keys. The + # resulting ticket should be encrypted with RC4, with an RC4 session key. + def test_tgs_rc4_supported_aes_session_rc4_requested(self): + creds = self.get_client_creds() + tgt = self.get_tgt(creds) + + target_creds = self._server_creds(supported=rc4_bit | aes256_sk_bit) ticket = self._tgs_req(tgt, expected_error=0, target_creds=target_creds, diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py index f5bfa68c433..e92049a2ecd 100644 --- a/python/samba/tests/krb5/kdc_base_test.py +++ b/python/samba/tests/krb5/kdc_base_test.py @@ -618,7 +618,9 @@ class KDCBaseTest(RawKerberosTest): for enctype, key in keys.items(): creds.set_forced_key(enctype, key) - def creds_set_enctypes(self, creds): + def creds_set_enctypes(self, creds, + extra_bits=None, + remove_bits=None): samdb = self.get_samdb() res = samdb.search(creds.get_dn(), @@ -627,7 +629,20 @@ class KDCBaseTest(RawKerberosTest): supported_enctypes = res[0].get('msDS-SupportedEncryptionTypes', idx=0) if supported_enctypes is None: - supported_enctypes = 0 + supported_enctypes = self.default_etypes + if supported_enctypes is None: + lp = self.get_lp() + supported_enctypes = lp.get('kdc default domain supported enctypes') + + supported_enctypes = int(supported_enctypes) + + if extra_bits is not None: + # We need to add in implicit or implied encryption types. + supported_enctypes |= extra_bits + if remove_bits is not None: + # We also need to remove certain bits, such as the non-encryption + # type bit aes256-sk. + supported_enctypes &= ~remove_bits creds.set_as_supported_enctypes(supported_enctypes) creds.set_tgs_supported_enctypes(supported_enctypes) @@ -958,8 +973,15 @@ class KDCBaseTest(RawKerberosTest): allow_missing_password=False, allow_missing_keys=True): def create_mach_account(): - return self.get_cached_creds(account_type=self.AccountType.COMPUTER, - opts={'fast_support': True}) + return self.get_cached_creds( + account_type=self.AccountType.COMPUTER, + opts={ + 'fast_support': True, + 'supported_enctypes': ( + security.KERB_ENCTYPE_RC4_HMAC_MD5 | + security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96_SK + ), + }) c = self._get_krb5_creds(prefix='MAC', allow_missing_password=allow_missing_password, @@ -975,7 +997,11 @@ class KDCBaseTest(RawKerberosTest): account_type=self.AccountType.COMPUTER, opts={ 'trusted_to_auth_for_delegation': True, - 'fast_support': True + 'fast_support': True, + 'supported_enctypes': ( + security.KERB_ENCTYPE_RC4_HMAC_MD5 | + security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96_SK + ), }) c = self._get_krb5_creds(prefix='SERVICE', @@ -1077,7 +1103,13 @@ class KDCBaseTest(RawKerberosTest): keys = self.get_keys(samdb, dn) self.creds_set_keys(creds, keys) - self.creds_set_enctypes(creds) + extra_bits = (security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 | + security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96) + remove_bits = (security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96_SK | + security.KERB_ENCTYPE_RC4_HMAC_MD5) + self.creds_set_enctypes(creds, + extra_bits=extra_bits, + remove_bits=remove_bits) return creds @@ -1170,7 +1202,12 @@ class KDCBaseTest(RawKerberosTest): keys = self.get_keys(samdb, dn) self.creds_set_keys(creds, keys) - self.creds_set_enctypes(creds) + extra_bits = (security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 | + security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96) + remove_bits = security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96_SK + self.creds_set_enctypes(creds, + extra_bits=extra_bits, + remove_bits=remove_bits) return creds @@ -1212,7 +1249,12 @@ class KDCBaseTest(RawKerberosTest): keys = self.get_keys(samdb, dn) self.creds_set_keys(creds, keys) - self.creds_set_enctypes(creds) + extra_bits = (security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 | + security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96) + remove_bits = security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96_SK + self.creds_set_enctypes(creds, + extra_bits=extra_bits, + remove_bits=remove_bits) return creds @@ -1255,6 +1297,8 @@ class KDCBaseTest(RawKerberosTest): if pa['padata-type'] == PADATA_ETYPE_INFO2: padata_value = pa['padata-value'] break + else: + self.fail('expected to find ETYPE-INFO2') etype_info2 = self.der_decode( padata_value, asn1Spec=krb5_asn1.ETYPE_INFO2()) diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py index f3779099596..1607818de1d 100755 --- a/python/samba/tests/krb5/kdc_tgs_tests.py +++ b/python/samba/tests/krb5/kdc_tgs_tests.py @@ -25,7 +25,7 @@ os.environ["PYTHONUNBUFFERED"] = "1" import ldb -from samba import dsdb +from samba import dsdb, ntstatus from samba.dcerpc import krb5pac, security @@ -38,7 +38,9 @@ from samba.tests.krb5.rfc4120_constants import ( ARCFOUR_HMAC_MD5, KRB_ERROR, KRB_TGS_REP, + KDC_ERR_BADKEYVER, KDC_ERR_BADMATCH, + KDC_ERR_ETYPE_NOSUPP, KDC_ERR_GENERIC, KDC_ERR_MODIFIED, KDC_ERR_NOT_US, @@ -1598,12 +1600,16 @@ class KdcTgsTests(KdcTgsBaseTests): def test_tgs_rc4(self): creds = self._get_creds() tgt = self._get_tgt(creds, etype=kcrypto.Enctype.RC4) - self._run_tgs(tgt, expected_error=KDC_ERR_GENERIC) + self._run_tgs(tgt, expected_error=(KDC_ERR_GENERIC, + KDC_ERR_BADKEYVER), + expect_edata=True, + expected_status=ntstatus.NT_STATUS_INSUFFICIENT_RESOURCES) def test_renew_rc4(self): creds = self._get_creds() tgt = self._get_tgt(creds, renewable=True, etype=kcrypto.Enctype.RC4) - self._renew_tgt(tgt, expected_error=KDC_ERR_GENERIC, + self._renew_tgt(tgt, expected_error=(KDC_ERR_GENERIC, + KDC_ERR_BADKEYVER), expect_pac_attrs=True, expect_pac_attrs_pac_request=True, expect_requester_sid=True) @@ -1611,7 +1617,8 @@ class KdcTgsTests(KdcTgsBaseTests): def test_validate_rc4(self): creds = self._get_creds() tgt = self._get_tgt(creds, invalid=True, etype=kcrypto.Enctype.RC4) - self._validate_tgt(tgt, expected_error=KDC_ERR_GENERIC, + self._validate_tgt(tgt, expected_error=(KDC_ERR_GENERIC, + KDC_ERR_BADKEYVER), expect_pac_attrs=True, expect_pac_attrs_pac_request=True, expect_requester_sid=True) @@ -1619,17 +1626,21 @@ class KdcTgsTests(KdcTgsBaseTests): def test_s4u2self_rc4(self): creds = self._get_creds() tgt = self._get_tgt(creds, etype=kcrypto.Enctype.RC4) - self._s4u2self(tgt, creds, expected_error=KDC_ERR_GENERIC) + self._s4u2self(tgt, creds, expected_error=(KDC_ERR_GENERIC, + KDC_ERR_BADKEYVER), + expect_edata=True, + expected_status=ntstatus.NT_STATUS_INSUFFICIENT_RESOURCES) def test_user2user_rc4(self): creds = self._get_creds() tgt = self._get_tgt(creds, etype=kcrypto.Enctype.RC4) - self._user2user(tgt, creds, expected_error=KDC_ERR_GENERIC) + self._user2user(tgt, creds, expected_error=KDC_ERR_ETYPE_NOSUPP) def test_fast_rc4(self): creds = self._get_creds() tgt = self._get_tgt(creds, etype=kcrypto.Enctype.RC4) - self._fast(tgt, creds, expected_error=KDC_ERR_GENERIC) + self._fast(tgt, creds, expected_error=KDC_ERR_GENERIC, + expect_edata=self.expect_padata_outer) # Test user-to-user with incorrect service principal names. def test_user2user_matching_sname_host(self): @@ -2664,7 +2675,7 @@ class KdcTgsTests(KdcTgsBaseTests): can_modify_requester_sid=can_modify_requester_sid, remove_pac_attrs=remove_pac_attrs, remove_requester_sid=remove_requester_sid, - etype=None, + etype=etype, cksum_etype=cksum_etype) def _modify_tgt(self, @@ -2886,7 +2897,8 @@ class KdcTgsTests(KdcTgsBaseTests): def _run_tgs(self, tgt, expected_error, expect_pac=True, expect_pac_attrs=None, expect_pac_attrs_pac_request=None, - expect_requester_sid=None, expected_sid=None): + expect_requester_sid=None, expected_sid=None, + expect_edata=False, expected_status=None): target_creds = self.get_service_creds() return self._tgs_req( tgt, expected_error, target_creds, @@ -2894,7 +2906,9 @@ class KdcTgsTests(KdcTgsBaseTests): expect_pac_attrs=expect_pac_attrs, expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, expect_requester_sid=expect_requester_sid, - expected_sid=expected_sid) + expected_sid=expected_sid, + expect_edata=expect_edata, + expected_status=expected_status) # These tests fail against Windows, which does not implement ticket # renewal. @@ -2958,7 +2972,8 @@ class KdcTgsTests(KdcTgsBaseTests): expect_pac=expect_pac) def _user2user(self, tgt, tgt_creds, expected_error, sname=None, - srealm=None, user_tgt=None, expect_pac=True): + srealm=None, user_tgt=None, expect_pac=True, + expected_status=None): if user_tgt is None: user_creds = self._get_mach_creds() user_tgt = self.get_tgt(user_creds) @@ -2969,10 +2984,11 @@ class KdcTgsTests(KdcTgsBaseTests): additional_ticket=tgt, sname=sname, srealm=srealm, - expect_pac=expect_pac) + expect_pac=expect_pac, + expected_status=expected_status) def _fast(self, armor_tgt, armor_tgt_creds, expected_error, - expected_sname=None, expect_pac=True): + expected_sname=None, expect_pac=True, expect_edata=False): user_creds = self._get_mach_creds() user_tgt = self.get_tgt(user_creds) @@ -2981,7 +2997,8 @@ class KdcTgsTests(KdcTgsBaseTests): return self._tgs_req(user_tgt, expected_error, target_creds, armor_tgt=armor_tgt, expected_sname=expected_sname, - expect_pac=expect_pac) + expect_pac=expect_pac, + expect_edata=expect_edata) if __name__ == "__main__": diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index b6752fb2080..f13ada7bbf8 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -369,6 +369,10 @@ class KerberosCredentials(Credentials): security.KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED | security.KERB_ENCTYPE_CLAIMS_SUPPORTED) + non_etype_bits = fast_supported_bits | ( + security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED) | ( + security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96_SK) + def __init__(self): super(KerberosCredentials, self).__init__() all_enc_types = 0 @@ -430,7 +434,7 @@ class KerberosCredentials(Credentials): bits &= ~bit etypes += (etype,) - bits &= ~cls.fast_supported_bits + bits &= ~cls.non_etype_bits if bits != 0: raise ValueError(f'Unsupported etype bits: {bits}') @@ -557,6 +561,8 @@ class RawKerberosTest(TestCaseInTempDir): {"value": kcrypto.Enctype.RC4, "name": "rc4", }, ) + expect_padata_outer = object() + setup_etype_test_permutations_done = False @classmethod @@ -686,6 +692,18 @@ class RawKerberosTest(TestCaseInTempDir): kadmin_is_tgs = '0' cls.kadmin_is_tgs = bool(int(kadmin_is_tgs)) + default_etypes = samba.tests.env_get_var_value('DEFAULT_ETYPES', + allow_missing=True) + if default_etypes is not None: + default_etypes = int(default_etypes) + cls.default_etypes = default_etypes + + forced_rc4 = samba.tests.env_get_var_value('FORCED_RC4', + allow_missing=True) + if forced_rc4 is None: + forced_rc4 = '0' + cls.forced_rc4 = bool(int(forced_rc4)) + def setUp(self): super().setUp() self.do_asn1_print = False @@ -1299,7 +1317,8 @@ class RawKerberosTest(TestCaseInTempDir): if etype is None: etypes = creds.get_tgs_krb5_etypes() - if etypes: + if etypes and etypes[0] not in (kcrypto.Enctype.DES_CRC, + kcrypto.Enctype.DES_MD5): etype = etypes[0] else: etype = kcrypto.Enctype.RC4 @@ -3004,10 +3023,6 @@ class RawKerberosTest(TestCaseInTempDir): if PADATA_SUPPORTED_ETYPES in 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( '<L', @@ -3347,6 +3362,8 @@ class RawKerberosTest(TestCaseInTempDir): and (not sent_fast or fast_armor_type is None or fast_armor_type == FX_FAST_ARMOR_AP_REQUEST) and not inner) + if inner and expect_edata is self.expect_padata_outer: + expect_edata = False if not expect_edata: self.assertIsNone(expected_status) if self.strict_checking: diff --git a/python/samba/tests/krb5/rfc4120_constants.py b/python/samba/tests/krb5/rfc4120_constants.py index 16527f13593..7ad336208e2 100644 --- a/python/samba/tests/krb5/rfc4120_constants.py +++ b/python/samba/tests/krb5/rfc4120_constants.py @@ -99,6 +99,7 @@ KDC_ERR_NOT_US = 35 KDC_ERR_BADMATCH = 36 KDC_ERR_SKEW = 37 KDC_ERR_MODIFIED = 41 +KDC_ERR_BADKEYVER = 44 KDC_ERR_INAPP_CKSUM = 50 KDC_ERR_GENERIC = 60 KDC_ERR_WRONG_REALM = 68 diff --git a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py index dcdd67e2b64..84b8160bb93 100755 --- a/python/samba/tests/krb5/s4u_tests.py +++ b/python/samba/tests/krb5/s4u_tests.py @@ -1095,7 +1095,8 @@ class S4UKerberosTests(KDCBaseTest): # Attempt to modify the ticket without updating the PAC checksums. self._run_delegation_test( { - 'expected_error_mode': KDC_ERR_MODIFIED, + 'expected_error_mode': (KDC_ERR_MODIFIED, + KDC_ERR_BAD_INTEGRITY), 'expected_status': ntstatus.NT_STATUS_NOT_SUPPORTED, 'allow_rbcd': True, 'pac_options': '0001', # supports RBCD |