mirror of
https://github.com/samba-team/samba.git
synced 2025-01-14 19:24:43 +03:00
49605b5e89
Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2183 lines
94 KiB
Python
Executable File
2183 lines
94 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Unix SMB/CIFS implementation.
|
|
# Copyright (C) Stefan Metzmacher 2020
|
|
# Copyright (C) Catalyst.Net Ltd 2022
|
|
#
|
|
# 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
|
|
|
|
sys.path.insert(0, 'bin/python')
|
|
os.environ['PYTHONUNBUFFERED'] = '1'
|
|
|
|
import random
|
|
import re
|
|
|
|
from enum import Enum
|
|
from functools import partial
|
|
|
|
import ldb
|
|
|
|
from samba import common, werror
|
|
from samba.dcerpc import krb5pac, netlogon, samr, security
|
|
from samba.dsdb import (
|
|
GTYPE_SECURITY_DOMAIN_LOCAL_GROUP,
|
|
GTYPE_SECURITY_GLOBAL_GROUP,
|
|
GTYPE_SECURITY_UNIVERSAL_GROUP,
|
|
)
|
|
from samba.tests import DynamicTestCase, env_get_var_value
|
|
from samba.tests.krb5 import kcrypto
|
|
from samba.tests.krb5.kdc_base_test import KDCBaseTest
|
|
from samba.tests.krb5.raw_testcase import RawKerberosTest
|
|
from samba.tests.krb5.rfc4120_constants import (
|
|
KRB_TGS_REP,
|
|
NT_PRINCIPAL,
|
|
)
|
|
|
|
SidType = RawKerberosTest.SidType
|
|
|
|
global_asn1_print = False
|
|
global_hexdump = False
|
|
|
|
|
|
class GroupType(Enum):
|
|
GLOBAL = GTYPE_SECURITY_GLOBAL_GROUP
|
|
DOMAIN_LOCAL = GTYPE_SECURITY_DOMAIN_LOCAL_GROUP
|
|
UNIVERSAL = GTYPE_SECURITY_UNIVERSAL_GROUP
|
|
|
|
|
|
# This simple class encapsulates the DN and SID of a Principal.
|
|
class Principal:
|
|
def __init__(self, dn, sid):
|
|
if dn is not None and not isinstance(dn, ldb.Dn):
|
|
raise AssertionError(f'expected {dn} to be an ldb.Dn')
|
|
|
|
self.dn = dn
|
|
self.sid = sid
|
|
|
|
|
|
@DynamicTestCase
|
|
class GroupTests(KDCBaseTest):
|
|
# Placeholder objects that represent the user account undergoing testing.
|
|
user = object()
|
|
trust_user = object()
|
|
|
|
# Constants for group SID attributes.
|
|
default_attrs = security.SE_GROUP_DEFAULT_FLAGS
|
|
resource_attrs = default_attrs | security.SE_GROUP_RESOURCE
|
|
|
|
asserted_identity = security.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY
|
|
|
|
trust_domain = 'S-1-5-21-123-456-789'
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.do_asn1_print = global_asn1_print
|
|
self.do_hexdump = global_hexdump
|
|
|
|
@classmethod
|
|
def setUpDynamicTestCases(cls):
|
|
FILTER = env_get_var_value('FILTER', allow_missing=True)
|
|
SKIP_INVALID = env_get_var_value('SKIP_INVALID', allow_missing=True)
|
|
|
|
for case in cls.cases:
|
|
invalid = case.pop('configuration_invalid', False)
|
|
if SKIP_INVALID and invalid:
|
|
# Some group setups are invalid on Windows, so we allow them to
|
|
# be skipped.
|
|
continue
|
|
name = case.pop('test')
|
|
if FILTER and not re.search(FILTER, name):
|
|
continue
|
|
name = re.sub(r'\W+', '_', name)
|
|
|
|
cls.generate_dynamic_test('test_group', name,
|
|
dict(case))
|
|
|
|
def test_set_universal_primary_group(self):
|
|
samdb = self.get_samdb()
|
|
|
|
# Create a universal group.
|
|
universal_dn = self.create_group(samdb,
|
|
self.get_new_username(),
|
|
gtype=GroupType.UNIVERSAL.value)
|
|
|
|
# Get the SID of the universal group.
|
|
universal_sid = self.get_objectSid(samdb, universal_dn)
|
|
|
|
# Create a user account belonging to the group.
|
|
creds = self.get_cached_creds(
|
|
account_type=self.AccountType.USER,
|
|
opts={
|
|
'member_of': (
|
|
universal_dn,
|
|
),
|
|
'kerberos_enabled': False,
|
|
},
|
|
use_cache=False)
|
|
|
|
# Set the user's primary group.
|
|
self.set_primary_group(samdb, creds.get_dn(), universal_sid)
|
|
|
|
def test_set_domain_local_primary_group(self):
|
|
samdb = self.get_samdb()
|
|
|
|
# Create a domain-local group.
|
|
domain_local_dn = self.create_group(samdb,
|
|
self.get_new_username(),
|
|
gtype=GroupType.DOMAIN_LOCAL.value)
|
|
|
|
# Get the SID of the domain-local group.
|
|
domain_local_sid = self.get_objectSid(samdb, domain_local_dn)
|
|
|
|
# Create a user account belonging to the group.
|
|
creds = self.get_cached_creds(
|
|
account_type=self.AccountType.USER,
|
|
opts={
|
|
'member_of': (
|
|
domain_local_dn,
|
|
),
|
|
'kerberos_enabled': False,
|
|
},
|
|
use_cache=False)
|
|
|
|
# Setting the user's primary group fails.
|
|
self.set_primary_group(
|
|
samdb, creds.get_dn(), domain_local_sid,
|
|
expected_error=ldb.ERR_UNWILLING_TO_PERFORM,
|
|
expected_werror=werror.WERR_MEMBER_NOT_IN_GROUP)
|
|
|
|
def test_change_universal_primary_group_to_global(self):
|
|
samdb = self.get_samdb()
|
|
|
|
# Create a universal group.
|
|
universal_dn = self.create_group(samdb,
|
|
self.get_new_username(),
|
|
gtype=GroupType.UNIVERSAL.value)
|
|
|
|
# Get the SID of the universal group.
|
|
universal_sid = self.get_objectSid(samdb, universal_dn)
|
|
|
|
# Create a user account belonging to the group.
|
|
creds = self.get_cached_creds(
|
|
account_type=self.AccountType.USER,
|
|
opts={
|
|
'member_of': (
|
|
universal_dn,
|
|
),
|
|
'kerberos_enabled': False,
|
|
},
|
|
use_cache=False)
|
|
|
|
# Set the user's primary group.
|
|
self.set_primary_group(samdb, creds.get_dn(), universal_sid)
|
|
|
|
# Change the group to a global group.
|
|
self.set_group_type(samdb,
|
|
ldb.Dn(samdb, universal_dn),
|
|
GroupType.GLOBAL)
|
|
|
|
def test_change_universal_primary_group_to_domain_local(self):
|
|
samdb = self.get_samdb()
|
|
|
|
# Create a universal group.
|
|
universal_dn = self.create_group(samdb,
|
|
self.get_new_username(),
|
|
gtype=GroupType.UNIVERSAL.value)
|
|
|
|
# Get the SID of the universal group.
|
|
universal_sid = self.get_objectSid(samdb, universal_dn)
|
|
|
|
# Create a user account belonging to the group.
|
|
creds = self.get_cached_creds(
|
|
account_type=self.AccountType.USER,
|
|
opts={
|
|
'member_of': (
|
|
universal_dn,
|
|
),
|
|
'kerberos_enabled': False,
|
|
},
|
|
use_cache=False)
|
|
|
|
# Set the user's primary group.
|
|
self.set_primary_group(samdb, creds.get_dn(), universal_sid)
|
|
|
|
# Change the group to a domain-local group. This works, even though the
|
|
# group is still the user's primary group.
|
|
self.set_group_type(samdb,
|
|
ldb.Dn(samdb, universal_dn),
|
|
GroupType.DOMAIN_LOCAL)
|
|
|
|
# Check the groups in a SamInfo structure returned by SamLogon.
|
|
def test_samlogon_SamInfo(self):
|
|
samdb = self.get_samdb()
|
|
|
|
# Create a universal and a domain-local group.
|
|
universal_dn = self.create_group(samdb,
|
|
self.get_new_username(),
|
|
gtype=GroupType.UNIVERSAL.value)
|
|
domain_local_dn = self.create_group(samdb,
|
|
self.get_new_username(),
|
|
gtype=GroupType.DOMAIN_LOCAL.value)
|
|
|
|
# Create a user account belonging to both groups.
|
|
creds = self.get_cached_creds(
|
|
account_type=self.AccountType.USER,
|
|
opts={
|
|
'member_of': (
|
|
universal_dn,
|
|
domain_local_dn,
|
|
),
|
|
'kerberos_enabled': False,
|
|
})
|
|
|
|
# Get the SID and RID of the user account.
|
|
user_sid = self.get_objectSid(samdb, creds.get_dn())
|
|
user_rid = int(user_sid.rsplit('-', 1)[1])
|
|
|
|
# Get the SID and RID of the universal group.
|
|
universal_sid = self.get_objectSid(samdb, universal_dn)
|
|
universal_rid = int(universal_sid.rsplit('-', 1)[1])
|
|
|
|
# We don't expect the EXTRA_SIDS flag to be set.
|
|
unexpected_flags = netlogon.NETLOGON_EXTRA_SIDS
|
|
|
|
# Do a SamLogon call and check we get back the right structure.
|
|
interactive = netlogon.NetlogonInteractiveInformation
|
|
level = netlogon.NetlogonValidationSamInfo
|
|
validation = self._test_samlogon(creds=creds,
|
|
logon_type=interactive,
|
|
protected=False,
|
|
validation_level=level)
|
|
self.assertIsInstance(validation, netlogon.netr_SamInfo2)
|
|
|
|
base = validation.base
|
|
|
|
# Check some properties of the base structure.
|
|
self.assertEqual(user_rid, base.rid)
|
|
self.assertEqual(security.DOMAIN_RID_USERS, base.primary_gid)
|
|
self.assertEqual(samdb.get_domain_sid(), str(base.domain_sid))
|
|
self.assertFalse(unexpected_flags & base.user_flags,
|
|
f'0x{unexpected_flags:x} unexpectedly set in '
|
|
f'user_flags (0x{base.user_flags:x})')
|
|
|
|
# Check we have two groups in the base.
|
|
self.assertEqual(2, base.groups.count)
|
|
|
|
rids = base.groups.rids
|
|
|
|
# The first group should be Domain Users.
|
|
self.assertEqual(security.DOMAIN_RID_USERS, rids[0].rid)
|
|
self.assertEqual(self.default_attrs, rids[0].attributes)
|
|
|
|
# The second should be our universal group.
|
|
self.assertEqual(universal_rid, rids[1].rid)
|
|
self.assertEqual(self.default_attrs, rids[1].attributes)
|
|
|
|
# The domain-local group is nowhere to be found.
|
|
|
|
# Check the groups in a SamInfo2 structure returned by SamLogon.
|
|
def test_samlogon_SamInfo2(self):
|
|
samdb = self.get_samdb()
|
|
|
|
# Create a universal and a domain-local group.
|
|
universal_dn = self.create_group(samdb,
|
|
self.get_new_username(),
|
|
gtype=GroupType.UNIVERSAL.value)
|
|
domain_local_dn = self.create_group(samdb,
|
|
self.get_new_username(),
|
|
gtype=GroupType.DOMAIN_LOCAL.value)
|
|
|
|
# Create a user account belonging to both groups.
|
|
creds = self.get_cached_creds(
|
|
account_type=self.AccountType.USER,
|
|
opts={
|
|
'member_of': (
|
|
universal_dn,
|
|
domain_local_dn,
|
|
),
|
|
'kerberos_enabled': False,
|
|
})
|
|
|
|
# Get the SID and RID of the user account.
|
|
user_sid = self.get_objectSid(samdb, creds.get_dn())
|
|
user_rid = int(user_sid.rsplit('-', 1)[1])
|
|
|
|
# Get the SID and RID of the universal group.
|
|
universal_sid = self.get_objectSid(samdb, universal_dn)
|
|
universal_rid = int(universal_sid.rsplit('-', 1)[1])
|
|
|
|
# Get the SID of the domain-local group.
|
|
domain_local_sid = self.get_objectSid(samdb, domain_local_dn)
|
|
|
|
# We expect the EXTRA_SIDS flag to be set.
|
|
expected_flags = netlogon.NETLOGON_EXTRA_SIDS
|
|
|
|
# Do a SamLogon call and check we get back the right structure.
|
|
interactive = netlogon.NetlogonInteractiveInformation
|
|
level = netlogon.NetlogonValidationSamInfo2
|
|
validation = self._test_samlogon(creds=creds,
|
|
logon_type=interactive,
|
|
protected=False,
|
|
validation_level=level)
|
|
self.assertIsInstance(validation, netlogon.netr_SamInfo3)
|
|
|
|
base = validation.base
|
|
|
|
# Check some properties of the base structure.
|
|
self.assertEqual(user_rid, base.rid)
|
|
self.assertEqual(security.DOMAIN_RID_USERS, base.primary_gid)
|
|
self.assertEqual(samdb.get_domain_sid(), str(base.domain_sid))
|
|
self.assertTrue(expected_flags & base.user_flags,
|
|
f'0x{expected_flags:x} unexpectedly reset in '
|
|
f'user_flags (0x{base.user_flags:x})')
|
|
|
|
# Check we have two groups in the base.
|
|
self.assertEqual(2, base.groups.count)
|
|
|
|
rids = base.groups.rids
|
|
|
|
# The first group should be Domain Users.
|
|
self.assertEqual(security.DOMAIN_RID_USERS, rids[0].rid)
|
|
self.assertEqual(self.default_attrs, rids[0].attributes)
|
|
|
|
# The second should be our universal group.
|
|
self.assertEqual(universal_rid, rids[1].rid)
|
|
self.assertEqual(self.default_attrs, rids[1].attributes)
|
|
|
|
# Check that we have one group in the SIDs array.
|
|
self.assertEqual(1, validation.sidcount)
|
|
|
|
sids = validation.sids
|
|
|
|
# That group should be our domain-local group.
|
|
self.assertEqual(domain_local_sid, str(sids[0].sid))
|
|
self.assertEqual(self.resource_attrs, sids[0].attributes)
|
|
|
|
# Check the groups in a SamInfo4 structure returned by SamLogon.
|
|
def test_samlogon_SamInfo4(self):
|
|
samdb = self.get_samdb()
|
|
|
|
# Create a universal and a domain-local group.
|
|
universal_dn = self.create_group(samdb,
|
|
self.get_new_username(),
|
|
gtype=GroupType.UNIVERSAL.value)
|
|
domain_local_dn = self.create_group(samdb,
|
|
self.get_new_username(),
|
|
gtype=GroupType.DOMAIN_LOCAL.value)
|
|
|
|
# Create a user account belonging to both groups.
|
|
creds = self.get_cached_creds(
|
|
account_type=self.AccountType.USER,
|
|
opts={
|
|
'member_of': (
|
|
universal_dn,
|
|
domain_local_dn,
|
|
),
|
|
'kerberos_enabled': False,
|
|
})
|
|
|
|
# Get the SID and RID of the user account.
|
|
user_sid = self.get_objectSid(samdb, creds.get_dn())
|
|
user_rid = int(user_sid.rsplit('-', 1)[1])
|
|
|
|
# Get the SID and RID of the universal group.
|
|
universal_sid = self.get_objectSid(samdb, universal_dn)
|
|
universal_rid = int(universal_sid.rsplit('-', 1)[1])
|
|
|
|
# Get the SID of the domain-local group.
|
|
domain_local_sid = self.get_objectSid(samdb, domain_local_dn)
|
|
|
|
# We expect the EXTRA_SIDS flag to be set.
|
|
expected_flags = netlogon.NETLOGON_EXTRA_SIDS
|
|
|
|
# Do a SamLogon call and check we get back the right structure.
|
|
interactive = netlogon.NetlogonInteractiveInformation
|
|
level = netlogon.NetlogonValidationSamInfo4
|
|
validation = self._test_samlogon(creds=creds,
|
|
logon_type=interactive,
|
|
protected=False,
|
|
validation_level=level)
|
|
self.assertIsInstance(validation, netlogon.netr_SamInfo6)
|
|
|
|
base = validation.base
|
|
|
|
# Check some properties of the base structure.
|
|
self.assertEqual(user_rid, base.rid)
|
|
self.assertEqual(security.DOMAIN_RID_USERS, base.primary_gid)
|
|
self.assertEqual(samdb.get_domain_sid(), str(base.domain_sid))
|
|
self.assertTrue(expected_flags & base.user_flags,
|
|
f'0x{expected_flags:x} unexpectedly reset in '
|
|
f'user_flags (0x{base.user_flags:x})')
|
|
|
|
# Check we have two groups in the base.
|
|
self.assertEqual(2, base.groups.count)
|
|
|
|
rids = base.groups.rids
|
|
|
|
# The first group should be Domain Users.
|
|
self.assertEqual(security.DOMAIN_RID_USERS, rids[0].rid)
|
|
self.assertEqual(self.default_attrs, rids[0].attributes)
|
|
|
|
# The second should be our universal group.
|
|
self.assertEqual(universal_rid, rids[1].rid)
|
|
self.assertEqual(self.default_attrs, rids[1].attributes)
|
|
|
|
# Check that we have one group in the SIDs array.
|
|
self.assertEqual(1, validation.sidcount)
|
|
|
|
sids = validation.sids
|
|
|
|
# That group should be our domain-local group.
|
|
self.assertEqual(domain_local_sid, str(sids[0].sid))
|
|
self.assertEqual(self.resource_attrs, sids[0].attributes)
|
|
|
|
# Get a ticket with the SIDs in the PAC replaced with ones we specify. This
|
|
# is useful for creating arbitrary tickets that can be used to perform a
|
|
# TGS-REQ.
|
|
def ticket_with_sids(self,
|
|
ticket,
|
|
new_sids,
|
|
domain_sid,
|
|
user_rid,
|
|
set_user_flags=0,
|
|
reset_user_flags=0):
|
|
krbtgt_creds = self.get_krbtgt_creds()
|
|
krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds)
|
|
|
|
checksum_keys = {
|
|
krb5pac.PAC_TYPE_KDC_CHECKSUM: krbtgt_key
|
|
}
|
|
|
|
modify_pac_fn = partial(self.set_pac_sids,
|
|
new_sids=new_sids,
|
|
domain_sid=domain_sid,
|
|
user_rid=user_rid,
|
|
set_user_flags=set_user_flags,
|
|
reset_user_flags=reset_user_flags)
|
|
|
|
return self.modified_ticket(ticket,
|
|
modify_pac_fn=modify_pac_fn,
|
|
checksum_keys=checksum_keys)
|
|
|
|
# Replace the SIDs in a PAC with 'new_sids'.
|
|
def set_pac_sids(self,
|
|
pac,
|
|
new_sids,
|
|
domain_sid,
|
|
user_rid,
|
|
set_user_flags=0,
|
|
reset_user_flags=0):
|
|
base_sids = []
|
|
extra_sids = []
|
|
resource_sids = []
|
|
|
|
resource_domain = None
|
|
|
|
primary_gid = None
|
|
|
|
# Filter our SIDs into three arrays depending on their ultimate
|
|
# location in the PAC.
|
|
for sid, sid_type, attrs in new_sids:
|
|
if sid_type is self.SidType.BASE_SID:
|
|
domain, rid = sid.rsplit('-', 1)
|
|
self.assertEqual(domain_sid, domain,
|
|
f'base SID {sid} must be in our domain')
|
|
|
|
base_sid = samr.RidWithAttribute()
|
|
base_sid.rid = int(rid)
|
|
base_sid.attributes = attrs
|
|
|
|
base_sids.append(base_sid)
|
|
elif sid_type is self.SidType.EXTRA_SID:
|
|
extra_sid = netlogon.netr_SidAttr()
|
|
extra_sid.sid = security.dom_sid(sid)
|
|
extra_sid.attributes = attrs
|
|
|
|
extra_sids.append(extra_sid)
|
|
elif sid_type is self.SidType.RESOURCE_SID:
|
|
domain, rid = sid.rsplit('-', 1)
|
|
if resource_domain is None:
|
|
resource_domain = domain
|
|
else:
|
|
self.assertEqual(resource_domain, domain,
|
|
'resource SIDs must share the same '
|
|
'domain')
|
|
|
|
resource_sid = samr.RidWithAttribute()
|
|
resource_sid.rid = int(rid)
|
|
resource_sid.attributes = attrs
|
|
|
|
resource_sids.append(resource_sid)
|
|
elif sid_type is self.SidType.PRIMARY_GID:
|
|
self.assertIsNone(primary_gid,
|
|
f'must not specify a second primary GID '
|
|
f'{sid}')
|
|
self.assertIsNone(attrs, 'cannot specify primary GID attrs')
|
|
|
|
domain, primary_gid = sid.rsplit('-', 1)
|
|
self.assertEqual(domain_sid, domain,
|
|
f'primary GID {sid} must be in our domain')
|
|
else:
|
|
self.fail(f'invalid SID type {sid_type}')
|
|
|
|
found_logon_info = True
|
|
|
|
user_sid = security.dom_sid(f'{domain_sid}-{user_rid}')
|
|
|
|
pac_buffers = pac.buffers
|
|
for pac_buffer in pac_buffers:
|
|
# Find the LOGON_INFO PAC buffer.
|
|
if pac_buffer.type == krb5pac.PAC_TYPE_LOGON_INFO:
|
|
logon_info = pac_buffer.info.info
|
|
|
|
# Add Extra SIDs and set the EXTRA_SIDS flag as needed.
|
|
logon_info.info3.sidcount = len(extra_sids)
|
|
if extra_sids:
|
|
logon_info.info3.sids = extra_sids
|
|
logon_info.info3.base.user_flags |= (
|
|
netlogon.NETLOGON_EXTRA_SIDS)
|
|
else:
|
|
logon_info.info3.sids = None
|
|
logon_info.info3.base.user_flags &= ~(
|
|
netlogon.NETLOGON_EXTRA_SIDS)
|
|
|
|
# Add Base SIDs.
|
|
logon_info.info3.base.groups.count = len(base_sids)
|
|
if base_sids:
|
|
logon_info.info3.base.groups.rids = base_sids
|
|
else:
|
|
logon_info.info3.base.groups.rids = None
|
|
|
|
logon_info.info3.base.domain_sid = security.dom_sid(domain_sid)
|
|
logon_info.info3.base.rid = int(user_rid)
|
|
|
|
if primary_gid is not None:
|
|
logon_info.info3.base.primary_gid = int(primary_gid)
|
|
|
|
# Add Resource SIDs and set the RESOURCE_GROUPS flag as needed.
|
|
logon_info.resource_groups.groups.count = len(resource_sids)
|
|
if resource_sids:
|
|
resource_domain = security.dom_sid(resource_domain)
|
|
logon_info.resource_groups.domain_sid = resource_domain
|
|
logon_info.resource_groups.groups.rids = resource_sids
|
|
logon_info.info3.base.user_flags |= (
|
|
netlogon.NETLOGON_RESOURCE_GROUPS)
|
|
else:
|
|
logon_info.resource_groups.domain_sid = None
|
|
logon_info.resource_groups.groups.rids = None
|
|
logon_info.info3.base.user_flags &= ~(
|
|
netlogon.NETLOGON_RESOURCE_GROUPS)
|
|
|
|
logon_info.info3.base.user_flags |= set_user_flags
|
|
logon_info.info3.base.user_flags &= ~reset_user_flags
|
|
|
|
found_logon_info = True
|
|
|
|
# Also replace the user's SID in the UPN DNS buffer.
|
|
elif pac_buffer.type == krb5pac.PAC_TYPE_UPN_DNS_INFO:
|
|
upn_dns_info_ex = pac_buffer.info.ex
|
|
|
|
upn_dns_info_ex.objectsid = user_sid
|
|
|
|
# But don't replace the user's SID in the Requester SID buffer, or
|
|
# we'll get a SID mismatch.
|
|
|
|
self.assertTrue(found_logon_info, 'no LOGON_INFO PAC buffer')
|
|
|
|
pac.buffers = pac_buffers
|
|
|
|
return pac
|
|
|
|
# A list of test cases.
|
|
cases = [
|
|
# AS-REQ tests.
|
|
{
|
|
'test': 'universal; as-req to krbtgt',
|
|
'groups': {
|
|
# A Universal group containing the user.
|
|
'foo': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
# Make an AS-REQ to the krbtgt with the user's account.
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
# Ignoring the user ID, or base RID, expect the PAC to contain
|
|
# precisely the following SIDS in any order:
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'universal; as-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
# The same again, but this time perform the AS-REQ to a service.
|
|
'as:to_krbtgt': False,
|
|
'as:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'global; as-req to krbtgt',
|
|
'groups': {
|
|
# The behaviour should be the same with a Global group.
|
|
'foo': (GroupType.GLOBAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'global; as-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.GLOBAL, {user}),
|
|
},
|
|
'as:to_krbtgt': False,
|
|
'as:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'domain-local; as-req to krbtgt',
|
|
'groups': {
|
|
# A Domain-local group containing the user.
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
# A TGT will not contain domain-local groups the user belongs
|
|
# to.
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'domain-local; compression; as-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'as:to_krbtgt': False,
|
|
'as:expected': {
|
|
# However, a service ticket will include domain-local
|
|
# groups. The account supports SID compression, so they are
|
|
# added as resource SIDs.
|
|
('foo', SidType.RESOURCE_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'domain-local; no compression; as-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'as:to_krbtgt': False,
|
|
# This time, the target account disclaims support for SID
|
|
# compression.
|
|
'as:compression': False,
|
|
'as:expected': {
|
|
# The SIDs in the PAC are the same, except the group SID is
|
|
# placed in Extra SIDs, not Resource SIDs.
|
|
('foo', SidType.EXTRA_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'nested domain-local; as-req to krbtgt',
|
|
'groups': {
|
|
# A Universal group containing a Domain-local group containing
|
|
# the user.
|
|
'universal': (GroupType.UNIVERSAL, {'dom-local'}),
|
|
'dom-local': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
# It is not possible in Windows for a Universal group to contain a
|
|
# Domain-local group without exploiting bugs. This flag provides a
|
|
# convenient means by which these tests can be skipped.
|
|
'configuration_invalid': True,
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
# While Windows would exclude the universal group from the PAC,
|
|
# expecting its inclusion is more sensible on the whole.
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'nested domain-local; compression; as-req to service',
|
|
'groups': {
|
|
'universal': (GroupType.UNIVERSAL, {'dom-local'}),
|
|
'dom-local': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'configuration_invalid': True,
|
|
'as:to_krbtgt': False,
|
|
'as:expected': {
|
|
# A service ticket is expected to include both SIDs.
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
('dom-local', SidType.RESOURCE_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'nested domain-local; no compression; as-req to service',
|
|
'groups': {
|
|
'universal': (GroupType.UNIVERSAL, {'dom-local'}),
|
|
'dom-local': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'configuration_invalid': True,
|
|
'as:to_krbtgt': False,
|
|
'as:compression': False,
|
|
'as:expected': {
|
|
# As before, but disclaiming SID compression support, so the
|
|
# domain-local SID goes in Extra SIDs.
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
('dom-local', SidType.EXTRA_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'nested universal; as-req to krbtgt',
|
|
'groups': {
|
|
# A similar scenario, except flipped around: a Domain-local
|
|
# group containing a Universal group containing the user.
|
|
'dom-local': (GroupType.DOMAIN_LOCAL, {'universal'}),
|
|
'universal': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
# Expect the Universal group's inclusion in the PAC.
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'nested universal; compression; as-req to service',
|
|
'groups': {
|
|
'dom-local': (GroupType.DOMAIN_LOCAL, {'universal'}),
|
|
'universal': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
'as:to_krbtgt': False,
|
|
'as:expected': {
|
|
# Expect a service ticket to contain both SIDs.
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
('dom-local', SidType.RESOURCE_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'nested universal; no compression; as-req to service',
|
|
'groups': {
|
|
'dom-local': (GroupType.DOMAIN_LOCAL, {'universal'}),
|
|
'universal': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
'as:to_krbtgt': False,
|
|
'as:compression': False,
|
|
'as:expected': {
|
|
# As before, but disclaiming SID compression support, so the
|
|
# domain-local SID goes in Extra SIDs.
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
('dom-local', SidType.EXTRA_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
# TGS-REQ tests.
|
|
{
|
|
'test': 'tgs-req to krbtgt',
|
|
'groups': {
|
|
# A Universal group containing the user.
|
|
'foo': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
# Make a TGS-REQ to the krbtgt with the user's account.
|
|
'tgs:to_krbtgt': True,
|
|
'tgs:expected': {
|
|
# Expect the same results as with an AS-REQ.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'tgs-req to service',
|
|
'groups': {
|
|
# A Universal group containing the user.
|
|
'foo': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
# Make a TGS-REQ to a service with the user's account.
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'domain-local; tgs-req to krbtgt',
|
|
'groups': {
|
|
# A Domain-local group containing the user.
|
|
'foo': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:to_krbtgt': True,
|
|
'tgs:expected': {
|
|
# Expect the same results as with an AS-REQ.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'domain-local; compression; tgs-req to service',
|
|
'groups': {
|
|
# A Domain-local group containing the user.
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
# The Domain-local group is not present in the PAC after an
|
|
# AS-REQ.
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:expected': {
|
|
# Now it's added as a resource SID after the TGS-REQ.
|
|
('foo', SidType.RESOURCE_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'domain-local; no compression; tgs-req to service',
|
|
'groups': {
|
|
# A Domain-local group containing the user.
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
# This time, the target account disclaims support for SID
|
|
# compression.
|
|
'as:expected': {
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:compression': False,
|
|
'tgs:expected': {
|
|
# The SIDs in the PAC are the same, except the group SID is
|
|
# placed in Extra SIDs, not Resource SIDs.
|
|
('foo', SidType.EXTRA_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'exclude asserted identity; tgs-req to krbtgt',
|
|
'groups': {
|
|
'foo': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': True,
|
|
'tgs:sids': {
|
|
# Remove the Asserted Identity SID from the PAC.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# It should not be re-added in the TGS-REQ.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'exclude asserted identity; tgs-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
# Nor should it be re-added if the TGS-REQ is directed to a
|
|
# service.
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:sids': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'exclude claims valid; tgs-req to krbtgt',
|
|
'groups': {
|
|
'foo': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': True,
|
|
'tgs:sids': {
|
|
# Remove the Claims Valid SID from the PAC.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# It should not be re-added in the TGS-REQ.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
},
|
|
},
|
|
{
|
|
'test': 'exclude claims valid; tgs-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
# Nor should it be re-added if the TGS-REQ is directed to a
|
|
# service.
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:sids': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
},
|
|
},
|
|
{
|
|
'test': 'user group removal; tgs-req to krbtgt',
|
|
'groups': {
|
|
# The user has been removed from the group...
|
|
'foo': (GroupType.UNIVERSAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': True,
|
|
'tgs:sids': {
|
|
# ...but the user's PAC still contains the group SID.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# The group SID should not be removed when a TGS-REQ is
|
|
# performed.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'user group removal; tgs-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.UNIVERSAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
# Likewise, but to a service.
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:sids': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'nested group removal; tgs-req to krbtgt',
|
|
'groups': {
|
|
# A Domain-local group contains a Universal group, of which the
|
|
# user is no longer a member...
|
|
'dom-local': (GroupType.DOMAIN_LOCAL, {'universal'}),
|
|
'universal': (GroupType.UNIVERSAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': True,
|
|
'tgs:sids': {
|
|
# ...but the user's PAC still contains the group SID.
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# The group SID should not be removed when a TGS-REQ is
|
|
# performed.
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'nested group removal; compression; tgs-req to service',
|
|
'groups': {
|
|
# A Domain-local group contains a Universal group, of which the
|
|
# user is no longer a member...
|
|
'dom-local': (GroupType.DOMAIN_LOCAL, {'universal'}),
|
|
'universal': (GroupType.UNIVERSAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:sids': {
|
|
# ...but the user's PAC still contains the group SID.
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# Both SIDs should be present in the PAC when a TGS-REQ is
|
|
# performed.
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
('dom-local', SidType.RESOURCE_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'nested group removal; no compression; tgs-req to service',
|
|
'groups': {
|
|
'dom-local': (GroupType.DOMAIN_LOCAL, {'universal'}),
|
|
'universal': (GroupType.UNIVERSAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': False,
|
|
# The same again, but with the server not supporting compression.
|
|
'tgs:compression': False,
|
|
'tgs:sids': {
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# The domain-local SID will go into Extra SIDs.
|
|
('universal', SidType.BASE_SID, default_attrs),
|
|
('dom-local', SidType.EXTRA_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'resource sids given; tgs-req to krbtgt',
|
|
'groups': {
|
|
# A couple of independent domain-local groups.
|
|
'dom-local-0': (GroupType.DOMAIN_LOCAL, {}),
|
|
'dom-local-1': (GroupType.DOMAIN_LOCAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': True,
|
|
'tgs:sids': {
|
|
# The TGT contains two resource SIDs for the domain-local
|
|
# groups.
|
|
('dom-local-0', SidType.RESOURCE_SID, resource_attrs),
|
|
('dom-local-1', SidType.RESOURCE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# The resource SIDs remain after performing a TGS-REQ to the
|
|
# krbtgt.
|
|
('dom-local-0', SidType.RESOURCE_SID, resource_attrs),
|
|
('dom-local-1', SidType.RESOURCE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'resource sids wrongly given; tgs-req to krbtgt',
|
|
'groups': {
|
|
'dom-local-0': (GroupType.DOMAIN_LOCAL, {}),
|
|
'dom-local-1': (GroupType.DOMAIN_LOCAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': True,
|
|
# Though we have provided resource SIDs, we have reset the flag
|
|
# indicating that they are present.
|
|
'tgs:reset_user_flags': netlogon.NETLOGON_RESOURCE_GROUPS,
|
|
'tgs:sids': {
|
|
('dom-local-0', SidType.RESOURCE_SID, resource_attrs),
|
|
('dom-local-1', SidType.RESOURCE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
# The resource SIDs remain in the PAC.
|
|
('dom-local-0', SidType.RESOURCE_SID, resource_attrs),
|
|
('dom-local-1', SidType.RESOURCE_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'resource sids claimed given; tgs-req to krbtgt',
|
|
'groups': {
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': True,
|
|
# Though we claim to have provided resource SIDs, we have not
|
|
# actually done so.
|
|
'tgs:set_user_flags': netlogon.NETLOGON_RESOURCE_GROUPS,
|
|
'tgs:sids': {
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'resource sids given; compression; tgs-req to service',
|
|
'groups': {
|
|
'dom-local-0': (GroupType.DOMAIN_LOCAL, {}),
|
|
'dom-local-1': (GroupType.DOMAIN_LOCAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:sids': {
|
|
('dom-local-0', SidType.RESOURCE_SID, resource_attrs),
|
|
('dom-local-1', SidType.RESOURCE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# The resource SIDs are removed upon issuing a service ticket.
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'resource sids given; no compression; tgs-req to service',
|
|
'groups': {
|
|
'dom-local-0': (GroupType.DOMAIN_LOCAL, {}),
|
|
'dom-local-1': (GroupType.DOMAIN_LOCAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': False,
|
|
# Compression is disabled on the service account.
|
|
'tgs:compression': False,
|
|
'tgs:sids': {
|
|
('dom-local-0', SidType.RESOURCE_SID, resource_attrs),
|
|
('dom-local-1', SidType.RESOURCE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# The resource SIDs are again removed.
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
# Testing operability with older Samba versions.
|
|
{
|
|
'test': 'domain-local; Samba 4.17; tgs-req to krbtgt',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': True,
|
|
'tgs:sids': {
|
|
# In Samba 4.17, domain-local groups are contained within the
|
|
# TGT, and do not have the SE_GROUP_RESOURCE bit set.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# After the TGS-REQ, the domain-local group remains in the PAC
|
|
# with its original attributes.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
},
|
|
},
|
|
{
|
|
'test': 'domain-local; Samba 4.17; compression; tgs-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
# The same scenario, but requesting a service ticket.
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:compression': True,
|
|
'tgs:sids': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# The domain-local group remains in the PAC...
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
# and another copy is added in Resource SIDs. This one has the
|
|
# SE_GROUP_RESOURCE bit set.
|
|
('foo', SidType.RESOURCE_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
},
|
|
},
|
|
{
|
|
'test': 'domain-local; Samba 4.17; no compression; tgs-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': False,
|
|
# In this case compression is disabled on the service.
|
|
'tgs:compression': False,
|
|
'tgs:sids': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
# Without compression, the extra SID appears in Extra SIDs.
|
|
('foo', SidType.EXTRA_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
},
|
|
},
|
|
# Simulate a ticket coming in over a trust.
|
|
{
|
|
'test': 'from trust; to krbtgt',
|
|
'groups': {
|
|
# The user belongs to a couple of domain-local groups in our
|
|
# domain.
|
|
'foo': (GroupType.DOMAIN_LOCAL, {trust_user}),
|
|
'bar': (GroupType.DOMAIN_LOCAL, {'foo'}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': True,
|
|
# The user SID is from a different domain.
|
|
'tgs:user_sid': trust_user,
|
|
'tgs:sids': {
|
|
(trust_user, SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
# This dummy resource SID comes from the trusted domain.
|
|
(f'{trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# After performing a TGS-REQ to the krbtgt, the PAC remains
|
|
# unchanged.
|
|
(trust_user, SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
(f'{trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'from trust; compression; to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {trust_user}),
|
|
'bar': (GroupType.DOMAIN_LOCAL, {'foo'}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
# The same thing, but to a service.
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:compression': True,
|
|
'tgs:user_sid': trust_user,
|
|
'tgs:sids': {
|
|
(trust_user, SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(f'{trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
(trust_user, SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
# The resource SIDs are added to the PAC.
|
|
('foo', SidType.RESOURCE_SID, resource_attrs),
|
|
('bar', SidType.RESOURCE_SID, resource_attrs),
|
|
},
|
|
},
|
|
# Simulate a ticket coming in over a trust
|
|
{
|
|
'test': 'from trust; no compression; to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {trust_user}),
|
|
'bar': (GroupType.DOMAIN_LOCAL, {'foo'}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': False,
|
|
# And again, but this time compression is disabled.
|
|
'tgs:compression': False,
|
|
'tgs:user_sid': trust_user,
|
|
'tgs:sids': {
|
|
(trust_user, SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(f'{trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
(trust_user, SidType.BASE_SID, default_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
|
|
# The resource SIDs are added again, but this time to Extra
|
|
# SIDs.
|
|
('foo', SidType.EXTRA_SID, resource_attrs),
|
|
('bar', SidType.EXTRA_SID, resource_attrs),
|
|
},
|
|
},
|
|
# Test a group being the primary one for the user.
|
|
{
|
|
'test': 'primary universal; as-req to krbtgt',
|
|
'groups': {
|
|
'foo': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
# Set this group as our primary group.
|
|
'primary_group': 'foo',
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
# It appears in the PAC as normal.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'primary universal; as-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.UNIVERSAL, {user}),
|
|
},
|
|
# Set this group as our primary group.
|
|
'primary_group': 'foo',
|
|
# The request is made to a service.
|
|
'as:to_krbtgt': False,
|
|
'as:expected': {
|
|
# The group appears in the PAC as normal.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
# Test domain-local primary groups.
|
|
{
|
|
'test': 'primary domain-local; as-req to krbtgt',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
# Though Windows normally disallows setting a domain-local group as
|
|
# a primary group, Samba does not.
|
|
'primary_group': 'foo',
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
# The domain-local group appears as our primary GID, but does
|
|
# not appear in the base SIDs.
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'primary domain-local; compression; as-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'primary_group': 'foo',
|
|
# The same test, but the request is made to a service.
|
|
'as:to_krbtgt': False,
|
|
'as:expected': {
|
|
# The domain-local still only appears as our primary GID.
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'primary domain-local; no compression; as-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'primary_group': 'foo',
|
|
'as:to_krbtgt': False,
|
|
# This time, the target account disclaims support for SID
|
|
# compression.
|
|
'as:compression': False,
|
|
'as:expected': {
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'primary domain-local; tgs-req to krbtgt',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
# Though Windows normally disallows setting a domain-local group as
|
|
# a primary group, Samba does not.
|
|
'primary_group': 'foo',
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
# The domain-local group appears as our primary GID, but does
|
|
# not appear in the base SIDs.
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:to_krbtgt': True,
|
|
'tgs:expected': {
|
|
# The domain-local group does not appear in the base SIDs.
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'primary domain-local; compression; tgs-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
# Though Windows normally disallows setting a domain-local group as
|
|
# a primary group, Samba does not.
|
|
'primary_group': 'foo',
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
# The domain-local group appears as our primary GID, but does
|
|
# not appear in the base SIDs.
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
# The service is made to a service.
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:expected': {
|
|
# The domain-local still only appears as our primary GID.
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'primary domain-local; no compression; tgs-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
# Though Windows normally disallows setting a domain-local group as
|
|
# a primary group, Samba does not.
|
|
'primary_group': 'foo',
|
|
'as:to_krbtgt': True,
|
|
'as:expected': {
|
|
# The domain-local group appears as our primary GID, but does
|
|
# not appear in the base SIDs.
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:to_krbtgt': False,
|
|
# The service does not support compression.
|
|
'tgs:compression': False,
|
|
'tgs:expected': {
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
# Test the scenario where we belong to a now-domain-local group, and
|
|
# possess an old TGT issued when the group was still our primary one.
|
|
{
|
|
'test': 'old primary domain-local; tgs-req to krbtgt',
|
|
'groups': {
|
|
# A domain-local group to which we belong.
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': True,
|
|
'tgs:sids': {
|
|
# In the PAC, the group has the attributes of an ordinary
|
|
# group...
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
# ...and remains our primary one.
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# The groups don't change.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'old primary domain-local; compression; tgs-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
# The TGS request is made to a service.
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:sids': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
# The group is added a second time to the PAC, now as a
|
|
# resource group.
|
|
('foo', SidType.RESOURCE_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'old primary domain-local; no compression; tgs-req to service',
|
|
'groups': {
|
|
'foo': (GroupType.DOMAIN_LOCAL, {user}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': False,
|
|
# The target service doesn't support SID compression.
|
|
'tgs:compression': False,
|
|
'tgs:sids': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
# This time, the group is added to Extra SIDs.
|
|
('foo', SidType.EXTRA_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
# Test the scenario where we possess an old TGT issued when a
|
|
# now-domain-local group was still our primary one. We no longer belong
|
|
# to that group, which itself belongs to another domain-local group.
|
|
{
|
|
'test': 'old primary domain-local; transitive; tgs-req to krbtgt',
|
|
'groups': {
|
|
'bar': (GroupType.DOMAIN_LOCAL, {'foo'}),
|
|
'foo': (GroupType.DOMAIN_LOCAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': True,
|
|
'tgs:sids': {
|
|
# In the PAC, the group has the attributes of an ordinary
|
|
# group...
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
# ...and remains our primary one.
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
# The groups don't change.
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'old primary domain-local; transitive; compression; tgs-req to service',
|
|
'groups': {
|
|
'bar': (GroupType.DOMAIN_LOCAL, {'foo'}),
|
|
'foo': (GroupType.DOMAIN_LOCAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
# The TGS request is made to a service.
|
|
'tgs:to_krbtgt': False,
|
|
'tgs:sids': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
# The second resource group is added to the PAC as a resource
|
|
# group.
|
|
('bar', SidType.RESOURCE_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
{
|
|
'test': 'old primary domain-local; transitive; no compression; tgs-req to service',
|
|
'groups': {
|
|
'bar': (GroupType.DOMAIN_LOCAL, {'foo'}),
|
|
'foo': (GroupType.DOMAIN_LOCAL, {}),
|
|
},
|
|
'as:to_krbtgt': True,
|
|
'tgs:to_krbtgt': False,
|
|
# The target service doesn't support SID compression.
|
|
'tgs:compression': False,
|
|
'tgs:sids': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
'tgs:expected': {
|
|
('foo', SidType.BASE_SID, default_attrs),
|
|
('foo', SidType.PRIMARY_GID, None),
|
|
# This time, the group is added to Extra SIDs.
|
|
('bar', SidType.EXTRA_SID, resource_attrs),
|
|
(asserted_identity, SidType.EXTRA_SID, default_attrs),
|
|
(security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
|
|
(security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
|
|
},
|
|
},
|
|
]
|
|
|
|
# Create a new group and return a Principal object representing it.
|
|
def create_group_principal(self, samdb, group_type):
|
|
name = self.get_new_username()
|
|
dn = self.create_group(samdb, name, gtype=group_type.value)
|
|
sid = self.get_objectSid(samdb, dn)
|
|
|
|
return Principal(ldb.Dn(samdb, dn), sid)
|
|
|
|
claims_valid_sid = (security.SID_CLAIMS_VALID,
|
|
SidType.EXTRA_SID,
|
|
default_attrs)
|
|
|
|
# Return SIDs from principal placeholders based on a supplied mapping.
|
|
def map_sids(self, sids, mapping, domain_sid):
|
|
if sids is None:
|
|
return None
|
|
|
|
mapped_sids = set()
|
|
for sid, sid_type, attrs in sids:
|
|
if isinstance(sid, int):
|
|
# If it's an integer, we assume it's a RID, and prefix the
|
|
# domain SID.
|
|
sid = f'{domain_sid}-{sid}'
|
|
elif sid in mapping:
|
|
# Or if we have a mapping for it, apply that. Otherwise leave
|
|
# it unmodified.
|
|
sid = mapping[sid].sid
|
|
|
|
# There's no point expecting the 'Claims Valid' SID to be present
|
|
# if we don't support claims. Filter it out to give the tests a
|
|
# chance of passing.
|
|
if not self.kdc_claims_support and (
|
|
sid == security.SID_CLAIMS_VALID):
|
|
continue
|
|
|
|
mapped_sids.add((sid, sid_type, attrs))
|
|
|
|
return mapped_sids
|
|
|
|
def set_primary_group(self, samdb, dn, primary_sid,
|
|
expected_error=None,
|
|
expected_werror=None):
|
|
# Get the RID to be set as our primary group.
|
|
primary_rid = primary_sid.rsplit('-', 1)[1]
|
|
|
|
# Find out our current primary group.
|
|
res = samdb.search(dn,
|
|
scope=ldb.SCOPE_BASE,
|
|
attrs=['primaryGroupId'])
|
|
orig_msg = res[0]
|
|
|
|
# Prepare to modify the attribute.
|
|
msg = ldb.Message(dn)
|
|
msg['primaryGroupId'] = ldb.MessageElement(str(primary_rid),
|
|
ldb.FLAG_MOD_REPLACE,
|
|
'primaryGroupId')
|
|
|
|
# We'll remove the primaryGroupId attribute after the test, to avoid
|
|
# problems in the teardown if the user outlives the group.
|
|
remove_msg = samdb.msg_diff(msg, orig_msg)
|
|
self.addCleanup(samdb.modify, remove_msg)
|
|
|
|
# Set primaryGroupId.
|
|
if expected_error is None:
|
|
self.assertIsNone(expected_werror)
|
|
|
|
samdb.modify(msg)
|
|
else:
|
|
self.assertIsNotNone(expected_werror)
|
|
|
|
with self.assertRaises(
|
|
ldb.LdbError,
|
|
msg='expected setting primary group to fail'
|
|
) as err:
|
|
samdb.modify(msg)
|
|
|
|
error, estr = err.exception.args
|
|
self.assertEqual(expected_error, error)
|
|
self.assertIn(f'{expected_werror:08X}', estr)
|
|
|
|
def set_group_type(self, samdb, dn, gtype):
|
|
group_type = common.normalise_int32(gtype.value)
|
|
msg = ldb.Message(dn)
|
|
msg['groupType'] = ldb.MessageElement(group_type,
|
|
ldb.FLAG_MOD_REPLACE,
|
|
'groupType')
|
|
samdb.modify(msg)
|
|
|
|
# Create an arrangement on groups based on a configuration specified in a
|
|
# test case. 'user_principal' is a principal representing the user account;
|
|
# 'trust_principal', a principal representing the account of a user from
|
|
# another domain.
|
|
def setup_groups(self,
|
|
samdb,
|
|
group_setup,
|
|
user_principal,
|
|
trust_principal,
|
|
primary_group):
|
|
# Initialiase the group mapping with the user principal.
|
|
groups = {
|
|
self.user: user_principal,
|
|
self.trust_user: trust_principal,
|
|
}
|
|
|
|
primary_group_type = None
|
|
|
|
# Create each group and add it to the group mapping.
|
|
for group_id, (group_type, _) in group_setup.items():
|
|
self.assertIsNot(group_id, self.user,
|
|
"don't specify user placeholder")
|
|
self.assertNotIn(group_id, groups,
|
|
'group ID specified more than once')
|
|
|
|
if primary_group is not None and group_id == primary_group:
|
|
# Windows disallows setting a domain-local group as a primary
|
|
# group, unless we create it as Universal first and change it
|
|
# back to Domain-Local later.
|
|
primary_group_type = group_type
|
|
group_type = GroupType.UNIVERSAL
|
|
|
|
groups[group_id] = self.create_group_principal(samdb, group_type)
|
|
|
|
# Map a group ID to that group's DN, and generate an
|
|
# understandable error message if the mapping fails.
|
|
def group_id_to_dn(group_id):
|
|
try:
|
|
group = groups[group_id]
|
|
except KeyError:
|
|
self.fail(f"included group member '{group_id}', but it is not "
|
|
f"specified in {groups.keys()}")
|
|
else:
|
|
if group.dn is not None:
|
|
return str(group.dn)
|
|
|
|
return f'<SID={group.sid}>'
|
|
|
|
# Populate each group's members.
|
|
for group_id, (_, members) in group_setup.items():
|
|
# Get the group's DN and the mapped DNs of its members.
|
|
dn = groups[group_id].dn
|
|
principal_members = map(group_id_to_dn, members)
|
|
|
|
# Add the members to the group.
|
|
self.add_to_group(principal_members, dn, 'member',
|
|
expect_attr=False)
|
|
|
|
# Set the user's primary group.
|
|
if primary_group is not None:
|
|
primary_sid = groups[primary_group].sid
|
|
self.set_primary_group(samdb, user_principal.dn, primary_sid)
|
|
|
|
# Change the primary group to its actual group type.
|
|
if primary_group_type is not None:
|
|
self.set_group_type(samdb,
|
|
groups[primary_group].dn,
|
|
primary_group_type)
|
|
|
|
# Return the mapping from group IDs to principals.
|
|
return groups
|
|
|
|
# This is the main function to handle a single testcase.
|
|
def _test_group_with_args(self, case):
|
|
# The group arrangement for the test.
|
|
group_setup = case.pop('groups')
|
|
|
|
# A group that should be the primary group for the user.
|
|
primary_group = case.pop('primary_group', None)
|
|
|
|
# Whether the AS-REQ or TGS-REQ should be directed to the krbtgt.
|
|
as_to_krbtgt = case.pop('as:to_krbtgt')
|
|
tgs_to_krbtgt = case.pop('tgs:to_krbtgt', None)
|
|
|
|
# Whether the target server of the AS-REQ or TGS-REQ should support
|
|
# resource SID compression.
|
|
as_compression = case.pop('as:compression', None)
|
|
tgs_compression = case.pop('tgs:compression', None)
|
|
|
|
# Optional SIDs to replace those in the PAC prior to a TGS-REQ.
|
|
tgs_sids = case.pop('tgs:sids', None)
|
|
|
|
# Optional user SID to replace that in the PAC prior to a TGS-REQ.
|
|
tgs_user_sid = case.pop('tgs:user_sid', None)
|
|
|
|
# User flags that may be set or reset in the PAC prior to a TGS-REQ.
|
|
tgs_set_user_flags = case.pop('tgs:set_user_flags', None)
|
|
tgs_reset_user_flags = case.pop('tgs:reset_user_flags', None)
|
|
|
|
# The SIDs we expect to see in the PAC after a AS-REQ or a TGS-REQ.
|
|
as_expected = case.pop('as:expected', None)
|
|
tgs_expected = case.pop('tgs:expected', None)
|
|
|
|
# There should be no parameters remaining in the testcase.
|
|
self.assertFalse(case, 'unexpected parameters in testcase')
|
|
|
|
if as_expected is None:
|
|
self.assertIsNotNone(tgs_expected,
|
|
'no set of expected SIDs is provided')
|
|
|
|
if as_to_krbtgt is None:
|
|
as_to_krbtgt = False
|
|
|
|
if not as_to_krbtgt:
|
|
self.assertIsNone(tgs_expected,
|
|
"if we're performing a TGS-REQ, then AS-REQ "
|
|
"should be directed to the krbtgt")
|
|
|
|
if tgs_to_krbtgt is None:
|
|
tgs_to_krbtgt = False
|
|
else:
|
|
self.assertIsNotNone(tgs_expected,
|
|
'specified TGS request to krbtgt, but no '
|
|
'expected SIDs provided')
|
|
|
|
if tgs_compression is not None:
|
|
self.assertIsNotNone(tgs_expected,
|
|
'specified compression for TGS request, but '
|
|
'no expected SIDs provided')
|
|
|
|
if tgs_user_sid is not None:
|
|
self.assertIsNotNone(tgs_sids,
|
|
'specified TGS-REQ user SID, but no '
|
|
'accompanying SIDs provided')
|
|
|
|
if tgs_set_user_flags is None:
|
|
tgs_set_user_flags = 0
|
|
else:
|
|
self.assertIsNotNone(tgs_sids,
|
|
'specified TGS-REQ set user flags, but no '
|
|
'accompanying SIDs provided')
|
|
|
|
if tgs_reset_user_flags is None:
|
|
tgs_reset_user_flags = 0
|
|
else:
|
|
self.assertIsNotNone(tgs_sids,
|
|
'specified TGS-REQ reset user flags, but no '
|
|
'accompanying SIDs provided')
|
|
|
|
samdb = self.get_samdb()
|
|
|
|
domain_sid = samdb.get_domain_sid()
|
|
|
|
# Create the user account. It needs to be freshly created rather than
|
|
# cached because we will probably add it to one or more groups.
|
|
user_creds = self.get_cached_creds(
|
|
account_type=self.AccountType.USER,
|
|
use_cache=False)
|
|
user_dn = user_creds.get_dn()
|
|
user_sid = self.get_objectSid(samdb, user_dn)
|
|
user_name = user_creds.get_username()
|
|
salt = user_creds.get_salt()
|
|
|
|
trust_user_rid = random.randint(2000, 0xfffffffe)
|
|
trust_user_sid = f'{self.trust_domain}-{trust_user_rid}'
|
|
|
|
cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
|
|
names=user_name.split('/'))
|
|
|
|
preauth_key = self.PasswordKey_from_creds(user_creds,
|
|
kcrypto.Enctype.AES256)
|
|
|
|
ts_enc_padata = self.get_enc_timestamp_pa_data_from_key(preauth_key)
|
|
padata = [ts_enc_padata]
|
|
|
|
target_creds, sname = self.get_target(as_to_krbtgt, as_compression)
|
|
decryption_key = self.TicketDecryptionKey_from_creds(target_creds)
|
|
|
|
target_supported_etypes = target_creds.tgs_supported_enctypes
|
|
realm = target_creds.get_realm()
|
|
|
|
user_principal = Principal(user_dn, user_sid)
|
|
trust_principal = Principal(None, trust_user_sid)
|
|
groups = self.setup_groups(samdb,
|
|
group_setup,
|
|
user_principal,
|
|
trust_principal,
|
|
primary_group)
|
|
del group_setup
|
|
|
|
if tgs_user_sid is None:
|
|
tgs_user_sid = user_sid
|
|
elif tgs_user_sid in groups:
|
|
tgs_user_sid = groups[tgs_user_sid].sid
|
|
|
|
tgs_domain_sid, tgs_user_rid = tgs_user_sid.rsplit('-', 1)
|
|
|
|
expected_groups = self.map_sids(as_expected, groups,
|
|
domain_sid)
|
|
tgs_sids_mapped = self.map_sids(tgs_sids, groups,
|
|
tgs_domain_sid)
|
|
tgs_expected_mapped = self.map_sids(tgs_expected, groups,
|
|
tgs_domain_sid)
|
|
|
|
till = self.get_KerberosTime(offset=36000)
|
|
kdc_options = '0'
|
|
|
|
etypes = self.get_default_enctypes()
|
|
|
|
# Perform an AS-REQ with the user account.
|
|
as_rep, kdc_exchange_dict = self._test_as_exchange(
|
|
cname=cname,
|
|
realm=realm,
|
|
sname=sname,
|
|
till=till,
|
|
expected_error_mode=0,
|
|
expected_crealm=realm,
|
|
expected_cname=cname,
|
|
expected_srealm=realm,
|
|
expected_sname=sname,
|
|
expected_salt=salt,
|
|
etypes=etypes,
|
|
padata=padata,
|
|
kdc_options=kdc_options,
|
|
expected_account_name=user_name,
|
|
expected_groups=expected_groups,
|
|
expected_sid=user_sid,
|
|
expected_domain_sid=domain_sid,
|
|
expected_supported_etypes=target_supported_etypes,
|
|
preauth_key=preauth_key,
|
|
ticket_decryption_key=decryption_key)
|
|
self.check_as_reply(as_rep)
|
|
|
|
ticket = kdc_exchange_dict['rep_ticket_creds']
|
|
|
|
if tgs_expected is None:
|
|
# We're not performing a TGS-REQ, so we're done.
|
|
self.assertIsNone(tgs_sids,
|
|
'provided SIDs to populate PAC for TGS-REQ, but '
|
|
'failed to specify expected SIDs')
|
|
return
|
|
|
|
if tgs_sids is not None:
|
|
# Replace the SIDs in the PAC with the ones provided by the test.
|
|
ticket = self.ticket_with_sids(ticket,
|
|
tgs_sids_mapped,
|
|
tgs_domain_sid,
|
|
tgs_user_rid,
|
|
set_user_flags=tgs_set_user_flags,
|
|
reset_user_flags=tgs_reset_user_flags)
|
|
|
|
target_creds, sname = self.get_target(tgs_to_krbtgt, tgs_compression)
|
|
decryption_key = self.TicketDecryptionKey_from_creds(target_creds)
|
|
|
|
subkey = self.RandomKey(ticket.session_key.etype)
|
|
|
|
requester_sid = None
|
|
if tgs_to_krbtgt:
|
|
requester_sid = user_sid
|
|
|
|
expect_resource_groups_flag = None
|
|
if tgs_reset_user_flags & netlogon.NETLOGON_RESOURCE_GROUPS:
|
|
expect_resource_groups_flag = False
|
|
elif tgs_set_user_flags & netlogon.NETLOGON_RESOURCE_GROUPS:
|
|
expect_resource_groups_flag = True
|
|
|
|
# Perform a TGS-REQ with the user account.
|
|
|
|
kdc_exchange_dict = self.tgs_exchange_dict(
|
|
expected_crealm=ticket.crealm,
|
|
expected_cname=cname,
|
|
expected_srealm=realm,
|
|
expected_sname=sname,
|
|
expected_account_name=user_name,
|
|
expected_groups=tgs_expected_mapped,
|
|
expected_sid=tgs_user_sid,
|
|
expected_requester_sid=requester_sid,
|
|
expected_domain_sid=tgs_domain_sid,
|
|
expected_supported_etypes=target_supported_etypes,
|
|
expect_resource_groups_flag=expect_resource_groups_flag,
|
|
ticket_decryption_key=decryption_key,
|
|
check_rep_fn=self.generic_check_kdc_rep,
|
|
check_kdc_private_fn=self.generic_check_kdc_private,
|
|
tgt=ticket,
|
|
authenticator_subkey=subkey,
|
|
kdc_options=kdc_options)
|
|
|
|
rep = self._generic_kdc_exchange(kdc_exchange_dict,
|
|
cname=None,
|
|
realm=realm,
|
|
sname=sname,
|
|
till_time=till,
|
|
etypes=etypes)
|
|
self.check_reply(rep, KRB_TGS_REP)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
global_asn1_print = False
|
|
global_hexdump = False
|
|
import unittest
|
|
unittest.main()
|