diff options
author | Joseph Sutton <josephsutton@catalyst.net.nz> | 2022-10-25 19:32:27 +1300 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2022-12-14 00:48:48 +0100 |
commit | 07edcef7463103ebb9d3eb6e25c945c1abf1e5d2 (patch) | |
tree | a4ed2f1b8e70627d09b027548b7ec740a4cda852 | |
parent | 92763515d9f0bb8ed56c721d752db1fb7a268407 (diff) | |
download | samba-07edcef7463103ebb9d3eb6e25c945c1abf1e5d2.tar.gz |
CVE-2022-37966 tests/krb5: Add a test requesting tickets with various encryption types
The KDC should leave the choice of ticket encryption type up to the
target service, and admit no influence from the client.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15237
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
(similar to commit 177334c04230d0ad74bfc2b6825ffbebd5afb9af)
[jsutton@samba.org Fixed conflicts in usage.py, knownfails, tests.py]
[jsutton@samba.org Fixed knownfail conflicts]
[jsutton@samba.org Added new enctype bits; re-added expect_edata
parameter to _test_as_exchange(); fixed conflicts in usage.py,
knownfails, tests.py]
-rw-r--r-- | librpc/idl/netlogon.idl | 3 | ||||
-rwxr-xr-x | python/samba/tests/krb5/etype_tests.py | 256 | ||||
-rwxr-xr-x | python/samba/tests/krb5/kdc_tgs_tests.py | 106 | ||||
-rw-r--r-- | python/samba/tests/krb5/raw_testcase.py | 2 | ||||
-rw-r--r-- | python/samba/tests/usage.py | 1 | ||||
-rw-r--r-- | selftest/knownfail_heimdal_kdc | 5 | ||||
-rw-r--r-- | selftest/knownfail_mit_kdc | 7 | ||||
-rwxr-xr-x | source4/selftest/tests.py | 4 |
8 files changed, 384 insertions, 0 deletions
diff --git a/librpc/idl/netlogon.idl b/librpc/idl/netlogon.idl index ae5e33aea40..6b8b1ee78ca 100644 --- a/librpc/idl/netlogon.idl +++ b/librpc/idl/netlogon.idl @@ -16,6 +16,9 @@ cpp_quote("#define ENC_RSA_MD5 KERB_ENCTYPE_DES_CBC_MD5") cpp_quote("#define ENC_RC4_HMAC_MD5 KERB_ENCTYPE_RC4_HMAC_MD5") cpp_quote("#define ENC_HMAC_SHA1_96_AES128 KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96") cpp_quote("#define ENC_HMAC_SHA1_96_AES256 KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96") +cpp_quote("#define ENC_FAST_SUPPORTED KERB_ENCTYPE_FAST_SUPPORTED") +cpp_quote("#define ENC_COMPOUND_IDENTITY_SUPPORTED KERB_ENCTYPE_COMPOUND_IDENTITY_SUPPORTED") +cpp_quote("#define ENC_CLAIMS_SUPPORTED KERB_ENCTYPE_CLAIMS_SUPPORTED") [ uuid("12345678-1234-abcd-ef00-01234567cffb"), diff --git a/python/samba/tests/krb5/etype_tests.py b/python/samba/tests/krb5/etype_tests.py new file mode 100755 index 00000000000..37dab1eab8d --- /dev/null +++ b/python/samba/tests/krb5/etype_tests.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python3 +# Unix SMB/CIFS implementation. +# Copyright (C) Stefan Metzmacher 2020 +# Copyright (C) 2022 Catalyst.Net Ltd +# +# 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 <http://www.gnu.org/licenses/>. +# + +import sys +import os + +from samba.dcerpc import security + +from samba.tests.krb5.kdc_tgs_tests import KdcTgsBaseTests +from samba.tests.krb5.rfc4120_constants import ( + AES256_CTS_HMAC_SHA1_96, + ARCFOUR_HMAC_MD5, + KDC_ERR_ETYPE_NOSUPP, +) + +sys.path.insert(0, "bin/python") +os.environ["PYTHONUNBUFFERED"] = "1" + +global_asn1_print = False +global_hexdump = False + + +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._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() + + 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) + + # 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, + }) + + 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. The resulting ticket should be encrypted with + # AES, with an RC4 session key. + def test_as_aes_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_AES256_CTS_HMAC_SHA1_96, + }) + + ticket = self._as_req(creds, expected_error=0, + target_creds=target_creds, + etype=(ARCFOUR_HMAC_MD5,)) + + self.assertEqual(AES256_CTS_HMAC_SHA1_96, ticket.decryption_key.etype) + self.assertEqual(ARCFOUR_HMAC_MD5, 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, + }) + + 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, when the target + # 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, + }) + + 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) + + # 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() + + self._tgs_req(tgt, expected_error=KDC_ERR_ETYPE_NOSUPP, + target_creds=target_creds, + etypes=(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) + + target_creds = self.get_mach_creds() + + 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 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() + 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, + }) + + 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. The resulting ticket should be encrypted with + # AES, with an RC4 session key. + def test_tgs_aes_supported_rc4_requested(self): + creds = self.get_mach_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, + }) + + ticket = self._tgs_req(tgt, expected_error=0, + target_creds=target_creds, + etypes=(ARCFOUR_HMAC_MD5,)) + + 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() + 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, + }) + + self._tgs_req(tgt, expected_error=KDC_ERR_ETYPE_NOSUPP, + target_creds=target_creds, + etypes=(AES256_CTS_HMAC_SHA1_96,)) + + # Perform a TGS-REQ for a service ticket, specifying RC4, when the target + # 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() + 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, + }) + + 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) + + +if __name__ == "__main__": + 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 4e26a011669..7239e0be8db 100755 --- a/python/samba/tests/krb5/kdc_tgs_tests.py +++ b/python/samba/tests/krb5/kdc_tgs_tests.py @@ -43,6 +43,7 @@ from samba.tests.krb5.rfc4120_constants import ( KDC_ERR_MODIFIED, KDC_ERR_NOT_US, KDC_ERR_POLICY, + KDC_ERR_PREAUTH_REQUIRED, KDC_ERR_C_PRINCIPAL_UNKNOWN, KDC_ERR_S_PRINCIPAL_UNKNOWN, KDC_ERR_TGT_REVOKED, @@ -58,6 +59,111 @@ global_hexdump = False class KdcTgsBaseTests(KDCBaseTest): + def _as_req(self, + creds, + expected_error, + target_creds, + etype): + user_name = creds.get_username() + cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, + names=user_name.split('/')) + + target_name = target_creds.get_username() + sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, + names=['host', target_name[:-1]]) + + if expected_error: + expected_sname = sname + else: + expected_sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, + names=[target_name]) + + realm = creds.get_realm() + salt = creds.get_salt() + + till = self.get_KerberosTime(offset=36000) + + ticket_decryption_key = ( + self.TicketDecryptionKey_from_creds(target_creds)) + expected_etypes = target_creds.tgs_supported_enctypes + + kdc_options = ('forwardable,' + 'renewable,' + 'canonicalize,' + 'renewable-ok') + kdc_options = krb5_asn1.KDCOptions(kdc_options) + + if expected_error: + initial_error = (KDC_ERR_PREAUTH_REQUIRED, expected_error) + else: + initial_error = KDC_ERR_PREAUTH_REQUIRED + + rep, kdc_exchange_dict = self._test_as_exchange( + cname=cname, + realm=realm, + sname=sname, + till=till, + client_as_etypes=etype, + expected_error_mode=initial_error, + expected_crealm=realm, + expected_cname=cname, + expected_srealm=realm, + expected_sname=sname, + expected_salt=salt, + expected_supported_etypes=expected_etypes, + etypes=etype, + padata=None, + kdc_options=kdc_options, + preauth_key=None, + ticket_decryption_key=ticket_decryption_key) + self.assertIsNotNone(rep) + self.assertEqual(KRB_ERROR, rep['msg-type']) + error_code = rep['error-code'] + if expected_error: + self.assertIn(error_code, initial_error) + if error_code == expected_error: + return + else: + self.assertEqual(initial_error, error_code) + + 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_from_key(preauth_key) + + padata = [ts_enc_padata] + + expected_realm = 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=expected_error, + expected_crealm=expected_realm, + expected_cname=cname, + expected_srealm=expected_realm, + expected_sname=expected_sname, + expected_salt=salt, + expected_supported_etypes=expected_etypes, + etypes=etype, + padata=padata, + kdc_options=kdc_options, + preauth_key=preauth_key, + ticket_decryption_key=ticket_decryption_key, + expect_edata=False) + if expected_error: + self.check_error_rep(rep, expected_error) + return None + + self.check_as_reply(rep) + return kdc_exchange_dict['rep_ticket_creds'] + def _tgs_req(self, tgt, expected_error, target_creds, armor_tgt=None, kdc_options='0', diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index b463b168242..61f8adc927c 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -4171,6 +4171,7 @@ class RawKerberosTest(TestCaseInTempDir): expect_pac_attrs=None, expect_pac_attrs_pac_request=None, expect_requester_sid=None, + expect_edata=None, to_rodc=False): def _generate_padata_copy(_kdc_exchange_dict, @@ -4217,6 +4218,7 @@ class RawKerberosTest(TestCaseInTempDir): expect_pac_attrs=expect_pac_attrs, expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, expect_requester_sid=expect_requester_sid, + expect_edata=expect_edata, to_rodc=to_rodc) rep = self._generic_kdc_exchange(kdc_exchange_dict, diff --git a/python/samba/tests/usage.py b/python/samba/tests/usage.py index a1210ada579..d045e3cc3ab 100644 --- a/python/samba/tests/usage.py +++ b/python/samba/tests/usage.py @@ -110,6 +110,7 @@ EXCLUDE_USAGE = { 'python/samba/tests/krb5/test_min_domain_uid.py', 'python/samba/tests/krb5/test_idmap_nss.py', 'python/samba/tests/krb5/kpasswd_tests.py', + 'python/samba/tests/krb5/etype_tests.py', } EXCLUDE_HELP = { diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc index 692b9ecdd72..5dc3c60847e 100644 --- a/selftest/knownfail_heimdal_kdc +++ b/selftest/knownfail_heimdal_kdc @@ -109,3 +109,8 @@ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting +# +# Encryption type tests +# +^samba.tests.krb5.etype_tests.samba.tests.krb5.etype_tests.EtypeTests.test_as_aes_requested.ad_dc +^samba.tests.krb5.etype_tests.samba.tests.krb5.etype_tests.EtypeTests.test_as_rc4_supported_aes_requested.ad_dc diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc index 680c637fbc5..4fa5fb3ba42 100644 --- a/selftest/knownfail_mit_kdc +++ b/selftest/knownfail_mit_kdc @@ -549,3 +549,10 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc +# +# Encryption type tests +# +^samba.tests.krb5.etype_tests.samba.tests.krb5.etype_tests.EtypeTests.test_as_aes_supported_aes_requested.ad_dc +^samba.tests.krb5.etype_tests.samba.tests.krb5.etype_tests.EtypeTests.test_as_aes_supported_rc4_requested.ad_dc +^samba.tests.krb5.etype_tests.samba.tests.krb5.etype_tests.EtypeTests.test_as_rc4_requested.ad_dc +^samba.tests.krb5.etype_tests.samba.tests.krb5.etype_tests.EtypeTests.test_as_rc4_supported_rc4_requested.ad_dc diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index bc06cba310d..06d4b1771c8 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -1595,6 +1595,10 @@ planoldpythontestsuite( 'ad_dc', 'samba.tests.krb5.kpasswd_tests', environ=krb5_environ) +planoldpythontestsuite( + 'ad_dc', + 'samba.tests.krb5.etype_tests', + environ=krb5_environ) for env in [ 'vampire_dc', |