summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Sutton <josephsutton@catalyst.net.nz>2022-10-25 19:32:27 +1300
committerStefan Metzmacher <metze@samba.org>2022-12-14 00:48:48 +0100
commit07edcef7463103ebb9d3eb6e25c945c1abf1e5d2 (patch)
treea4ed2f1b8e70627d09b027548b7ec740a4cda852
parent92763515d9f0bb8ed56c721d752db1fb7a268407 (diff)
downloadsamba-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.idl3
-rwxr-xr-xpython/samba/tests/krb5/etype_tests.py256
-rwxr-xr-xpython/samba/tests/krb5/kdc_tgs_tests.py106
-rw-r--r--python/samba/tests/krb5/raw_testcase.py2
-rw-r--r--python/samba/tests/usage.py1
-rw-r--r--selftest/knownfail_heimdal_kdc5
-rw-r--r--selftest/knownfail_mit_kdc7
-rwxr-xr-xsource4/selftest/tests.py4
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',