tests/krb5: Add tests for device info
authorJoseph Sutton <josephsutton@catalyst.net.nz>
Thu, 2 Mar 2023 22:48:22 +0000 (11:48 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 8 Mar 2023 04:39:32 +0000 (04:39 +0000)
These tests verify that the groups in the device info structure in the
PAC are exactly as expected under various scenarios.

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
python/samba/tests/krb5/device_tests.py [new file with mode: 0755]
python/samba/tests/usage.py
selftest/knownfail_heimdal_kdc
selftest/knownfail_mit_kdc
source4/selftest/tests.py

diff --git a/python/samba/tests/krb5/device_tests.py b/python/samba/tests/krb5/device_tests.py
new file mode 100755 (executable)
index 0000000..8aed597
--- /dev/null
@@ -0,0 +1,2045 @@
+#!/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 samba.dcerpc import netlogon, security
+from samba.tests import DynamicTestCase, env_get_var_value
+from samba.tests.krb5 import kcrypto
+from samba.tests.krb5.kdc_base_test import GroupType, KDCBaseTest, Principal
+from samba.tests.krb5.raw_testcase import Krb5EncryptionKey, RawKerberosTest
+from samba.tests.krb5.rfc4120_constants import (
+    AES256_CTS_HMAC_SHA1_96,
+    ARCFOUR_HMAC_MD5,
+    KRB_TGS_REP,
+)
+
+SidType = RawKerberosTest.SidType
+
+global_asn1_print = False
+global_hexdump = False
+
+
+@DynamicTestCase
+class DeviceTests(KDCBaseTest):
+    # Placeholder objects that represent accounts undergoing testing.
+    user = object()
+    mach = object()
+    trust_user = object()
+    trust_mach = 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
+    compounded_auth = security.SID_COMPOUNDED_AUTHENTICATION
+
+    user_trust_domain = 'S-1-5-21-123-456-111'
+    mach_trust_domain = 'S-1-5-21-123-456-222'
+
+    def setUp(self):
+        super().setUp()
+        self.do_asn1_print = global_asn1_print
+        self.do_hexdump = global_hexdump
+
+    # Some general information on how Windows handles device info:
+
+    # All the SIDs in the computer's info3.sids end up in device.domain_groups
+    # (if they are in any domain), or in device.sids (if they are not). Even if
+    # netlogon.NETLOGON_EXTRA_SIDS is not set.
+
+    # The remainder of the SIDs in device.domain_groups come from an LDAP
+    # search of the computer's domain-local groups.
+
+    # None of the SIDs in the computer's logon_info.resource_groups.groups go
+    # anywhere. Even if netlogon.NETLOGON_RESOURCE_GROUPS is set.
+
+    # In summary:
+    # info3.base.groups => device.groups
+    # info3.sids => device.sids (if not in a domain)
+    # info3.sids => device.domain_groups (if in a domain)
+    # searched-for domain-local groups => device.domain_groups
+
+    # These searched-for domain-local groups are based on _all_ the groups in
+    # info3.base.groups and info3.sids. So if the account is no longer a member
+    # of a (universal or global) group that belongs to a domain-local group,
+    # but has that universal or global group in info3.base.groups or
+    # info3.sids, then the domain-local group will still get added to the
+    # PAC. But the resource groups don't affect this (presumably, they are
+    # being filtered out). Also, those groups the search is based on do not go
+    # in themselves, even if they are domain-local groups.
+
+    cases = [
+        {
+            # Make a TGS request to the krbtgt.
+            'test': 'basic to krbtgt',
+            'as:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            # Indicate this request is to the krbtgt.
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            # Make a TGS request to a service that supports SID compression.
+            'test': 'device to service compressed',
+            'as:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                # The compounded authentication SID indicates that we used FAST
+                # with a device's TGT.
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            # Make a TGS request to a service that lacks support for SID
+            # compression.
+            'test': 'device to service uncompressed',
+            'as:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            # SID compression is unsupported.
+            'tgs:compression': False,
+            # There is no change in the reply PAC.
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            # Make a TGS request to a service that lacks support for compound
+            # identity.
+            'test': 'device to service no compound id',
+            'as:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            # Compound identity is unsupported.
+            'tgs:compound_id': False,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            # The device info is still generated.
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'universal groups to krbtgt',
+            'groups': {
+                # The user and computer each belong to a couple of universal
+                # groups.
+                'group0': (GroupType.UNIVERSAL, {'group1'}),
+                'group1': (GroupType.UNIVERSAL, {user}),
+                'group2': (GroupType.UNIVERSAL, {'group3'}),
+                'group3': (GroupType.UNIVERSAL, {mach}),
+            },
+            'as:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                # The user's groups appear in the PAC of the TGT.
+                ('group0', SidType.BASE_SID, default_attrs),
+                ('group1', SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # So too for the computer's groups.
+                ('group2', SidType.BASE_SID, default_attrs),
+                ('group3', SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                # The user's groups appear in the TGS reply PAC.
+                ('group0', SidType.BASE_SID, default_attrs),
+                ('group1', SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            'test': 'universal groups to service',
+            'groups': {
+                'group0': (GroupType.UNIVERSAL, {'group1'}),
+                'group1': (GroupType.UNIVERSAL, {user}),
+                'group2': (GroupType.UNIVERSAL, {'group3'}),
+                'group3': (GroupType.UNIVERSAL, {mach}),
+            },
+            'as:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                ('group0', SidType.BASE_SID, default_attrs),
+                ('group1', SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                ('group2', SidType.BASE_SID, default_attrs),
+                ('group3', SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                ('group0', SidType.BASE_SID, default_attrs),
+                ('group1', SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # The computer's groups appear in the device info structure of
+                # the TGS reply PAC.
+                ('group2', SidType.BASE_SID, default_attrs),
+                ('group3', SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'domain-local groups to krbtgt',
+            'groups': {
+                # The user and computer each belong to a couple of domain-local
+                # groups.
+                'group0': (GroupType.DOMAIN_LOCAL, {'group1'}),
+                'group1': (GroupType.DOMAIN_LOCAL, {user}),
+                'group2': (GroupType.DOMAIN_LOCAL, {'group3'}),
+                'group3': (GroupType.DOMAIN_LOCAL, {mach}),
+            },
+            'as:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                # The user's domain-local group memberships do not appear.
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # Nor do the computer's.
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                # The user's groups do not appear in the TGS reply PAC.
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            'test': 'domain-local groups to service compressed',
+            'groups': {
+                'group0': (GroupType.DOMAIN_LOCAL, {'group1'}),
+                'group1': (GroupType.DOMAIN_LOCAL, {user}),
+                'group2': (GroupType.DOMAIN_LOCAL, {'group3'}),
+                'group3': (GroupType.DOMAIN_LOCAL, {mach}),
+            },
+            'as:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                # These groups appear as resource SIDs.
+                ('group0', SidType.RESOURCE_SID, resource_attrs),
+                ('group1', SidType.RESOURCE_SID, resource_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # The computer's groups appear together as resource SIDs.
+                frozenset([
+                    ('group2', SidType.RESOURCE_SID, resource_attrs),
+                    ('group3', SidType.RESOURCE_SID, resource_attrs),
+                ]),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'domain-local groups to service uncompressed',
+            'groups': {
+                'group0': (GroupType.DOMAIN_LOCAL, {'group1'}),
+                'group1': (GroupType.DOMAIN_LOCAL, {user}),
+                'group2': (GroupType.DOMAIN_LOCAL, {'group3'}),
+                'group3': (GroupType.DOMAIN_LOCAL, {mach}),
+            },
+            'as:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': False,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                # The user's groups now appear as extra SIDs.
+                ('group0', SidType.EXTRA_SID, resource_attrs),
+                ('group1', SidType.EXTRA_SID, resource_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # The computer's groups are still resource SIDs.
+                frozenset([
+                    ('group2', SidType.RESOURCE_SID, resource_attrs),
+                    ('group3', SidType.RESOURCE_SID, resource_attrs),
+                ]),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Test what happens if the computer is removed from a group prior to
+        # the TGS request.
+        {
+            'test': 'remove transitive domain-local groups to krbtgt',
+            'groups': {
+                # The computer is transitively a member of a couple of
+                # domain-local groups...
+                'dom-local-outer-0': (GroupType.DOMAIN_LOCAL, {'dom-local-inner'}),
+                'dom-local-outer-1': (GroupType.DOMAIN_LOCAL, {'universal-inner'}),
+                # ...via another domain-local group and a universal group.
+                'dom-local-inner': (GroupType.DOMAIN_LOCAL, {mach}),
+                'universal-inner': (GroupType.UNIVERSAL, {mach}),
+            },
+            # Just prior to the TGS request, the computer is removed from both
+            # inner groups. Domain-local groups will have not been added to the
+            # PAC at this point.
+            'tgs:mach:removed': {
+                'dom-local-inner',
+                'universal-inner',
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # Only the universal group appears in the PAC.
+                ('universal-inner', SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            'test': 'remove transitive domain-local groups to service compressed',
+            'groups': {
+                'dom-local-outer-0': (GroupType.DOMAIN_LOCAL, {'dom-local-inner'}),
+                'dom-local-outer-1': (GroupType.DOMAIN_LOCAL, {'universal-inner'}),
+                'dom-local-inner': (GroupType.DOMAIN_LOCAL, {mach}),
+                'universal-inner': (GroupType.UNIVERSAL, {mach}),
+            },
+            'tgs:mach:removed': {
+                'dom-local-inner',
+                'universal-inner',
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                ('universal-inner', SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # The universal group appears in the device info...
+                ('universal-inner', SidType.BASE_SID, default_attrs),
+                # ...along with the second domain-local group, even though the
+                # computer no longer belongs to it.
+                frozenset([
+                    ('dom-local-outer-1', SidType.RESOURCE_SID, resource_attrs),
+                ]),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'remove transitive domain-local groups to service uncompressed',
+            'groups': {
+                'dom-local-outer-0': (GroupType.DOMAIN_LOCAL, {'dom-local-inner'}),
+                'dom-local-outer-1': (GroupType.DOMAIN_LOCAL, {'universal-inner'}),
+                'dom-local-inner': (GroupType.DOMAIN_LOCAL, {mach}),
+                'universal-inner': (GroupType.UNIVERSAL, {mach}),
+            },
+            'tgs:mach:removed': {
+                'dom-local-inner',
+                'universal-inner',
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                ('universal-inner', SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': False,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                ('universal-inner', SidType.BASE_SID, default_attrs),
+                frozenset([
+                    ('dom-local-outer-1', SidType.RESOURCE_SID, resource_attrs),
+                ]),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Test what happens if the computer is added to a group prior to the
+        # TGS request.
+        {
+            'test': 'add transitive domain-local groups to krbtgt',
+            'groups': {
+                # We create a pair of groups, to be used presently.
+                'dom-local-outer': (GroupType.DOMAIN_LOCAL, {'universal-inner'}),
+                'universal-inner': (GroupType.UNIVERSAL, {}),
+            },
+            # Just prior to the TGS request, the computer is added to the inner
+            # group.
+            'tgs:mach:added': {
+                'universal-inner',
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            'test': 'add transitive domain-local groups to service compressed',
+            'groups': {
+                'dom-local-outer': (GroupType.DOMAIN_LOCAL, {'universal-inner'}),
+                'universal-inner': (GroupType.UNIVERSAL, {}),
+            },
+            'tgs:mach:added': {
+                'universal-inner',
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # The computer was not a member of the universal group at the
+                # time of obtaining a TGT, and said group did not make it into
+                # the PAC. Group expansion is only concerned with domain-local
+                # groups, none of which the machine currently belongs
+                # to. Therefore, neither group is present in the device info
+                # structure.
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'add transitive domain-local groups to service uncompressed',
+            'groups': {
+                'dom-local-outer': (GroupType.DOMAIN_LOCAL, {'universal-inner'}),
+                'universal-inner': (GroupType.UNIVERSAL, {}),
+            },
+            'tgs:mach:added': {
+                'universal-inner',
+            },
+            'as:mach:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': False,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Simulate a machine ticket coming in over a trust.
+        {
+            'test': 'from trust domain-local groups to service compressed',
+            'groups': {
+                # The machine belongs to a couple of domain-local groups in our
+                # domain.
+                'foo': (GroupType.DOMAIN_LOCAL, {trust_mach}),
+                'bar': (GroupType.DOMAIN_LOCAL, {'foo'}),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': True,
+            # The machine SID is from a different domain.
+            'tgs:mach_sid': trust_mach,
+            'tgs:mach:sids': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # This dummy resource SID comes from the trusted domain.
+                (f'{mach_trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # The domain-local groups end up in the device info.
+                frozenset([
+                    ('foo', SidType.RESOURCE_SID, resource_attrs),
+                    ('bar', SidType.RESOURCE_SID, resource_attrs),
+                ]),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'from trust domain-local groups to service uncompressed',
+            'groups': {
+                'foo': (GroupType.DOMAIN_LOCAL, {trust_mach}),
+                'bar': (GroupType.DOMAIN_LOCAL, {'foo'}),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': False,
+            'tgs:mach_sid': trust_mach,
+            'tgs:mach:sids': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                (f'{mach_trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                frozenset([
+                    ('foo', SidType.RESOURCE_SID, resource_attrs),
+                    ('bar', SidType.RESOURCE_SID, resource_attrs),
+                ]),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Simulate the user ticket coming in over a trust.
+        {
+            'test': 'user from trust domain-local groups to krbtgt',
+            'groups': {
+                # The user belongs to a couple of domain-local groups in our
+                # domain.
+                'group0': (GroupType.DOMAIN_LOCAL, {trust_user}),
+                'group1': (GroupType.DOMAIN_LOCAL, {'group0'}),
+            },
+            'tgs:to_krbtgt': True,
+            # Both SIDs are from a different domain.
+            'tgs:user_sid': trust_user,
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # This dummy resource SID comes from the trusted domain.
+                (f'{mach_trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # The dummy resource SID remains in the PAC.
+                (f'{mach_trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
+            },
+        },
+        {
+            'test': 'user from trust domain-local groups to service compressed',
+            'groups': {
+                'group0': (GroupType.DOMAIN_LOCAL, {trust_user}),
+                'group1': (GroupType.DOMAIN_LOCAL, {'group0'}),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': True,
+            'tgs:user_sid': trust_user,
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # This dummy resource SID comes from the trusted domain.
+                (f'{mach_trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                ('group0', SidType.RESOURCE_SID, resource_attrs),
+                ('group1', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'user from trust domain-local groups to service uncompressed',
+            'groups': {
+                'group0': (GroupType.DOMAIN_LOCAL, {trust_user}),
+                'group1': (GroupType.DOMAIN_LOCAL, {'group0'}),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': False,
+            'tgs:user_sid': trust_user,
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # This dummy resource SID comes from the trusted domain.
+                (f'{mach_trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                ('group0', SidType.EXTRA_SID, resource_attrs),
+                ('group1', SidType.EXTRA_SID, resource_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Simulate both tickets coming in over a trust.
+        {
+            'test': 'both from trust domain-local groups to krbtgt',
+            'groups': {
+                # The user and machine each belong to a couple of domain-local
+                # groups in our domain.
+                'group0': (GroupType.DOMAIN_LOCAL, {trust_user}),
+                'group1': (GroupType.DOMAIN_LOCAL, {'group0'}),
+                'group2': (GroupType.DOMAIN_LOCAL, {trust_mach}),
+                'group3': (GroupType.DOMAIN_LOCAL, {'group2'}),
+            },
+            'tgs:to_krbtgt': True,
+            # Both SIDs are from a different domain.
+            'tgs:user_sid': trust_user,
+            'tgs:mach_sid': trust_mach,
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # This dummy resource SID comes from the trusted domain.
+                (f'{mach_trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:mach:sids': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # This dummy resource SID comes from the trusted domain.
+                (f'{mach_trust_domain}-444', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # The dummy resource SID remains in the PAC.
+                (f'{mach_trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
+            },
+        },
+        {
+            'test': 'both from trust domain-local groups to service compressed',
+            'groups': {
+                # The machine belongs to a couple of domain-local groups in our
+                # domain.
+                'group0': (GroupType.DOMAIN_LOCAL, {trust_user}),
+                'group1': (GroupType.DOMAIN_LOCAL, {'group0'}),
+                'group2': (GroupType.DOMAIN_LOCAL, {trust_mach}),
+                'group3': (GroupType.DOMAIN_LOCAL, {'group2'}),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': True,
+            'tgs:user_sid': trust_user,
+            'tgs:mach_sid': trust_mach,
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # This dummy resource SID comes from the trusted domain.
+                (f'{mach_trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:mach:sids': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # This dummy resource SID comes from the trusted domain.
+                (f'{mach_trust_domain}-444', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                ('group0', SidType.RESOURCE_SID, resource_attrs),
+                ('group1', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # The domain-local groups end up in the device info.
+                frozenset([
+                    ('group2', SidType.RESOURCE_SID, resource_attrs),
+                    ('group3', SidType.RESOURCE_SID, resource_attrs),
+                ]),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'both from trust domain-local groups to service uncompressed',
+            'groups': {
+                'group0': (GroupType.DOMAIN_LOCAL, {trust_user}),
+                'group1': (GroupType.DOMAIN_LOCAL, {'group0'}),
+                'group2': (GroupType.DOMAIN_LOCAL, {trust_mach}),
+                'group3': (GroupType.DOMAIN_LOCAL, {'group2'}),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': False,
+            'tgs:user_sid': trust_user,
+            'tgs:mach_sid': trust_mach,
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # This dummy resource SID comes from the trusted domain.
+                (f'{mach_trust_domain}-333', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:mach:sids': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                # This dummy resource SID comes from the trusted domain.
+                (f'{mach_trust_domain}-444', SidType.RESOURCE_SID, resource_attrs),
+            },
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+                ('group0', SidType.EXTRA_SID, resource_attrs),
+                ('group1', SidType.EXTRA_SID, resource_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                frozenset([
+                    ('group2', SidType.RESOURCE_SID, resource_attrs),
+                    ('group3', SidType.RESOURCE_SID, resource_attrs),
+                ]),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Test how resource SIDs are propagated into the device info structure.
+        {
+            'test': 'mach resource sids',
+            'tgs:mach:sids': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                # Of these SIDs, the Base SIDs and Extra SIDs are all
+                # propagated into the device info structure, regardless of
+                # their attrs, while the Resource SIDs are all dropped.
+                (123, SidType.BASE_SID, default_attrs),
+                (333, SidType.BASE_SID, default_attrs),
+                (333, SidType.BASE_SID, resource_attrs),
+                (1000, SidType.BASE_SID, resource_attrs),
+                (497, SidType.EXTRA_SID, resource_attrs),  # the Claims Valid RID.
+                (333, SidType.RESOURCE_SID, default_attrs),
+                (498, SidType.RESOURCE_SID, resource_attrs),
+                (99999, SidType.RESOURCE_SID, default_attrs),
+                (12345678, SidType.RESOURCE_SID, resource_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (123, SidType.BASE_SID, default_attrs),
+                (333, SidType.BASE_SID, default_attrs),
+                (333, SidType.BASE_SID, resource_attrs),
+                (1000, SidType.BASE_SID, resource_attrs),
+                frozenset({
+                    (497, SidType.RESOURCE_SID, resource_attrs),
+                }),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Add a Base SID to the user's PAC, and confirm it is propagated into
+        # the PAC of the service ticket.
+        {
+            'test': 'base sid to krbtgt',
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (123, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (123, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            'test': 'base sid to service',
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (123, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (123, SidType.BASE_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Add a Base SID with resource attrs to the user's PAC, and confirm it
+        # is propagated into the PAC of the service ticket.
+        {
+            'test': 'base sid resource attrs to krbtgt',
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (123, SidType.BASE_SID, resource_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (123, SidType.BASE_SID, resource_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            'test': 'base sid resource attrs to service',
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (123, SidType.BASE_SID, resource_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (123, SidType.BASE_SID, resource_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Add a couple of Extra SIDs to the user's PAC, and confirm they are
+        # propagated into the PAC of the service ticket.
+        {
+            'test': 'extra sids to krbtgt',
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                ('S-1-5-2-3-4', SidType.EXTRA_SID, default_attrs),
+                ('S-1-5-2-3-5', SidType.EXTRA_SID, resource_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                ('S-1-5-2-3-4', SidType.EXTRA_SID, default_attrs),
+                ('S-1-5-2-3-5', SidType.EXTRA_SID, resource_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            'test': 'extra sids to service',
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                ('S-1-5-2-3-4', SidType.EXTRA_SID, default_attrs),
+                ('S-1-5-2-3-5', SidType.EXTRA_SID, resource_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                ('S-1-5-2-3-4', SidType.EXTRA_SID, default_attrs),
+                ('S-1-5-2-3-5', SidType.EXTRA_SID, resource_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Test what happens if we remove the CLAIMS_VALID and ASSERTED_IDENTITY
+        # SIDs from either of the PACs, so we can see at what point these SIDs
+        # are added.
+        {
+            'test': 'removed special sids to krbtgt',
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                ('S-1-5-2-3-4', SidType.EXTRA_SID, default_attrs),
+                # We don't specify asserted identity or claims valid SIDs for
+                # the user...
+            },
+            'tgs:mach:sids': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # ...nor for the computer.
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                ('S-1-5-2-3-4', SidType.EXTRA_SID, default_attrs),
+                # They don't show up in the service ticket.
+            },
+        },
+        {
+            'test': 'removed special sids to service',
+            'tgs:user:sids': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                ('S-1-5-2-3-4', SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:mach:sids': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                ('S-1-5-2-3-4', SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # These special SIDs don't show up in the device info either.
+            },
+        },
+        # Test a group being the primary one for the user and machine.
+        {
+            'test': 'primary universal to krbtgt',
+            'groups': {
+                'primary-user': (GroupType.UNIVERSAL, {user}),
+                'primary-mach': (GroupType.UNIVERSAL, {mach}),
+            },
+            # Set these groups as the account's primary groups.
+            'primary_group': 'primary-user',
+            'mach:primary_group': 'primary-mach',
+            'as:expected': {
+                # They appear in the PAC as normal.
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                ('primary-mach', SidType.BASE_SID, default_attrs),
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            'test': 'primary universal to service compressed',
+            'groups': {
+                'primary-user': (GroupType.UNIVERSAL, {user}),
+                'primary-mach': (GroupType.UNIVERSAL, {mach}),
+            },
+            'primary_group': 'primary-user',
+            'mach:primary_group': 'primary-mach',
+            'as:expected': {
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                ('primary-mach', SidType.BASE_SID, default_attrs),
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': True,
+            'tgs:expected': {
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                ('primary-mach', SidType.BASE_SID, default_attrs),
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'primary universal to service uncompressed',
+            'groups': {
+                'primary-user': (GroupType.UNIVERSAL, {user}),
+                'primary-mach': (GroupType.UNIVERSAL, {mach}),
+            },
+            'primary_group': 'primary-user',
+            'mach:primary_group': 'primary-mach',
+            'as:expected': {
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                ('primary-mach', SidType.BASE_SID, default_attrs),
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            # SID compression is unsupported.
+            'tgs:compression': False,
+            'tgs:expected': {
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                ('primary-mach', SidType.BASE_SID, default_attrs),
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Test domain-local primary groups.
+        {
+            'test': 'primary domain-local to krbtgt',
+            'groups': {
+                'primary-user': (GroupType.DOMAIN_LOCAL, {user}),
+                'primary-mach': (GroupType.DOMAIN_LOCAL, {mach}),
+            },
+            # Though Windows normally disallows setting domain-locals group as
+            # primary groups, Samba does not.
+            'primary_group': 'primary-user',
+            'mach:primary_group': 'primary-mach',
+            'as:expected': {
+                # The domain-local groups appear as our primary GIDs, but do
+                # not appear in the base SIDs.
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            'test': 'primary domain-local to service compressed',
+            'groups': {
+                'primary-user': (GroupType.DOMAIN_LOCAL, {user}),
+                'primary-mach': (GroupType.DOMAIN_LOCAL, {mach}),
+            },
+            'primary_group': 'primary-user',
+            'mach:primary_group': 'primary-mach',
+            'as:expected': {
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': True,
+            'tgs:expected': {
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'primary domain-local to service uncompressed',
+            'groups': {
+                'primary-user': (GroupType.DOMAIN_LOCAL, {user}),
+                'primary-mach': (GroupType.DOMAIN_LOCAL, {mach}),
+            },
+            'primary_group': 'primary-user',
+            'mach:primary_group': 'primary-mach',
+            'as:expected': {
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'as:mach:expected': {
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            # SID compression is unsupported.
+            'tgs:compression': False,
+            'tgs:expected': {
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_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 to krbtgt',
+            'groups': {
+                # Domain-local groups to which the accounts belong.
+                'primary-user': (GroupType.DOMAIN_LOCAL, {user}),
+                'primary-mach': (GroupType.DOMAIN_LOCAL, {mach}),
+            },
+            'tgs:user:sids': {
+                # In the PACs, the groups have the attributes of an ordinary
+                # group...
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                # ...and remain our primary ones.
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:mach:sids': {
+                ('primary-mach', SidType.BASE_SID, default_attrs),
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                # The groups don't change.
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            'test': 'old primary domain-local to service compressed',
+            'groups': {
+                'primary-user': (GroupType.DOMAIN_LOCAL, {user}),
+                'primary-mach': (GroupType.DOMAIN_LOCAL, {mach}),
+            },
+            'tgs:user:sids': {
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:mach:sids': {
+                ('primary-mach', SidType.BASE_SID, default_attrs),
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': True,
+            'tgs:expected': {
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                ('primary-user', SidType.PRIMARY_GID, None),
+                # The groups are added a second time to the PAC, now as
+                # resource groups.
+                ('primary-user', SidType.RESOURCE_SID, resource_attrs),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                ('primary-mach', SidType.BASE_SID, default_attrs),
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                frozenset([('primary-mach', SidType.RESOURCE_SID, resource_attrs)]),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'old primary domain-local to service uncompressed',
+            'groups': {
+                'primary-user': (GroupType.DOMAIN_LOCAL, {user}),
+                'primary-mach': (GroupType.DOMAIN_LOCAL, {mach}),
+            },
+            'tgs:user:sids': {
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                ('primary-user', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:mach:sids': {
+                ('primary-mach', SidType.BASE_SID, default_attrs),
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            # SID compression is unsupported.
+            'tgs:compression': False,
+            'tgs:expected': {
+                ('primary-user', SidType.BASE_SID, default_attrs),
+                ('primary-user', SidType.PRIMARY_GID, None),
+                # This time, the group is added to Extra SIDs.
+                ('primary-user', SidType.EXTRA_SID, resource_attrs),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                ('primary-mach', SidType.BASE_SID, default_attrs),
+                ('primary-mach', SidType.PRIMARY_GID, None),
+                frozenset([('primary-mach', SidType.RESOURCE_SID, resource_attrs)]),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Test the scenario where each account possesses an old TGT issued when
+        # a now-domain-local group was still its primary one. The accounts no
+        # longer belong to those groups, which themselves belong to other
+        # domain-local groups.
+        {
+            'test': 'old primary domain-local transitive to krbtgt',
+            'groups': {
+                'user-outer': (GroupType.DOMAIN_LOCAL, {'user-inner'}),
+                'user-inner': (GroupType.DOMAIN_LOCAL, {}),
+                'mach-outer': (GroupType.DOMAIN_LOCAL, {'mach-inner'}),
+                'mach-inner': (GroupType.DOMAIN_LOCAL, {}),
+            },
+            'tgs:user:sids': {
+                # In the PACs, the groups have the attributes of an ordinary
+                # group...
+                ('user-inner', SidType.BASE_SID, default_attrs),
+                # ...and remain our primary ones.
+                ('user-inner', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:mach:sids': {
+                ('mach-inner', SidType.BASE_SID, default_attrs),
+                ('mach-inner', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': True,
+            'tgs:expected': {
+                # The groups don't change.
+                ('user-inner', SidType.BASE_SID, default_attrs),
+                ('user-inner', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+        {
+            'test': 'old primary domain-local transitive to service compressed',
+            'groups': {
+                'user-outer': (GroupType.DOMAIN_LOCAL, {'user-inner'}),
+                'user-inner': (GroupType.DOMAIN_LOCAL, {}),
+                'mach-outer': (GroupType.DOMAIN_LOCAL, {'mach-inner'}),
+                'mach-inner': (GroupType.DOMAIN_LOCAL, {}),
+            },
+            'tgs:user:sids': {
+                ('user-inner', SidType.BASE_SID, default_attrs),
+                ('user-inner', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:mach:sids': {
+                ('mach-inner', SidType.BASE_SID, default_attrs),
+                ('mach-inner', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:compression': True,
+            'tgs:expected': {
+                ('user-inner', SidType.BASE_SID, default_attrs),
+                ('user-inner', SidType.PRIMARY_GID, None),
+                # The second resource groups are added a second time to the PAC
+                # as resource groups.
+                ('user-outer', SidType.RESOURCE_SID, resource_attrs),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                ('mach-inner', SidType.BASE_SID, default_attrs),
+                ('mach-inner', SidType.PRIMARY_GID, None),
+                frozenset([('mach-outer', SidType.RESOURCE_SID, resource_attrs)]),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        {
+            'test': 'old primary domain-local transitive to service uncompressed',
+            'groups': {
+                'user-outer': (GroupType.DOMAIN_LOCAL, {'user-inner'}),
+                'user-inner': (GroupType.DOMAIN_LOCAL, {}),
+                'mach-outer': (GroupType.DOMAIN_LOCAL, {'mach-inner'}),
+                'mach-inner': (GroupType.DOMAIN_LOCAL, {}),
+            },
+            'tgs:user:sids': {
+                ('user-inner', SidType.BASE_SID, default_attrs),
+                ('user-inner', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:mach:sids': {
+                ('mach-inner', SidType.BASE_SID, default_attrs),
+                ('mach-inner', SidType.PRIMARY_GID, None),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            # SID compression is unsupported.
+            'tgs:compression': False,
+            'tgs:expected': {
+                ('user-inner', SidType.BASE_SID, default_attrs),
+                ('user-inner', SidType.PRIMARY_GID, None),
+                # This time, the group is added to Extra SIDs.
+                ('user-outer', SidType.EXTRA_SID, resource_attrs),
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                ('mach-inner', SidType.BASE_SID, default_attrs),
+                ('mach-inner', SidType.PRIMARY_GID, None),
+                frozenset([('mach-outer', SidType.RESOURCE_SID, resource_attrs)]),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs)]),
+            },
+        },
+        # Test how the various categories of SIDs are propagated into the
+        # device info structure.
+        {
+            'test': 'device info sid grouping',
+            'tgs:mach:sids': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # These base SIDs are simply propagated into the device info,
+                # irrespective of whatever attributes they have.
+                (1, SidType.BASE_SID, default_attrs),
+                (2, SidType.BASE_SID, 12345),
+                # Extra SIDs not from a domain are also propagated.
+                ('S-1-5-2-3-4', SidType.EXTRA_SID, 789),
+                ('S-1-5-20', SidType.EXTRA_SID, 999),
+                ('S-1-5-21', SidType.EXTRA_SID, 999),
+                ('S-1-6-0', SidType.EXTRA_SID, 999),
+                ('S-1-6-2-3-4', SidType.EXTRA_SID, 789),
+                # Extra SIDs from our own domain are collated into a group.
+                (3, SidType.EXTRA_SID, default_attrs),
+                (4, SidType.EXTRA_SID, 12345),
+                # Extra SIDs from other domains are collated into separate groups.
+                ('S-1-5-21-0-0-0-490', SidType.EXTRA_SID, 5),
+                ('S-1-5-21-0-0-0-491', SidType.EXTRA_SID, 6),
+                ('S-1-5-21-0-0-1-492', SidType.EXTRA_SID, 7),
+                ('S-1-5-21-0-0-1-493', SidType.EXTRA_SID, 8),
+                ('S-1-5-21-0-0-1-494', SidType.EXTRA_SID, 9),
+                # A non-domain SID (too few subauths), ...
+                ('S-1-5-21-242424-12345-2', SidType.EXTRA_SID, 1111111111),
+                # ... a domain SID, ...
+                ('S-1-5-21-242424-12345-321321-2', SidType.EXTRA_SID, 1111111111),
+                # ... and a non-domain SID (too many subauths).
+                ('S-1-5-21-242424-12345-321321-654321-2', SidType.EXTRA_SID, default_attrs),
+                # Special SIDs.
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:to_krbtgt': False,
+            'tgs:expected': {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+                (compounded_auth, SidType.EXTRA_SID, default_attrs),
+                (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, default_attrs),
+            },
+            'tgs:device:expected': {
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.BASE_SID, default_attrs),
+                (security.DOMAIN_RID_DOMAIN_MEMBERS, SidType.PRIMARY_GID, None),
+                # Base SIDs.
+                (1, SidType.BASE_SID, default_attrs),
+                (2, SidType.BASE_SID, 12345),
+                # Extra SIDs from other domains.
+                ('S-1-5-2-3-4', SidType.EXTRA_SID, 789),
+                ('S-1-5-20', SidType.EXTRA_SID, 999),
+                ('S-1-5-21', SidType.EXTRA_SID, 999),
+                ('S-1-6-0', SidType.EXTRA_SID, 999),
+                ('S-1-6-2-3-4', SidType.EXTRA_SID, 789),
+                # Extra SIDs from our own domain.
+                frozenset({
+                    (3, SidType.RESOURCE_SID, default_attrs),
+                    (4, SidType.RESOURCE_SID, 12345),
+                }),
+                # Extra SIDs from other domains.
+                frozenset({
+                    ('S-1-5-21-0-0-0-490', SidType.RESOURCE_SID, 5),
+                    ('S-1-5-21-0-0-0-491', SidType.RESOURCE_SID, 6),
+                    # These SIDs end up placed with the CLAIMS_VALID SID.
+                    (security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, default_attrs),
+                }),
+                frozenset({
+                    ('S-1-5-21-0-0-1-492', SidType.RESOURCE_SID, 7),
+                    ('S-1-5-21-0-0-1-493', SidType.RESOURCE_SID, 8),
+                    ('S-1-5-21-0-0-1-494', SidType.RESOURCE_SID, 9),
+                }),
+                # Non-domain SID.
+                ('S-1-5-21-242424-12345-2', SidType.EXTRA_SID, 1111111111),
+                # Domain SID.
+                frozenset({
+                    ('S-1-5-21-242424-12345-321321-2', SidType.RESOURCE_SID, 1111111111),
+                }),
+                # Non-domain SID.
+                ('S-1-5-21-242424-12345-321321-654321-2', SidType.EXTRA_SID, default_attrs),
+                # Special SIDs.
+                (asserted_identity, SidType.EXTRA_SID, default_attrs),
+            },
+        },
+    ]
+
+    @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_device_info', name,
+                                      dict(case))
+
+    def _test_device_info_with_args(self, case):
+        # The group arrangement for the test.
+        group_setup = case.pop('groups', None)
+
+        # Groups that should be the primary group for the user and machine
+        # respectively.
+        primary_group = case.pop('primary_group', None)
+        mach_primary_group = case.pop('mach:primary_group', None)
+
+        # Whether the TGS-REQ should be directed to the krbtgt.
+        tgs_to_krbtgt = case.pop('tgs:to_krbtgt', None)
+
+        # Whether the target server of the TGS-REQ should support compound
+        # identity or resource SID compression.
+        tgs_compound_id = case.pop('tgs:compound_id', None)
+        tgs_compression = case.pop('tgs:compression', None)
+
+        # Optional SIDs to replace those in the PACs prior to a TGS-REQ.
+        tgs_user_sids = case.pop('tgs:user:sids', None)
+        tgs_mach_sids = case.pop('tgs:mach:sids', None)
+
+        # Optional groups which the machine is added to or removed from prior
+        # to a TGS-REQ , to test how the groups in the device PAC are expanded.
+        tgs_mach_added = case.pop('tgs:mach:added', None)
+        tgs_mach_removed = case.pop('tgs:mach:removed', None)
+
+        # Optional account SIDs to replace those in the PACs prior to a
+        # TGS-REQ.
+        tgs_user_sid = case.pop('tgs:user_sid', None)
+        tgs_mach_sid = case.pop('tgs:mach_sid', None)
+
+        # User flags that may be set or reset in the PAC prior to a TGS-REQ.
+        tgs_mach_set_user_flags = case.pop('tgs:mach:set_user_flags', None)
+        tgs_mach_reset_user_flags = case.pop('tgs:mach: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)
+        as_mach_expected = case.pop('as:mach:expected', None)
+        tgs_expected = case.pop('tgs:expected', None)
+        tgs_device_expected = case.pop('tgs:device: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_mach_expected is None:
+            self.assertIsNotNone(tgs_expected,
+                                 'no set of expected machine SIDs is provided')
+
+        if tgs_to_krbtgt is None:
+            tgs_to_krbtgt = False
+
+        if tgs_compound_id is None and not tgs_to_krbtgt:
+            # Assume the service supports compound identity by default.
+            tgs_compound_id = True
+
+        if tgs_to_krbtgt:
+            self.assertIsNone(tgs_device_expected,
+                              'device SIDs are not added for a krbtgt request')
+
+        self.assertIsNotNone(tgs_expected,
+                             'no set of expected TGS SIDs is provided')
+
+        if tgs_user_sid is not None:
+            self.assertIsNotNone(tgs_user_sids,
+                                 'specified TGS-REQ user SID, but no '
+                                 'accompanying user SIDs provided')
+
+        if tgs_mach_sid is not None:
+            self.assertIsNotNone(tgs_mach_sids,
+                                 'specified TGS-REQ mach SID, but no '
+                                 'accompanying machine SIDs provided')
+
+        if tgs_mach_set_user_flags is None:
+            tgs_mach_set_user_flags = 0
+        else:
+            self.assertIsNotNone(tgs_mach_sids,
+                                 'specified TGS-REQ set user flags, but no '
+                                 'accompanying machine SIDs provided')
+
+        if tgs_mach_reset_user_flags is None:
+            tgs_mach_reset_user_flags = 0
+        else:
+            self.assertIsNotNone(tgs_mach_sids,
+                                 'specified TGS-REQ reset user flags, but no '
+                                 'accompanying machine SIDs provided')
+
+        user_use_cache = not group_setup and (
+            not primary_group)
+        mach_use_cache = not group_setup and (
+            not mach_primary_group) and (
+            not tgs_mach_added) and (
+                not tgs_mach_removed)
+
+        samdb = self.get_samdb()
+
+        domain_sid = samdb.get_domain_sid()
+
+        # Create the user account. It needs to be freshly created rather than
+        # cached if there is a possibility of adding it to one or more groups.
+        user_creds = self.get_cached_creds(
+            account_type=self.AccountType.USER,
+            use_cache=user_use_cache)
+        user_dn = user_creds.get_dn()
+        user_sid = self.get_objectSid(samdb, user_dn)
+        user_name = user_creds.get_username()
+
+        trust_user_rid = random.randint(2000, 0xfffffffe)
+        trust_user_sid = f'{self.user_trust_domain}-{trust_user_rid}'
+
+        trust_mach_rid = random.randint(2000, 0xfffffffe)
+        trust_mach_sid = f'{self.mach_trust_domain}-{trust_mach_rid}'
+
+        # Create the machine account. It needs to be freshly created rather
+        # than cached if there is a possibility of adding it to one or more
+        # groups.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            use_cache=mach_use_cache)
+        mach_dn = mach_creds.get_dn()
+        mach_dn_str = str(mach_dn)
+        mach_sid = self.get_objectSid(samdb, mach_dn)
+
+        user_principal = Principal(user_dn, user_sid)
+        mach_principal = Principal(mach_dn, mach_sid)
+        trust_user_principal = Principal(None, trust_user_sid)
+        trust_mach_principal = Principal(None, trust_mach_sid)
+        preexisting_groups = {
+            self.user: user_principal,
+            self.mach: mach_principal,
+            self.trust_user: trust_user_principal,
+            self.trust_mach: trust_mach_principal,
+        }
+        primary_groups = {}
+        if primary_group is not None:
+            primary_groups[user_principal] = primary_group
+        if mach_primary_group is not None:
+            primary_groups[mach_principal] = mach_primary_group
+        groups = self.setup_groups(samdb,
+                                   preexisting_groups,
+                                   group_setup,
+                                   primary_groups)
+        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_user_domain_sid, tgs_user_rid = tgs_user_sid.rsplit('-', 1)
+
+        if tgs_mach_sid is None:
+            tgs_mach_sid = mach_sid
+        elif tgs_mach_sid in groups:
+            tgs_mach_sid = groups[tgs_mach_sid].sid
+
+        tgs_mach_domain_sid, tgs_mach_rid = tgs_mach_sid.rsplit('-', 1)
+
+        expected_groups = self.map_sids(as_expected, groups,
+                                        domain_sid)
+        mach_expected_groups = self.map_sids(as_mach_expected, groups,
+                                             domain_sid)
+        tgs_user_sids_mapped = self.map_sids(tgs_user_sids, groups,
+                                             tgs_user_domain_sid)
+        tgs_mach_sids_mapped = self.map_sids(tgs_mach_sids, groups,
+                                             tgs_mach_domain_sid)
+        tgs_expected_mapped = self.map_sids(tgs_expected, groups,
+                                            tgs_user_domain_sid)
+        tgs_device_expected_mapped = self.map_sids(tgs_device_expected, groups,
+                                                   tgs_mach_domain_sid)
+
+        user_tgt = self.get_tgt(user_creds,
+                                expected_groups=expected_groups,
+                                unexpected_groups=None)
+
+        mach_tgt = self.get_tgt(mach_creds,
+                                expected_groups=mach_expected_groups,
+                                unexpected_groups=None)
+
+        if tgs_user_sids is not None:
+            # Replace the SIDs in the user's PAC with the ones provided by the
+            # test.
+            user_tgt = self.ticket_with_sids(user_tgt,
+                                             tgs_user_sids_mapped,
+                                             tgs_user_domain_sid,
+                                             tgs_user_rid)
+
+        if tgs_mach_sids is not None:
+            # Replace the SIDs in the machine's PAC with the ones provided by
+            # the test.
+            mach_tgt = self.ticket_with_sids(mach_tgt,
+                                             tgs_mach_sids_mapped,
+                                             tgs_mach_domain_sid,
+                                             tgs_mach_rid,
+                                             set_user_flags=tgs_mach_set_user_flags,
+                                             reset_user_flags=tgs_mach_reset_user_flags)
+
+        if tgs_mach_removed is not None:
+            for removed in tgs_mach_removed:
+                group_dn = self.map_to_dn(removed, groups, domain_sid=None)
+                self.remove_from_group(mach_dn, group_dn)
+
+        if tgs_mach_added is not None:
+            for added in tgs_mach_added:
+                group_dn = self.map_to_dn(added, groups, domain_sid=None)
+                self.add_to_group(mach_dn_str, group_dn, 'member',
+                                  expect_attr=False)
+
+        subkey = self.RandomKey(user_tgt.session_key.etype)
+
+        armor_subkey = self.RandomKey(subkey.etype)
+        explicit_armor_key = self.generate_armor_key(armor_subkey,
+                                                     mach_tgt.session_key)
+        armor_key = kcrypto.cf2(explicit_armor_key.key,
+                                subkey.key,
+                                b'explicitarmor',
+                                b'tgsarmor')
+        armor_key = Krb5EncryptionKey(armor_key, None)
+
+        target_creds, sname = self.get_target(
+            to_krbtgt=tgs_to_krbtgt,
+            compound_id=tgs_compound_id,
+            compression=tgs_compression)
+        srealm = target_creds.get_realm()
+
+        decryption_key = self.TicketDecryptionKey_from_creds(
+            target_creds)
+
+        target_supported_etypes = target_creds.tgs_supported_enctypes
+
+        etypes = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
+
+        kdc_options = '0'
+        pac_options = '1'  # claims support
+
+        requester_sid = None
+        if tgs_to_krbtgt:
+            requester_sid = user_sid
+
+        expect_resource_groups_flag = None
+        if tgs_mach_reset_user_flags & netlogon.NETLOGON_RESOURCE_GROUPS:
+            expect_resource_groups_flag = False
+        elif tgs_mach_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=user_tgt.crealm,
+            expected_cname=user_tgt.cname,
+            expected_srealm=srealm,
+            expected_sname=sname,
+            expected_account_name=user_name,
+            ticket_decryption_key=decryption_key,
+            generate_fast_fn=self.generate_simple_fast,
+            generate_fast_armor_fn=self.generate_ap_req,
+            check_rep_fn=self.generic_check_kdc_rep,
+            check_kdc_private_fn=self.generic_check_kdc_private,
+            tgt=user_tgt,
+            armor_key=armor_key,
+            armor_tgt=mach_tgt,
+            armor_subkey=armor_subkey,
+            pac_options=pac_options,
+            authenticator_subkey=subkey,
+            kdc_options=kdc_options,
+            expect_pac=True,
+            expect_pac_attrs=tgs_to_krbtgt,
+            expect_pac_attrs_pac_request=tgs_to_krbtgt,
+            expected_sid=tgs_user_sid,
+            expected_requester_sid=requester_sid,
+            expected_domain_sid=tgs_user_domain_sid,
+            expected_device_domain_sid=tgs_mach_domain_sid,
+            expected_supported_etypes=target_supported_etypes,
+            expect_resource_groups_flag=expect_resource_groups_flag,
+            expected_groups=tgs_expected_mapped,
+            unexpected_groups=None,
+            expect_device_claims=None,
+            expect_device_info=not tgs_to_krbtgt,
+            expected_device_groups=tgs_device_expected_mapped)
+
+        rep = self._generic_kdc_exchange(kdc_exchange_dict,
+                                         cname=None,
+                                         realm=srealm,
+                                         sname=sname,
+                                         etypes=etypes)
+        self.check_reply(rep, KRB_TGS_REP)
+
+
+if __name__ == '__main__':
+    global_asn1_print = False
+    global_hexdump = False
+    import unittest
+    unittest.main()
index 27c2810e80064d809d9b00162afe901a17daddc8..229f8336ad83866bd9d3138f4b66192f3dbeba3b 100644 (file)
@@ -118,6 +118,7 @@ EXCLUDE_USAGE = {
     'python/samba/tests/krb5/group_tests.py',
     'lib/compression/tests/scripts/three-byte-hash',
     'python/samba/tests/krb5/etype_tests.py',
+    'python/samba/tests/krb5/device_tests.py',
 }
 
 EXCLUDE_HELP = {
index 45589036603948638ae5ebf509b2e191f945a64b..1248614ac80e6f44504e4e9b378fbe2b52bd9972 100644 (file)
 ^samba.tests.krb5.group_tests.samba.tests.krb5.group_tests.GroupTests.test_group_nested_group_addition_tgs_req_to_krbtgt.ad_dc
 ^samba.tests.krb5.group_tests.samba.tests.krb5.group_tests.GroupTests.test_group_user_group_addition_tgs_req_to_krbtgt.ad_dc
 ^samba.tests.krb5.group_tests.samba.tests.krb5.group_tests.GroupTests.test_group_user_group_addition_tgs_req_to_service.ad_dc
+#
+# Device info tests
+#
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_add_transitive_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_add_transitive_domain_local_groups_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_base_sid_resource_attrs_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_base_sid_resource_attrs_to_service.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_base_sid_to_service.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_both_from_trust_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_both_from_trust_domain_local_groups_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_device_info_sid_grouping.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_device_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_device_to_service_no_compound_id.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_device_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_domain_local_groups_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_extra_sids_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_extra_sids_to_service.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_from_trust_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_from_trust_domain_local_groups_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_mach_resource_sids.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_old_primary_domain_local_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_old_primary_domain_local_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_old_primary_domain_local_transitive_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_old_primary_domain_local_transitive_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_primary_domain_local_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_primary_domain_local_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_primary_universal_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_primary_universal_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_remove_transitive_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_remove_transitive_domain_local_groups_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_removed_special_sids_to_service.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_universal_groups_to_service.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_user_from_trust_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_user_from_trust_domain_local_groups_to_service_uncompressed.ad_dc
index 5a40cbbe025a9654ebaa07c6db865d5d94fcb38c..243ff608c24fe12b3fdc76fd334df4ab92b9151f 100644 (file)
@@ -2142,3 +2142,51 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
 ^samba.tests.krb5.group_tests.samba.tests.krb5.group_tests.GroupTests.test_group_nested_group_addition_tgs_req_to_krbtgt.ad_dc
 ^samba.tests.krb5.group_tests.samba.tests.krb5.group_tests.GroupTests.test_group_user_group_addition_tgs_req_to_krbtgt.ad_dc
 ^samba.tests.krb5.group_tests.samba.tests.krb5.group_tests.GroupTests.test_group_user_group_addition_tgs_req_to_service.ad_dc
+#
+# Device info tests
+#
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_add_transitive_domain_local_groups_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_add_transitive_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_add_transitive_domain_local_groups_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_base_sid_resource_attrs_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_base_sid_resource_attrs_to_service.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_base_sid_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_base_sid_to_service.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_basic_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_both_from_trust_domain_local_groups_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_both_from_trust_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_both_from_trust_domain_local_groups_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_device_info_sid_grouping.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_device_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_device_to_service_no_compound_id.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_device_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_domain_local_groups_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_domain_local_groups_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_extra_sids_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_extra_sids_to_service.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_from_trust_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_from_trust_domain_local_groups_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_mach_resource_sids.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_old_primary_domain_local_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_old_primary_domain_local_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_old_primary_domain_local_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_old_primary_domain_local_transitive_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_old_primary_domain_local_transitive_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_old_primary_domain_local_transitive_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_primary_domain_local_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_primary_domain_local_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_primary_domain_local_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_primary_universal_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_primary_universal_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_primary_universal_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_remove_transitive_domain_local_groups_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_remove_transitive_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_remove_transitive_domain_local_groups_to_service_uncompressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_removed_special_sids_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_removed_special_sids_to_service.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_universal_groups_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_universal_groups_to_service.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_user_from_trust_domain_local_groups_to_krbtgt.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_user_from_trust_domain_local_groups_to_service_compressed.ad_dc
+^samba.tests.krb5.device_tests.samba.tests.krb5.device_tests.DeviceTests.test_device_info_user_from_trust_domain_local_groups_to_service_uncompressed.ad_dc
index a325eb528b049ce659df1f5f29729b1656dbc10d..8924e22ee1da5ab9008ba66823ac99078454d854 100755 (executable)
@@ -1778,6 +1778,10 @@ planoldpythontestsuite(
     'ad_dc',
     'samba.tests.krb5.claims_tests',
     environ=krb5_environ)
+planoldpythontestsuite(
+    'ad_dc',
+    'samba.tests.krb5.device_tests',
+    environ=krb5_environ)
 planoldpythontestsuite(
     'ad_dc:local',
     'samba.tests.krb5.lockout_tests',