diff options
author | Joseph Sutton <josephsutton@catalyst.net.nz> | 2022-11-03 14:54:23 +1300 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2022-11-08 02:39:37 +0000 |
commit | 8556576d8df47710757ff4e32b04668fa5045daf (patch) | |
tree | c468f15d7962817db2446fb1875ca1b2beae1ff9 /python | |
parent | 5a613db6f511cfe3739cfe04cefa84e4f6681c99 (diff) | |
download | samba-8556576d8df47710757ff4e32b04668fa5045daf.tar.gz |
tests/krb5: Overhaul PAC logon info group checking
We can now verify attributes of SIDs and the PAC locations in which SIDs
are placed. We also gain the ability to assert that no SIDs are present
in the PAC other than the ones we expect.
We lighten somewhat the requirement that no duplicates are present among
the SIDs, as such a situation may arise even with Windows, especially if
group types are changed. For example, if a Universal group containing a
user is changed to a Domain-Local group in between an AS-REQ and a
TGS-REQ, the group's SID will be added to the PAC once for each request.
We only verify that there are no exact duplicates (SID, attributes, and
PAC location all being identical).
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Diffstat (limited to 'python')
-rw-r--r-- | python/samba/tests/krb5/raw_testcase.py | 138 | ||||
-rwxr-xr-x | python/samba/tests/krb5/s4u_tests.py | 40 |
2 files changed, 142 insertions, 36 deletions
diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py index 172ad3bf146..1fe29581204 100644 --- a/python/samba/tests/krb5/raw_testcase.py +++ b/python/samba/tests/krb5/raw_testcase.py @@ -38,7 +38,7 @@ from pyasn1.codec.ber.encoder import BitStringEncoder from pyasn1.error import PyAsn1Error from samba.credentials import Credentials -from samba.dcerpc import claims, krb5pac, security +from samba.dcerpc import claims, krb5pac, netlogon, security from samba.gensec import FEATURE_SEAL from samba.ndr import ndr_pack, ndr_unpack @@ -549,6 +549,12 @@ class RawKerberosTest(TestCaseInTempDir): SET = object() CHANGE = object() + # The location of a SID within the PAC + class SidType(Enum): + BASE_SID = object() # in info3.base.groups + EXTRA_SID = object() # in info3.sids + RESOURCE_SID = object() # in resource_groups + pac_checksum_types = {krb5pac.PAC_TYPE_SRV_CHECKSUM, krb5pac.PAC_TYPE_KDC_CHECKSUM, krb5pac.PAC_TYPE_TICKET_CHECKSUM} @@ -3124,6 +3130,106 @@ class RawKerberosTest(TestCaseInTempDir): kdc_exchange_dict['rep_ticket_creds'] = ticket_creds + # Check the SIDs in a LOGON_INFO PAC buffer. + def check_logon_info_sids(self, logon_info_buffer, kdc_exchange_dict): + info3 = logon_info_buffer.info.info.info3 + logon_info = info3.base + resource_groups = logon_info_buffer.info.info.resource_groups + + expected_groups = kdc_exchange_dict['expected_groups'] + unexpected_groups = kdc_exchange_dict['unexpected_groups'] + expected_sid = kdc_exchange_dict['expected_sid'] + + domain_sid = logon_info.domain_sid + + if expected_sid is not None: + got_sid = f'{domain_sid}-{logon_info.rid}' + self.assertEqual(expected_sid, got_sid) + + if expected_groups is None and unexpected_groups is None: + # Nothing more to do. + return + + # Check the SIDs in the PAC. + + # A representation of the PAC. + pac_sids = set() + + # Collect the Extra SIDs. + if info3.sids is not None: + self.assertTrue(logon_info.user_flags & ( + netlogon.NETLOGON_EXTRA_SIDS), + 'extra SIDs present, but EXTRA_SIDS flag not set') + self.assertTrue(info3.sids, 'got empty SIDs') + + for sid_attr in info3.sids: + got_sid = str(sid_attr.sid) + if unexpected_groups is not None: + self.assertNotIn(got_sid, unexpected_groups) + + pac_sid = (got_sid, + self.SidType.EXTRA_SID, + sid_attr.attributes) + self.assertNotIn(pac_sid, pac_sids, 'got duplicated SID') + pac_sids.add(pac_sid) + else: + self.assertFalse(logon_info.user_flags & ( + netlogon.NETLOGON_EXTRA_SIDS), + 'no extra SIDs present, but EXTRA_SIDS flag set') + + # Collect the Base RIDs. + if logon_info.groups.rids is not None: + self.assertTrue(logon_info.groups.rids, 'got empty RIDs') + + for group in logon_info.groups.rids: + got_sid = f'{domain_sid}-{group.rid}' + if unexpected_groups is not None: + self.assertNotIn(got_sid, unexpected_groups) + + pac_sid = (got_sid, self.SidType.BASE_SID, group.attributes) + self.assertNotIn(pac_sid, pac_sids, 'got duplicated SID') + pac_sids.add(pac_sid) + + # Collect the Resource SIDs. + if resource_groups.groups.rids is not None: + self.assertTrue(logon_info.user_flags & ( + netlogon.NETLOGON_RESOURCE_GROUPS), + 'resource groups present, but RESOURCE_GROUPS ' + 'flag not set') + self.assertTrue(resource_groups.groups.rids, 'got empty RIDs') + + resource_group_sid = resource_groups.domain_sid + for resource_group in resource_groups.groups.rids: + got_sid = f'{resource_group_sid}-{resource_group.rid}' + if unexpected_groups is not None: + self.assertNotIn(got_sid, unexpected_groups) + + pac_sid = (got_sid, + self.SidType.RESOURCE_SID, + resource_group.attributes) + self.assertNotIn(pac_sid, pac_sids, 'got duplicated SID') + pac_sids.add(pac_sid) + else: + self.assertFalse(logon_info.user_flags & ( + netlogon.NETLOGON_RESOURCE_GROUPS), + 'no resource groups present, but RESOURCE_GROUPS ' + 'flag set') + + # Compare the aggregated SIDs against the set of expected SIDs. + if expected_groups is not None: + if ... in expected_groups: + # The caller is only interested in asserting the + # presence of particular groups, and doesn't mind if + # other groups are present as well. + pac_sids.add(...) + self.assertLessEqual(expected_groups, pac_sids, + 'expected groups') + else: + # The caller wants to make sure the groups match + # exactly. + self.assertEqual(expected_groups, pac_sids, + 'expected != got') + def check_pac_buffers(self, pac_data, kdc_exchange_dict): pac = ndr_unpack(krb5pac.PAC_DATA, pac_data) @@ -3249,8 +3355,6 @@ class RawKerberosTest(TestCaseInTempDir): unchecked=unchecked) expected_account_name = kdc_exchange_dict['expected_account_name'] - expected_groups = kdc_exchange_dict['expected_groups'] - unexpected_groups = kdc_exchange_dict['unexpected_groups'] expected_sid = kdc_exchange_dict['expected_sid'] expect_upn_dns_info_ex = kdc_exchange_dict['expect_upn_dns_info_ex'] @@ -3290,33 +3394,7 @@ class RawKerberosTest(TestCaseInTempDir): self.assertEqual(expected_account_name, str(logon_info.account_name)) - if expected_sid is not None: - expected_rid = int(expected_sid.rsplit('-', 1)[1]) - self.assertEqual(expected_rid, logon_info.rid) - - if expected_groups is not None: - self.assertIsNotNone(info3.sids) - got_sids = {str(sid_attr.sid) for sid_attr in info3.sids} - self.assertEqual(info3.sidcount, - len(got_sids), - 'Found duplicate SIDs') - - match_count = 0 - for g in expected_groups: - for sid_attr in info3.sids: - if g == str(sid_attr.sid): - match_count += 1 - self.assertEqual(match_count, len(expected_groups)) - - if unexpected_groups is not None: - match_count = 0 - - for g in unexpected_groups: - self.assertIsNotNone(info3.sids) - for sid_attr in info3.sids: - if g == str(sid_attr.sid): - match_count += 1 - self.assertEqual(match_count, 0) + self.check_logon_info_sids(pac_buffer, kdc_exchange_dict) elif pac_buffer.type == krb5pac.PAC_TYPE_UPN_DNS_INFO: upn_dns_info = pac_buffer.info diff --git a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py index 482e95ae02e..49727685f72 100755 --- a/python/samba/tests/krb5/s4u_tests.py +++ b/python/samba/tests/krb5/s4u_tests.py @@ -30,6 +30,7 @@ from samba.tests import env_get_var_value from samba.tests.krb5.kcrypto import Cksumtype, Enctype from samba.tests.krb5.kdc_base_test import KDCBaseTest from samba.tests.krb5.raw_testcase import ( + RawKerberosTest, RodcPacEncryptionKey, ZeroedChecksumKey ) @@ -51,12 +52,18 @@ from samba.tests.krb5.rfc4120_constants import ( ) import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1 +SidType = RawKerberosTest.SidType + global_asn1_print = False global_hexdump = False class S4UKerberosTests(KDCBaseTest): + default_attrs = (security.SE_GROUP_MANDATORY | + security.SE_GROUP_ENABLED_BY_DEFAULT | + security.SE_GROUP_ENABLED) + def setUp(self): super(S4UKerberosTests, self).setUp() self.do_asn1_print = global_asn1_print @@ -541,8 +548,15 @@ class S4UKerberosTests(KDCBaseTest): 'client_opts': { 'not_delegated': False }, - 'expected_groups': [security.SID_SERVICE_ASSERTED_IDENTITY], - 'unexpected_groups': [security.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY] + 'expected_groups': { + (security.SID_SERVICE_ASSERTED_IDENTITY, + SidType.EXTRA_SID, + self.default_attrs), + ... + }, + 'unexpected_groups': { + security.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY, + }, }) def _run_delegation_test(self, kdc_dict): @@ -783,8 +797,15 @@ class S4UKerberosTests(KDCBaseTest): { 'expected_error_mode': 0, 'allow_delegation': True, - 'expected_groups': [security.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY], - 'unexpected_groups': [security.SID_SERVICE_ASSERTED_IDENTITY] + 'expected_groups': { + (security.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY, + SidType.EXTRA_SID, + self.default_attrs), + ... + }, + 'unexpected_groups': { + security.SID_SERVICE_ASSERTED_IDENTITY, + }, }) def test_constrained_delegation_service_asserted_identity(self): @@ -798,8 +819,15 @@ class S4UKerberosTests(KDCBaseTest): 'service1_opts': { 'trusted_to_auth_for_delegation': True, }, - 'expected_groups': [security.SID_SERVICE_ASSERTED_IDENTITY], - 'unexpected_groups': [security.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY] + 'expected_groups': { + (security.SID_SERVICE_ASSERTED_IDENTITY, + SidType.EXTRA_SID, + self.default_attrs), + ... + }, + 'unexpected_groups': { + security.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY, + }, }) def test_constrained_delegation_no_auth_data_required(self): |