1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-27 14:04:05 +03:00

CVE-2022-37966 python:tests/krb5: test much more etype combinations

This tests work out the difference between
- msDS-SupportedEncryptionTypes value or it's default
- software defined extra flags for DC accounts
- accounts with only an nt hash being stored
- the resulting value in the KRB5_PADATA_SUPPORTED_ETYPES announcement

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13135
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15237

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
(cherry picked from commit 1dfa91682efd3b12d7d6af75287efb12ebd9e526)
This commit is contained in:
Stefan Metzmacher 2022-11-29 17:11:01 +01:00
parent 8e6d2953ba
commit c8afae7869
3 changed files with 4051 additions and 520 deletions

View File

@ -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)
for _requested_etypes in requested_etype_cases:
_s = str(_requested_etypes)
_t = _s.maketrans(",", "_", "( )")
requested_etypes = _s.translate(_t)
def _test_etype_as_with_args(self, supported_bits, requested_etypes):
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)
# Perform the TGS-REQ.
ticket = self._as_req(creds, expected_error=expected_error,
target_creds=target_creds,
etype=requested_etypes)
if expected_error:
# There's no more to check. Return.
return
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 AS-REQ.
ticket = self._as_req(creds, expected_error=expected_error,
target_creds=target_creds,
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 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)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff