tests/krb5: Add samba.tests.krb5.conditional_ace_tests
authorJoseph Sutton <josephsutton@catalyst.net.nz>
Thu, 28 Sep 2023 01:10:16 +0000 (14:10 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 28 Sep 2023 04:35:05 +0000 (04:35 +0000)
This is a test using conditional ACEs and claims to confirm that we understand
the full end-to-end network behaviour of these all the way from the PAC to the
application in the access check of the KDC.

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Pair-programmed-by: Andrew Bartlett <abartlet@samba.org>
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Thu Sep 28 04:35:05 UTC 2023 on atb-devel-224

python/samba/tests/krb5/conditional_ace_tests.py [new file with mode: 0755]
python/samba/tests/krb5/kdc_tgs_tests.py
selftest/knownfail_heimdal_kdc
selftest/knownfail_mit_kdc
source4/selftest/tests.py

diff --git a/python/samba/tests/krb5/conditional_ace_tests.py b/python/samba/tests/krb5/conditional_ace_tests.py
new file mode 100755 (executable)
index 0000000..0b351ae
--- /dev/null
@@ -0,0 +1,3731 @@
+#!/usr/bin/env python3
+# Unix SMB/CIFS implementation.
+# Copyright (C) Stefan Metzmacher 2020
+# Copyright (C) Catalyst.Net Ltd 2023
+#
+# 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'
+
+from collections import OrderedDict
+from functools import partial
+import re
+from string import Formatter
+
+import ldb
+
+from samba import dsdb, ntstatus
+from samba.dcerpc import claims, krb5pac, security
+from samba.ndr import ndr_pack, ndr_unpack
+
+from samba.tests import DynamicTestCase, env_get_var_value
+from samba.tests.krb5.authn_policy_tests import (
+    AuditEvent,
+    AuditReason,
+    AuthnPolicyBaseTests,
+)
+from samba.tests.krb5.raw_testcase import RawKerberosTest
+from samba.tests.krb5.rfc4120_constants import (
+    KDC_ERR_BADOPTION,
+    KDC_ERR_GENERIC,
+    KDC_ERR_MODIFIED,
+    KDC_ERR_POLICY,
+    NT_PRINCIPAL,
+)
+import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1
+
+SidType = RawKerberosTest.SidType
+
+global_asn1_print = False
+global_hexdump = False
+
+
+# When used as a test outcome, indicates that the test can cause a Windows
+# server to crash, and is to be run with caution.
+CRASHES_WINDOWS = object()
+
+
+class ConditionalAceBaseTests(AuthnPolicyBaseTests):
+    # Constants for group SID attributes.
+    default_attrs = security.SE_GROUP_DEFAULT_FLAGS
+    resource_attrs = default_attrs | security.SE_GROUP_RESOURCE
+
+    aa_asserted_identity = (
+        security.SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY)
+    service_asserted_identity = security.SID_SERVICE_ASSERTED_IDENTITY
+
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+
+        cls._setup = False
+
+    def setUp(self):
+        super().setUp()
+        self.do_asn1_print = global_asn1_print
+        self.do_hexdump = global_hexdump
+
+        if not self._setup:
+            samdb = self.get_samdb()
+            cls = type(self)
+
+            # Create a machine account with which to perform FAST.
+            cls._mach_creds = self.get_cached_creds(
+                account_type=self.AccountType.COMPUTER)
+
+            # Create some new groups.
+
+            group0_name = self.get_new_username()
+            group0_dn = self.create_group(samdb, group0_name)
+            cls._group0_sid = self.get_objectSid(samdb, group0_dn)
+
+            group1_name = self.get_new_username()
+            group1_dn = self.create_group(samdb, group1_name)
+            cls._group1_sid = self.get_objectSid(samdb, group1_dn)
+
+            # Create machine accounts with which to perform FAST that belong to
+            # various arrangements of the groups.
+
+            cls._member_of_both_creds = self.get_cached_creds(
+                account_type=self.AccountType.COMPUTER,
+                opts={'member_of': (group0_dn, group1_dn)})
+
+            cls._member_of_one_creds = self.get_cached_creds(
+                account_type=self.AccountType.COMPUTER,
+                opts={'member_of': (group1_dn,)})
+
+            # Create some authentication silos.
+            cls._unenforced_silo = self.create_authn_silo(enforced=False)
+            cls._enforced_silo = self.create_authn_silo(enforced=True)
+
+            # Create machine accounts with which to perform FAST that belong to
+            # the respective silos.
+
+            cls._member_of_unenforced_silo = self._get_creds(
+                account_type=self.AccountType.COMPUTER,
+                assigned_silo=self._unenforced_silo,
+                cached=True)
+            self.add_to_group(str(self._member_of_unenforced_silo.get_dn()),
+                              self._unenforced_silo.dn,
+                              'msDS-AuthNPolicySiloMembers',
+                              expect_attr=False)
+
+            cls._member_of_enforced_silo = self._get_creds(
+                account_type=self.AccountType.COMPUTER,
+                assigned_silo=self._enforced_silo,
+                cached=True)
+            self.add_to_group(str(self._member_of_enforced_silo.get_dn()),
+                              self._enforced_silo.dn,
+                              'msDS-AuthNPolicySiloMembers',
+                              expect_attr=False)
+
+            # Create a couple of multi‐valued string claims for testing claim
+            # value comparisons.
+
+            cls.claim0_attr = 'carLicense'
+            cls.claim0_id = self.get_new_username()
+            self.create_claim(cls.claim0_id,
+                              enabled=True,
+                              attribute=cls.claim0_attr,
+                              single_valued=False,
+                              source_type='AD',
+                              for_classes=['computer', 'user'],
+                              value_type=claims.CLAIM_TYPE_STRING)
+
+            cls.claim1_attr = 'departmentNumber'
+            cls.claim1_id = self.get_new_username()
+            self.create_claim(cls.claim1_id,
+                              enabled=True,
+                              attribute=cls.claim1_attr,
+                              single_valued=False,
+                              source_type='AD',
+                              for_classes=['computer', 'user'],
+                              value_type=claims.CLAIM_TYPE_STRING)
+
+            cls._setup = True
+
+    # For debugging purposes. Prints out the SDDL representation of
+    # authentication policy conditions set by the Windows GUI.
+    def _print_authn_policy_sddl(self, policy_id):
+        policy_dn = self.get_authn_policies_dn()
+        policy_dn.add_child(f'CN={policy_id}')
+
+        attrs = [
+            'msDS-ComputerAllowedToAuthenticateTo',
+            'msDS-ServiceAllowedToAuthenticateFrom',
+            'msDS-ServiceAllowedToAuthenticateTo',
+            'msDS-UserAllowedToAuthenticateFrom',
+            'msDS-UserAllowedToAuthenticateTo',
+        ]
+
+        samdb = self.get_samdb()
+        res = samdb.search(policy_dn, scope=ldb.SCOPE_BASE, attrs=attrs)
+        self.assertEqual(1, len(res),
+                         f'Authentication policy {policy_id} not found')
+        result = res[0]
+
+        def print_sddl(attr):
+            sd = result.get(attr, idx=0)
+            if sd is None:
+                return
+
+            sec_desc = ndr_unpack(security.descriptor, sd)
+            print(f'{attr}: {sec_desc.as_sddl()}')
+
+        for attr in attrs:
+            print_sddl(attr)
+
+    def sddl_array_from_sids(self, sids):
+        def sddl_from_sid_entry(sid_entry):
+            sid, _, _ = sid_entry
+            return f'SID({sid})'
+
+        return f"{{{', '.join(map(sddl_from_sid_entry, sids))}}}"
+
+    def allow_if(self, condition):
+        return f'O:SYD:(XA;;CR;;;WD;({condition}))'
+
+    @staticmethod
+    def escaped_claim_id(claim_id):
+        escapes = '\x00\t\n\x0b\x0c\r !"%&()<=>|'
+        return ''.join(c
+                       if c not in escapes
+                       else f'%{ord(c):04x}'
+                       for c in claim_id)
+
+
+@DynamicTestCase
+class ConditionalAceTests(ConditionalAceBaseTests):
+    @classmethod
+    def setUpDynamicTestCases(cls):
+        FILTER = env_get_var_value('FILTER', allow_missing=True)
+
+        # These operators are arranged so that each operator precedes its own
+        # affixes.
+        op_names = OrderedDict([
+            ('!=', 'does not equal'),
+            ('!', 'not'),
+            ('&&', 'and'),
+            ('<=', 'is less than or equals'),
+            ('<', 'is less than'),
+            ('==', 'equals'),
+            ('>=', 'exceeds or equals'),
+            ('>', 'exceeds'),
+            ('Not_Any_of', 'matches none of'),
+            ('Any_of', 'matches any of'),
+            ('Not_Contains', 'does not contain'),
+            ('Contains', 'contains'),
+            ('Not_Member_of_Any', 'the user belongs to none of'),
+            ('Not_Device_Member_of_Any', 'the device belongs to none of'),  # TODO: no test for this yet
+            ('Device_Member_of_Any', 'the device belongs to any of'),  # TODO: no test for this yet
+            ('Not_Device_Member_of', 'the device does not belong to'),  # TODO: no test for this yet
+            ('Device_Member_of', 'the device belongs to'),
+            ('Not_Exists', 'there does not exist'),
+            ('Exists', 'there exists'),
+            ('Member_of_Any', 'the user belongs to any of'),
+            ('Not_Member_of', 'the user does not belong to'),
+            ('Member_of', 'the user belongs to'),
+            ('||', 'or'),
+        ])
+
+        # This is a safety measure to ensure correct ordering of op_names
+        keys = list(op_names.keys())
+        for i in range(len(keys)):
+            for j in range(i + 1, len(keys)):
+                if keys[i] in keys[j]:
+                    raise AssertionError((keys[i], keys[j]))
+
+        for case in cls.pac_claim_cases:
+            if len(case) == 3:
+                pac_claims, expression, outcome = case
+                claim_map = None
+            elif len(case) == 4:
+                pac_claims, expression, claim_map, outcome = case
+            else:
+                raise AssertionError(
+                    f'found {len(case)} items in case, expected 3–4')
+
+            expression_name = expression
+            for op, op_name in op_names.items():
+                expression_name = expression_name.replace(op, op_name)
+
+            name = f'{pac_claims}_{expression_name}'
+
+            if claim_map is not None:
+                name += f'_{claim_map}'
+
+            name = re.sub(r'\W+', '_', name)
+            if len(name) > 150:
+                name = f'{name[:125]}+{len(name) - 125}‐more'
+
+            if FILTER and not re.search(FILTER, name):
+                continue
+
+            cls.generate_dynamic_test('test_pac_claim_cmp', name,
+                                      pac_claims, expression, claim_map,
+                                      outcome)
+
+        for case in cls.claim_against_claim_cases:
+            lhs, op, rhs, outcome = case
+            op_name = op_names[op]
+
+            name = f'{lhs}_{op_name}_{rhs}'
+
+            name = re.sub(r'\W+', '_', name)
+            if FILTER and not re.search(FILTER, name):
+                continue
+
+            cls.generate_dynamic_test('test_cmp', name,
+                                      lhs, op, rhs, outcome)
+
+        for case in cls.claim_against_literal_cases:
+            lhs, op, rhs, outcome = case
+            op_name = op_names[op]
+
+            name = f'{lhs}_{op_name}_literal_{rhs}'
+
+            name = re.sub(r'\W+', '_', name)
+            if FILTER and not re.search(FILTER, name):
+                continue
+
+            cls.generate_dynamic_test('test_cmp', name,
+                                      lhs, op, rhs, outcome, True)
+
+    def test_allowed_from_member_of_each(self):
+        # Create an authentication policy that allows accounts belonging to
+        # both groups.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XA;;CR;;;WD;(Member_of '
+                f'{{SID({self._group0_sid}), SID({self._group1_sid})}}))'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that we get a policy error if the machine account does not
+        # belong to both groups.
+        armor_tgt = self.get_tgt(self._member_of_one_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        # Otherwise, authentication should succeed.
+        armor_tgt = self.get_tgt(self._member_of_both_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_member_of_any(self):
+        # Create an authentication policy that allows accounts belonging to
+        # either group.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XA;;CR;;;WD;(Member_of_Any '
+                f'{{SID({self._group0_sid}), SID({self._group1_sid})}}))'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that we get a policy error if the machine account belongs to
+        # neither group.
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        # Otherwise, authentication should succeed.
+        armor_tgt = self.get_tgt(self._member_of_one_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_not_member_of_each(self):
+        # Create an authentication policy that allows accounts not belonging to
+        # both groups.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XA;;CR;;;WD;(Not_Member_of '
+                f'{{SID({self._group0_sid}), SID({self._group1_sid})}}))'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that we get a policy error if the machine account belongs to
+        # both groups.
+        armor_tgt = self.get_tgt(self._member_of_both_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        # Otherwise, authentication should succeed.
+        armor_tgt = self.get_tgt(self._member_of_one_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_not_member_of_any(self):
+        # Create an authentication policy that allows accounts belonging to
+        # neither group.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XA;;CR;;;WD;(Not_Member_of_Any '
+                f'{{SID({self._group0_sid}), SID({self._group1_sid})}}))'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that we get a policy error if the machine account belongs to one
+        # of the groups.
+        armor_tgt = self.get_tgt(self._member_of_one_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        # Otherwise, authentication should succeed.
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_member_of_each_deny(self):
+        # Create an authentication policy that denies accounts belonging to
+        # both groups, and allows other accounts.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XD;;CR;;;WD;(Member_of '
+                f'{{SID({self._group0_sid}), SID({self._group1_sid})}}))'
+                f'(A;;CR;;;WD)'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that we get a policy error if the machine account belongs to
+        # both groups.
+        armor_tgt = self.get_tgt(self._member_of_both_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        # Otherwise, authentication should succeed.
+        armor_tgt = self.get_tgt(self._member_of_one_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_member_of_any_deny(self):
+        # Create an authentication policy that denies accounts belonging to
+        # either group, and allows other accounts.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XD;;CR;;;WD;(Member_of_Any '
+                f'{{SID({self._group0_sid}), SID({self._group1_sid})}}))'
+                f'(A;;CR;;;WD)'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that we get a policy error if the machine account belongs to
+        # either group.
+        armor_tgt = self.get_tgt(self._member_of_one_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        # Otherwise, authentication should succeed.
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_not_member_of_each_deny(self):
+        # Create an authentication policy that denies accounts not belonging to
+        # both groups, and allows other accounts.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XD;;CR;;;WD;(Not_Member_of '
+                f'{{SID({self._group0_sid}), SID({self._group1_sid})}}))'
+                f'(A;;CR;;;WD)'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that we get a policy error if the machine account doesn’t belong
+        # to both groups.
+        armor_tgt = self.get_tgt(self._member_of_one_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        # Otherwise, authentication should succeed.
+        armor_tgt = self.get_tgt(self._member_of_both_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_not_member_of_any_deny(self):
+        # Create an authentication policy that denies accounts belonging to
+        # neither group, and allows other accounts.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XD;;CR;;;WD;(Not_Member_of_Any '
+                f'{{SID({self._group0_sid}), SID({self._group1_sid})}}))'
+                f'(A;;CR;;;WD)'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that we get a policy error if the machine account belongs to
+        # neither group.
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        # Otherwise, authentication should succeed.
+        armor_tgt = self.get_tgt(self._member_of_one_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_unenforced_silo_equals(self):
+        # Create an authentication policy that allows accounts belonging to the
+        # unenforced silo.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XA;;CR;;;WD;'
+                f'(@User.ad://ext/AuthenticationSilo == '
+                f'"{self._unenforced_silo}"))'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # As the policy is unenforced, the ‘ad://ext/AuthenticationSilo’ claim
+        # will not be present in the TGT, and the ACE will never allow access.
+
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_unenforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_enforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+    def test_allowed_from_enforced_silo_equals(self):
+        # Create an authentication policy that allows accounts belonging to the
+        # enforced silo.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XA;;CR;;;WD;'
+                f'(@User.ad://ext/AuthenticationSilo == '
+                f'"{self._enforced_silo}"))'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that we get a policy error if the machine account does not
+        # belong to the silo.
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_unenforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        # Otherwise, authentication should succeed.
+        armor_tgt = self.get_tgt(self._member_of_enforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_unenforced_silo_not_equals(self):
+        # Create an authentication policy that allows accounts not belonging to
+        # the unenforced silo.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XA;;CR;;;WD;'
+                f'(@User.ad://ext/AuthenticationSilo != '
+                f'"{self._unenforced_silo}"))'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that authentication fails unless the account belongs to a silo
+        # other than the unenforced silo.
+
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_unenforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_enforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_enforced_silo_not_equals(self):
+        # Create an authentication policy that allows accounts not belonging to
+        # the enforced silo.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XA;;CR;;;WD;'
+                f'(@User.ad://ext/AuthenticationSilo != '
+                f'"{self._enforced_silo}"))'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that authentication always fails, as none of the machine
+        # accounts belong to a silo that is not the enforced one. (The
+        # unenforced silo doesn’t count, as it will never appear in a claim.)
+
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_unenforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_enforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+    def test_allowed_from_unenforced_silo_equals_deny(self):
+        # Create an authentication policy that denies accounts belonging to the
+        # unenforced silo, and allows other accounts.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XD;;CR;;;WD;'
+                f'(@User.ad://ext/AuthenticationSilo == '
+                f'"{self._unenforced_silo}"))'
+                f'(A;;CR;;;WD)'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that authentication fails unless the account belongs to a silo
+        # other than the unenforced silo.
+
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_unenforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_enforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_enforced_silo_equals_deny(self):
+        # Create an authentication policy that denies accounts belonging to the
+        # enforced silo, and allows other accounts.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XD;;CR;;;WD;'
+                f'(@User.ad://ext/AuthenticationSilo == '
+                f'"{self._enforced_silo}"))'
+                f'(A;;CR;;;WD)'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that authentication always fails, as none of the machine
+        # accounts belong to a silo that is not the enforced one. (The
+        # unenforced silo doesn’t count, as it will never appear in a claim.)
+
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_unenforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_enforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+    def test_allowed_from_unenforced_silo_not_equals_deny(self):
+        # Create an authentication policy that denies accounts not belonging to
+        # the unenforced silo, and allows other accounts.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XD;;CR;;;WD;'
+                f'(@User.ad://ext/AuthenticationSilo != '
+                f'"{self._unenforced_silo}"))'
+                f'(A;;CR;;;WD)'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that authentication always fails, as the unenforced silo will
+        # never appear in a claim.
+
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_unenforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_enforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+    def test_allowed_from_enforced_silo_not_equals_deny(self):
+        # Create an authentication policy that denies accounts not belonging to
+        # the enforced silo, and allows other accounts.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XD;;CR;;;WD;'
+                f'(@User.ad://ext/AuthenticationSilo != '
+                f'"{self._enforced_silo}"))'
+                f'(A;;CR;;;WD)'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that authentication fails unless the account belongs to the
+        # enforced silo.
+
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_unenforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        armor_tgt = self.get_tgt(self._member_of_enforced_silo)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_from_claim_equals_claim(self):
+        # Create a couple of claims.
+
+        claim0_id = self.get_new_username()
+        self.create_claim(claim0_id,
+                          enabled=True,
+                          attribute='carLicense',
+                          single_valued=True,
+                          source_type='AD',
+                          for_classes=['computer'],
+                          value_type=claims.CLAIM_TYPE_STRING)
+
+        claim1_id = self.get_new_username()
+        self.create_claim(claim1_id,
+                          enabled=True,
+                          attribute='comment',
+                          single_valued=True,
+                          source_type='AD',
+                          for_classes=['computer'],
+                          value_type=claims.CLAIM_TYPE_STRING)
+
+        # Create an authentication policy that allows accounts having the two
+        # claims be equal.
+        policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=(
+                f'O:SYD:(XA;;CR;;;WD;'
+                f'(@User.{claim0_id} == @User.{claim1_id}))'),
+        )
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        armor_tgt = self.get_tgt(self._mach_creds)
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={
+                'additional_details': (
+                    ('carLicense', 'foo'),
+                    ('comment', 'foo'),
+                ),
+            })
+        armor_tgt = self.get_tgt(
+            mach_creds,
+            expect_client_claims=True,
+            expected_client_claims={
+                claim0_id: {
+                    'source_type': claims.CLAIMS_SOURCE_TYPE_AD,
+                    'type': claims.CLAIM_TYPE_STRING,
+                    'values': ('foo',),
+                },
+                claim1_id: {
+                    'source_type': claims.CLAIMS_SOURCE_TYPE_AD,
+                    'type': claims.CLAIM_TYPE_STRING,
+                    'values': ('foo',),
+                },
+            })
+        self._get_tgt(client_creds, armor_tgt=armor_tgt,
+                      expected_error=0)
+
+    def test_allowed_to_client_equals(self):
+        client_claim_attr = 'carLicense'
+        client_claim_value = 'foo bar'
+        client_claim_values = client_claim_value,
+
+        client_claim_id = self.get_new_username()
+        self.create_claim(client_claim_id,
+                          enabled=True,
+                          attribute=client_claim_attr,
+                          single_valued=True,
+                          source_type='AD',
+                          for_classes=['user'],
+                          value_type=claims.CLAIM_TYPE_STRING)
+
+        # Create an authentication policy that allows authorization if the
+        # client has a particular claim value.
+        policy = self.create_authn_policy(
+            enforced=True,
+            computer_allowed_to=(
+                f'O:SYD:(XA;;CR;;;WD;'
+                f'((@User.{client_claim_id} == "{client_claim_value}")))'),
+        )
+
+        # Create a computer account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=policy)
+
+        armor_tgt = self.get_tgt(self._mach_creds)
+
+        # Create a user account without the claim value.
+        client_creds = self.get_cached_creds(
+            account_type=self.AccountType.USER)
+        tgt = self.get_tgt(client_creds)
+        # Show that obtaining a service ticket is denied.
+        self._tgs_req(
+            tgt, KDC_ERR_POLICY, client_creds, target_creds,
+            armor_tgt=armor_tgt,
+            expect_edata=self.expect_padata_outer,
+            # We aren’t particular about whether or not we get an NTSTATUS.
+            expect_status=None,
+            expected_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+            check_patypes=False)
+
+        # Create a user account with the claim value.
+        client_creds = self.get_cached_creds(
+            account_type=self.AccountType.USER,
+            opts={
+                'additional_details': (
+                    (client_claim_attr, client_claim_values),
+                ),
+            })
+        tgt = self.get_tgt(
+            client_creds,
+            expect_client_claims=True,
+            expected_client_claims={
+                client_claim_id: {
+                    'source_type': claims.CLAIMS_SOURCE_TYPE_AD,
+                    'type': claims.CLAIM_TYPE_STRING,
+                    'values': client_claim_values,
+                },
+            })
+        # Show that obtaining a service ticket is allowed.
+        self._tgs_req(tgt, 0, client_creds, target_creds,
+                      armor_tgt=armor_tgt)
+
+    def test_allowed_to_device_equals(self):
+        device_claim_attr = 'carLicense'
+        device_claim_value = 'bar'
+        device_claim_values = device_claim_value,
+
+        device_claim_id = self.get_new_username()
+        self.create_claim(device_claim_id,
+                          enabled=True,
+                          attribute=device_claim_attr,
+                          single_valued=True,
+                          source_type='AD',
+                          for_classes=['computer'],
+                          value_type=claims.CLAIM_TYPE_STRING)
+
+        # Create a user account.
+        client_creds = self.get_cached_creds(
+            account_type=self.AccountType.USER)
+        tgt = self.get_tgt(client_creds)
+
+        # Create an authentication policy that allows authorization if the
+        # device has a particular claim value.
+        policy = self.create_authn_policy(
+            enforced=True,
+            computer_allowed_to=(
+                f'O:SYD:(XA;;CR;;;WD;'
+                f'(@Device.{device_claim_id} == "{device_claim_value}"))'),
+        )
+
+        # Create a computer account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=policy)
+
+        armor_tgt = self.get_tgt(self._mach_creds)
+        # Show that obtaining a service ticket is denied when the claim value
+        # is not present.
+        self._tgs_req(
+            tgt, KDC_ERR_POLICY, client_creds, target_creds,
+            armor_tgt=armor_tgt,
+            expect_edata=self.expect_padata_outer,
+            # We aren’t particular about whether or not we get an NTSTATUS.
+            expect_status=None,
+            expected_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+            check_patypes=False)
+
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={
+                'additional_details': (
+                    (device_claim_attr, device_claim_values),
+                ),
+            })
+        armor_tgt = self.get_tgt(
+            mach_creds,
+            expect_client_claims=True,
+            expected_client_claims={
+                device_claim_id: {
+                    'source_type': claims.CLAIMS_SOURCE_TYPE_AD,
+                    'type': claims.CLAIM_TYPE_STRING,
+                    'values': device_claim_values,
+                },
+            })
+        # Show that obtaining a service ticket is allowed when the claim value
+        # is present.
+        self._tgs_req(tgt, 0, client_creds, target_creds,
+                      armor_tgt=armor_tgt)
+
+    claim_against_claim_cases = [
+        # If either side is missing, the result is unknown.
+        ((), '==', (), None),
+        ((), '!=', (), None),
+        ('a', '==', (), None),
+        ((), '==', 'b', None),
+        # Straightforward equality and inequality checks work.
+        ('foo', '==', 'foo', True),
+        ('foo', '==', 'bar', False),
+        ('foo', '!=', 'foo', False),
+        ('foo', '!=', 'bar', True),
+        # We can perform less‐than and greater‐than operations.
+        ('cat', '<', 'dog', True),
+        ('cat', '<=', 'dog', True),
+        ('cat', '>', 'dog', False),
+        ('cat', '>=', 'dog', False),
+        ('foo', '<=', 'foo', True),
+        ('foo', '>=', 'foo', True),
+        ('foo', '<', 'foo bar', True),
+        ('foo bar', '>', 'foo', True),
+        # String comparison is case‐sensitive.
+        ('foo bar', '==', 'Foo BAR', True),
+        ('foo bar', '==', 'FOO BAR', True),
+        ('ćàț', '==', 'ĆÀȚ', True),
+        ('ḽ', '==', 'Ḽ', True),
+        ('ⅸ', '==', 'Ⅸ', True),
+        ('ꙭ', '==', 'Ꙭ', True),
+        ('ⱦ', '==', 'Ⱦ', True),  # Lowercased variant added in Unicode 5.0.
+        ('ԛԣ', '==', 'ԚԢ', True),  # All added in Unicode 5.1.
+        ('foo', '<', 'foo', True),
+        ('ćàș', '<', 'ĆÀȚ', True),
+        ('cat', '<', 'ćàț', True),
+        # This is done by converting to UPPER CASE. Hence, both ‘A’ (U+41) and
+        # ‘a’ (U+61) compare less than ‘_’ (U+5F).
+        ('A', '<', '_', True),
+        ('a', '<', '_', True),
+        # But not all uppercased/lowercased pairs are considered to be equal in
+        # this way.
+        ('ß', '<', 'ẞ', True),
+        ('ß', '>', 'SS', True),
+        ('ⳬ', '>', 'Ⳬ', True),  # Added in Unicode 5.2.
+        ('ʞ', '<', 'Ʞ', True),  # Uppercased variant added in Unicode 6.0.
+        ('ʞ', '<', 'ʟ', True),  # U+029E < U+029F < U+A7B0 (upper variant, Ʞ)
+        ('ꞧ', '>', 'Ꞧ', True),  # Added in Unicode 6.0.
+        ('ɜ', '<', 'Ɜ', True),  # Uppercased variant added in Unicode 7.0.
+        #
+        # Strings are compared as UTF‐16 code units, rather than as Unicode
+        # codepoints. So while you might expect ‘𐀀’ (U+10000) to compare
+        # greater than ‘豈’ (U+F900), it is actually considered to be the
+        # *smaller* of the pair. That is because it is encoded as a sequence of
+        # two code units, 0xd800 and 0xdc00, which combination compares less
+        # than the single code unit 0xf900.
+        ('ퟻ', '<', '𐀀', True),
+        ('𐀀', '<', '豈', True),
+        ('ퟻ', '<', '豈', True),
+        # Composites can be compared.
+        (('foo', 'bar'), '==', ('foo', 'bar'), True),
+        (('foo', 'bar'), '==', ('foo', 'baz'), False),
+        # The individual components don’t have to match in case.
+        (('foo', 'bar'), '==', ('FOO', 'BAR'), True),
+        # Nor must they match in order.
+        (('foo', 'bar'), '==', ('bar', 'foo'), True),
+        # Composites of different lengths compare unequal.
+        (('foo', 'bar'), '!=', 'foo', True),
+        (('foo', 'bar'), '!=', ('foo', 'bar', 'baz'), True),
+        # But composites don’t have a defined ordering, and aren’t considered
+        # greater or lesser than one another.
+        (('foo', 'bar'), '<', ('foo', 'bar'), None),
+        (('foo', 'bar'), '<=', ('foo', 'bar'), None),
+        (('foo', 'bar'), '>', ('foo', 'bar', 'baz'), None),
+        (('foo', 'bar'), '>=', ('foo', 'bar', 'baz'), None),
+        # We can test for containment.
+        (('foo', 'bar'), 'Contains', ('FOO'), True),
+        (('foo', 'bar'), 'Contains', ('foo', 'bar'), True),
+        (('foo', 'bar'), 'Not_Contains', ('foo', 'bar'), False),
+        (('foo', 'bar'), 'Contains', ('foo', 'bar', 'baz'), False),
+        (('foo', 'bar'), 'Not_Contains', ('foo', 'bar', 'baz'), True),
+        # We can test whether the operands have any elements in common.
+        ('foo', 'Any_of', 'foo', True),
+        (('foo', 'bar'), 'Any_of', 'BAR', True),
+        (('foo', 'bar'), 'Any_of', 'baz', False),
+        (('foo', 'bar'), 'Not_Any_of', 'baz', True),
+        (('foo', 'bar'), 'Any_of', ('bar', 'baz'), True),
+        (('foo', 'bar'), 'Not_Any_of', ('bar', 'baz'), False),
+    ]
+
+    claim_against_literal_cases = [
+        # String comparisons also work against literals.
+        ('foo bar', '==', '"foo bar"', True),
+        # Composites can be compared with literals.
+        ('bar', '==', '{{"bar"}}', True),
+        (('apple', 'banana'), '==', '{{"APPLE", "BANANA"}}', True),
+        (('apple', 'banana'), '==', '{{"BANANA", "APPLE"}}', True),
+        (('apple', 'banana'), '==', '{{"apple", "banana", "apple"}}', False),
+        # We can test for containment.
+        ((), 'Contains', '{{"foo"}}', None),
+        ((), 'Not_Contains', '{{"foo", "bar"}}', None),
+        ('bar', 'Contains', '{{"bar"}}', True),
+        (('foo', 'bar'), 'Contains', '{{"foo", "bar"}}', True),
+        (('foo', 'bar'), 'Contains', '{{"foo", "bar", "baz"}}', False),
+        # The right‐hand side of Contains or Not_Contains does not have to be a
+        # composite.
+        ('foo', 'Contains', '"foo"', True),
+        (('foo', 'bar'), 'Not_Contains', '"foo"', False),
+        # It’s fine if the right‐hand side contains duplicate elements.
+        (('foo', 'bar'), 'Contains', '{{"foo", "bar", "bar"}}', True),
+        # We can test whether the operands have any elements in common.
+        ('bar', 'Any_of', '{{"bar"}}', True),
+        (('foo', 'bar'), 'Any_of', '{{"bar", "baz"}}', True),
+        (('foo', 'bar'), 'Any_of', '{{"baz"}}', False),
+        # The right‐hand side of Any_of or Not_Any_of must be a composite.
+        ('foo', 'Any_of', '"foo"', None),
+        (('foo', 'bar'), 'Not_Any_of', '"baz"', None),
+        # A string won’t compare equal to a numeric literal.
+        ('42', '==', '"42"', True),
+        ('42', '==', '42', None),
+        # Nor can composites that mismatch in type be compared.
+        (('123', '456'), '==', '{{"123", "456"}}', True),
+        (('654', '321'), '==', '{{654, 321}}', None),
+        (('foo', 'bar'), 'Contains', '{{1, 2, 3}}', None),
+    ]
+
+    ##########################################################################################
+
+    def _test_cmp_with_args(self, lhs, op, rhs, outcome, rhs_is_literal=False):
+        # Construct a conditional ACE expression that evaluates to True if the
+        # two claim values are equal.
+        if rhs_is_literal:
+            self.assertIsInstance(rhs, str)
+            rhs = rhs.format(self=self)
+            expression = f'(@User.{self.claim0_id} {op} {rhs})'
+        else:
+            expression = f'(@User.{self.claim0_id} {op} @User.{self.claim1_id})'
+
+        # Create an authentication policy that will allow authentication when
+        # the expression is true, and a second that will deny authentication in
+        # the same circumstance. By observing the results of authenticating
+        # against each of these policies in turn, we can determine whether the
+        # expression evaluates to a True, False, or Unknown value.
+
+        allowed_sddl = f'O:SYD:(XA;;CR;;;WD;{expression})'
+        denied_sddl = f'O:SYD:(XD;;CR;;;WD;{expression})(A;;CR;;;WD)'
+
+        allowed_policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=allowed_sddl)
+        denied_policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=denied_sddl)
+
+        # Create a user account assigned to each policy.
+        allowed_creds = self._get_creds(account_type=self.AccountType.USER,
+                                        assigned_policy=allowed_policy)
+        denied_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=denied_policy)
+
+        additional_details = ()
+        if lhs:
+            additional_details += ((self.claim0_attr, lhs),)
+        if rhs and not rhs_is_literal:
+            additional_details += ((self.claim1_attr, rhs),)
+
+        # Create a computer account with the provided attribute values.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'additional_details': additional_details})
+
+        def expected_values(val):
+            if isinstance(val, (str, bytes)):
+                return val,
+
+            return val
+
+        expected_client_claims = {}
+        if lhs:
+            expected_client_claims[self.claim0_id] = {
+                'source_type': claims.CLAIMS_SOURCE_TYPE_AD,
+                'type': claims.CLAIM_TYPE_STRING,
+                'values': expected_values(lhs),
+            }
+        if rhs and not rhs_is_literal:
+            expected_client_claims[self.claim1_id] = {
+                'source_type': claims.CLAIMS_SOURCE_TYPE_AD,
+                'type': claims.CLAIM_TYPE_STRING,
+                'values': expected_values(rhs),
+            }
+
+        # Fetch the computer account’s TGT, and ensure it contains the claims.
+        armor_tgt = self.get_tgt(
+            mach_creds,
+            expect_client_claims=bool(expected_client_claims) or None,
+            expected_client_claims=expected_client_claims)
+
+        # The first or the second authentication request is expected to succeed
+        # if the outcome is True or False, respectively. An Unknown outcome,
+        # represented by None, will result in a policy error in either case.
+        allowed_error = 0 if outcome is True else KDC_ERR_POLICY
+        denied_error = 0 if outcome is False else KDC_ERR_POLICY
+
+        # Attempt to authenticate and ensure that we observe the expected
+        # results.
+        self._get_tgt(allowed_creds, armor_tgt=armor_tgt,
+                      expected_error=allowed_error)
+        self._get_tgt(denied_creds, armor_tgt=armor_tgt,
+                      expected_error=denied_error)
+
+    pac_claim_cases = [
+        # Test a very simple expression with various claims.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{non_empty_string}', claims.CLAIM_TYPE_STRING, ['foo bar']),
+            ]),
+        ], '{non_empty_string}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{zero_uint}', claims.CLAIM_TYPE_UINT64, [0]),
+            ]),
+        ], '{zero_uint}', False),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{nonzero_uint}', claims.CLAIM_TYPE_UINT64, [1]),
+            ]),
+        ], '{nonzero_uint}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{zero_uints}', claims.CLAIM_TYPE_UINT64, [0, 0]),
+            ]),
+        ], '{zero_uints}', KDC_ERR_GENERIC),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{zero_and_one_uint}', claims.CLAIM_TYPE_UINT64, [0, 1]),
+            ]),
+        ], '{zero_and_one_uint}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{one_and_zero_uint}', claims.CLAIM_TYPE_UINT64, [1, 0]),
+            ]),
+        ], '{one_and_zero_uint}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{zero_int}', claims.CLAIM_TYPE_INT64, [0]),
+            ]),
+        ], '{zero_int}', False),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{nonzero_int}', claims.CLAIM_TYPE_INT64, [1]),
+            ]),
+        ], '{nonzero_int}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{zero_ints}', claims.CLAIM_TYPE_INT64, [0, 0]),
+            ]),
+        ], '{zero_ints}', KDC_ERR_GENERIC),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{zero_and_one_int}', claims.CLAIM_TYPE_INT64, [0, 1]),
+            ]),
+        ], '{zero_and_one_int}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{one_and_zero_int}', claims.CLAIM_TYPE_INT64, [1, 0]),
+            ]),
+        ], '{one_and_zero_int}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{false_boolean}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+            ]),
+        ], '{false_boolean}', False),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{true_boolean}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+            ]),
+        ], '{true_boolean}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{false_booleans}', claims.CLAIM_TYPE_BOOLEAN, [0, 0]),
+            ]),
+        ], '{false_booleans}', KDC_ERR_GENERIC),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{false_and_true_boolean}', claims.CLAIM_TYPE_BOOLEAN, [0, 1]),
+            ]),
+        ], '{false_and_true_boolean}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{true_and_false_boolean}', claims.CLAIM_TYPE_BOOLEAN, [1, 0]),
+            ]),
+        ], '{true_and_false_boolean}', True),
+        # Test a basic comparison against a literal.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_STRING, ['foo bar']),
+            ]),
+        ], '{a} == "foo bar"', True),
+        # Claims can be compared against one another.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_STRING, ['foo bar']),
+                ('{b}', claims.CLAIM_TYPE_STRING, ['FOO BAR']),
+            ]),
+        ], '{a} == {b}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{b}', claims.CLAIM_TYPE_STRING, ['FOO', 'BAR', 'BAZ']),
+                ('{a}', claims.CLAIM_TYPE_STRING, ['foo', 'bar', 'baz']),
+            ]),
+        ], '{a} != {b}', False),
+        # Certificate claims are also valid.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_CERTIFICATE, [
+                ('{a}', claims.CLAIM_TYPE_STRING, ['foo']),
+            ]),
+        ], '{a} == "foo"', True),
+        # Other claim source types are ignored.
+        ([
+            (0, [
+                ('{a}', claims.CLAIM_TYPE_STRING, ['foo']),
+            ]),
+        ], '{a} == "foo"', None),
+        ([
+            (3, [
+                ('{a}', claims.CLAIM_TYPE_STRING, ['foo']),
+            ]),
+        ], '{a} == "foo"', None),
+        # If multiple claims have the same ID, the *last* one takes precedence.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_STRING, ['this is not the value…']),
+                ('{a}', claims.CLAIM_TYPE_STRING, ['…nor is this…']),
+            ]),
+            (claims.CLAIMS_SOURCE_TYPE_CERTIFICATE, [
+                ('{a}', claims.CLAIM_TYPE_STRING, ['…and this isn’t either.']),
+            ]),
+            (claims.CLAIMS_SOURCE_TYPE_CERTIFICATE, [
+                ('{a}', claims.CLAIM_TYPE_STRING, ['here’s the actual value!']),
+            ]),
+            (3, [
+                ('{a}', claims.CLAIM_TYPE_STRING, ['this is a red herring.']),
+            ]),
+        ], '{a} == "here’s the actual value!"', True),
+        # Claim values can be empty.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{empty_claim_string}', claims.CLAIM_TYPE_STRING, []),
+            ]),
+        ], '{empty_claim_string} != "foo bar"', None),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{empty_claim_boolean}', claims.CLAIM_TYPE_BOOLEAN, []),
+            ]),
+        ], 'Exists {empty_claim_boolean}', None),
+        # Test unsigned integer equality.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_UINT64, [42]),
+            ]),
+        ], '{a} == 42', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_UINT64, [0]),
+            ]),
+        ], '{a} == 3', False),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_UINT64, [1, 2, 3]),
+            ]),
+        ], '{a} == {{1, 2, 3}}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_UINT64, [4, 5, 6]),
+            ]),
+        ], '{a} != {{1, 2, 3}}', True),
+        # Test unsigned integer comparison. Ensure we don’t run into any
+        # integer overflow issues.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_UINT64, [1 << 32]),
+            ]),
+        ], '{a} > 0', True),
+        # Test signed integer comparisons.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_INT64, [42]),
+            ]),
+        ], '{a} == 42', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_INT64, [42 << 32]),
+            ]),
+        ], f'{{a}} == {42 << 32}', True),
+        # Test boolean claims. Be careful! Windows will *crash* if you send it
+        # claims that aren’t real booleans (not 0 or 1). I doubt Microsoft will
+        # consider this a security issue though.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [2]),
+                ('{b}', claims.CLAIM_TYPE_BOOLEAN, [3]),
+            ]),
+        ], '{a} == {b}', CRASHES_WINDOWS),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+                ('{b}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+            ]),
+        ], '{a} == {b}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+            ]),
+        ], '{a} == 42', None),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+                ('{b}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+            ]),
+        ], '{a} && {b}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+                ('{b}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+            ]),
+        ], '{a} && {b}', False),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+                ('{b}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+            ]),
+        ], '{a} && {b}', False),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+                ('{b}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+            ]),
+        ], '{a} || {b}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+                ('{b}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+            ]),
+        ], '{a} || {b}', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+                ('{b}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+            ]),
+        ], '{a} || {b}', False),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+            ]),
+        ], '!({a})', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+            ]),
+        ], '!(!(!(!({a}))))', False),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+            ]),
+        ], '!({a} && {a})', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+                ('{b}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+            ]),
+        ], '{a} && !({b} || {b})', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+            ]),
+        ], '!({a}) || !({a})', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+                ('{b}', claims.CLAIM_TYPE_BOOLEAN, [0]),
+            ]),
+        ], '{a} && !({b})', None),
+        # Expressions containing the ‘not’ operator are occasionally evaluated
+        # inconsistently, as evidenced here. ‘a || !a’ evaluates to ‘unknown’…
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+            ]),
+        ], '{a} || !({a})', None),
+        # …but ‘!a || a’ — the same expression, just with the operands switched
+        # round — evaluates to ‘true’.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+            ]),
+        ], '!({a}) || {a}', True),
+        # This inconsistency is not observed with other boolean expressions,
+        # such as ‘a || a’.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+            ]),
+        ], '{a} || ({a} || {a})', True),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{b}', claims.CLAIM_TYPE_BOOLEAN, [1]),
+            ]),
+        ], '({b} || {b}) || {b}', True),
+        # Test a very large claim. Much larger than this, and
+        # conditional_ace_encode_binary() will refuse to encode the conditions.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{large_claim}', claims.CLAIM_TYPE_STRING, ['z' * 4900]),
+            ]),
+        ], f'{{large_claim}} == "{"z" * 4900}"', True),
+        # Test an even larger claim. Windows does not appear to like receiving
+        # a claim this large.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{larger_claim}', claims.CLAIM_TYPE_STRING, ['z' * 100000]),
+            ]),
+        ], '{larger_claim} > "z"', CRASHES_WINDOWS),
+        # Test a great number of claims. Windows does not appear to like
+        # receiving this many claims.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{many_claims}', claims.CLAIM_TYPE_UINT64,
+                 list(range(0, 100000))),
+            ]),
+        ], '{many_claims} Any_of "99999"', CRASHES_WINDOWS),
+        # Test a claim with a very long name. Much larger than this, and
+        # conditional_ace_encode_binary() will refuse to encode the conditions.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{long_name}', claims.CLAIM_TYPE_STRING, ['a']),
+            ]),
+        ], '{long_name} == "a"', {'long_name': 'z' * 4900}, True),
+        # Test attribute name escaping.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{escaped_claim}', claims.CLAIM_TYPE_STRING, ['claim value']),
+            ]),
+        ], '{escaped_claim} == "claim value"',
+           {'escaped_claim': '(:foo:! /&/ :bar:!)'}, True),
+        # Test a claim whose name consists entirely of dots.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{dotty_claim}', claims.CLAIM_TYPE_STRING, ['a']),
+            ]),
+        ], '{dotty_claim} == "a"', {'dotty_claim': '...'}, True),
+        # Test a claim whose name consists of the first thousand non‐zero
+        # Unicode codepoints.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{1000_unicode}', claims.CLAIM_TYPE_STRING, ['a']),
+            ]),
+        ], '{1000_unicode} == "a"',
+           {'1000_unicode': ''.join(map(chr, range(1, 1001)))}, True),
+        # Test a claim whose name consists of some higher Unicode codepoints,
+        # including non‐BMP ones.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{higher_unicode}', claims.CLAIM_TYPE_STRING, ['a']),
+            ]),
+        ], '{higher_unicode} == "a"',
+           {'higher_unicode': ''.join(map(chr, range(0xfe00, 0x10800)))}, True),
+        # Duplicate claim values are not allowed…
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_INT64, [42, 42, 42]),
+            ]),
+        ], '{a} == {a}', KDC_ERR_GENERIC),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_UINT64, [42, 42]),
+            ]),
+        ], '{a} == {a}', KDC_ERR_GENERIC),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_STRING, ['foo', 'foo']),
+            ]),
+        ], '{a} == {a}', KDC_ERR_GENERIC),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_STRING, ['FOO', 'foo']),
+            ]),
+        ], '{a} == {a}', KDC_ERR_GENERIC),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{a}', claims.CLAIM_TYPE_BOOLEAN, [0, 0]),
+            ]),
+        ], '{a} == {a}', KDC_ERR_GENERIC),
+        # …but it’s OK if duplicate values are spread across multiple claim
+        # entries.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{dup}', claims.CLAIM_TYPE_STRING, ['foo']),
+                ('{dup}', claims.CLAIM_TYPE_STRING, ['foo']),
+            ]),
+            (claims.CLAIMS_SOURCE_TYPE_CERTIFICATE, [
+                ('{dup}', claims.CLAIM_TYPE_UINT64, [42]),
+                ('{dup}', claims.CLAIM_TYPE_UINT64, [42]),
+            ]),
+            (claims.CLAIMS_SOURCE_TYPE_CERTIFICATE, [
+                ('{dup}', claims.CLAIM_TYPE_STRING, ['foo']),
+                ('{dup}', claims.CLAIM_TYPE_STRING, ['foo']),
+                ('{dup}', claims.CLAIM_TYPE_STRING, ['foo', 'bar']),
+                ('{dup}', claims.CLAIM_TYPE_STRING, ['foo', 'bar']),
+            ]),
+        ], '{dup} == {dup}', True),
+        # Test invalid claim types. Be careful! Windows will *crash* if you
+        # send it invalid claim types. I doubt Microsoft will consider this a
+        # security issue though.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{invalid_sid}', 5, []),
+            ]),
+        ], '{invalid_sid} == {invalid_sid}', CRASHES_WINDOWS),
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{invalid_octet_string}', 16, []),
+            ]),
+        ], '{invalid_octet_string} == {invalid_octet_string}', CRASHES_WINDOWS),
+        # Sending an empty string will crash Windows.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{empty_string}', claims.CLAIM_TYPE_STRING, ['']),
+            ]),
+        ], '{empty_string}', CRASHES_WINDOWS),
+        # But sending empty arrays is OK.
+        ([
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                ('{empty_array}', claims.CLAIM_TYPE_INT64, []),
+                ('{empty_array}', claims.CLAIM_TYPE_UINT64, []),
+                ('{empty_array}', claims.CLAIM_TYPE_BOOLEAN, []),
+                ('{empty_array}', claims.CLAIM_TYPE_STRING, []),
+            ]),
+        ], '{empty_array}', None),
+    ]
+
+    def _test_pac_claim_cmp_with_args(self,
+                                      pac_claims,
+                                      expression,
+                                      claim_map,
+                                      outcome):
+        self.assertIsInstance(expression, str)
+
+        if outcome is CRASHES_WINDOWS and not self.crash_windows:
+            self.skipTest('test crashes Windows servers')
+
+        if claim_map is None:
+            claim_map = {}
+
+        claim_ids = {}
+
+        def get_claim_id(claim_name):
+            claim = claim_ids.get(claim_name)
+            if claim is None:
+                claim = claim_map.pop(claim_name, None)
+                if claim is None:
+                    claim = self.get_new_username()
+
+                claim_ids[claim_name] = claim
+
+            return claim
+
+        def formatted_claim_expression(expr):
+            formatter = Formatter()
+            result = []
+
+            for literal_text, field_name, format_spec, conversion in (
+                    formatter.parse(expr)):
+                self.assertFalse(format_spec,
+                                 f'format specifier ({format_spec}) should '
+                                 f'not be specified')
+                self.assertFalse(conversion,
+                                 f'conversion ({conversion}) should not be '
+                                 'specified')
+
+                result.append(literal_text)
+
+                if field_name is not None:
+                    self.assertTrue(field_name,
+                                    'a field name should be specified')
+
+                    claim_id = get_claim_id(field_name)
+                    claim_id = self.escaped_claim_id(claim_id)
+                    result.append(f'@User.{claim_id}')
+
+            return ''.join(result)
+
+        # Construct the conditional ACE expression.
+        expression = formatted_claim_expression(expression)
+
+        self.assertFalse(claim_map, 'unused claim mapping(s) remain')
+
+        # Create an authentication policy that will allow authentication when
+        # the expression is true, and a second that will deny authentication in
+        # the same circumstance. By observing the results of authenticating
+        # against each of these policies in turn, we can determine whether the
+        # expression evaluates to a True, False, or Unknown value.
+
+        allowed_sddl = f'O:SYD:(XA;;CR;;;WD;({expression}))'
+        denied_sddl = f'O:SYD:(XD;;CR;;;WD;({expression}))(A;;CR;;;WD)'
+
+        allowed_policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=allowed_sddl)
+        denied_policy = self.create_authn_policy(
+            enforced=True,
+            user_allowed_from=denied_sddl)
+
+        # Create a user account assigned to each policy.
+        allowed_creds = self._get_creds(account_type=self.AccountType.USER,
+                                        assigned_policy=allowed_policy)
+        denied_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=denied_policy)
+
+        # Create a computer account.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER)
+
+        def expected_values(val):
+            if isinstance(val, (str, bytes)):
+                return val,
+
+            return val
+
+        # Fetch the computer account’s TGT.
+        armor_tgt = self.get_tgt(mach_creds)
+
+        if pac_claims:
+            # Replace the claims in the PAC with our own.
+            armor_tgt = self.modified_ticket(
+                armor_tgt,
+                modify_pac_fn=partial(self.set_pac_claims,
+                                      client_claims=pac_claims,
+                                      claim_ids=claim_ids),
+                checksum_keys=self.get_krbtgt_checksum_key())
+
+        # The first or the second authentication request is expected to succeed
+        # if the outcome is True or False, respectively. An Unknown outcome,
+        # represented by None, will result in a policy error in either case.
+        if outcome is True:
+            allowed_error, denied_error = 0, KDC_ERR_POLICY
+        elif outcome is False:
+            allowed_error, denied_error = KDC_ERR_POLICY, 0
+        elif outcome is None:
+            allowed_error, denied_error = KDC_ERR_POLICY, KDC_ERR_POLICY
+        else:
+            allowed_error, denied_error = outcome, outcome
+
+        # Attempt to authenticate and ensure that we observe the expected
+        # results.
+        self._get_tgt(allowed_creds, armor_tgt=armor_tgt,
+                      expected_error=allowed_error)
+        self._get_tgt(denied_creds, armor_tgt=armor_tgt,
+                      expected_error=denied_error)
+
+    def test_rbcd_without_aa_asserted_identity(self):
+        service_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Member_of SID({self.aa_asserted_identity})',
+                   service_sids=service_sids,
+                   code=KDC_ERR_BADOPTION,
+                   status=ntstatus.NT_STATUS_UNSUCCESSFUL,
+                   edata=self.expect_padata_outer)
+
+        self._rbcd(target_policy=f'Member_of SID({self.aa_asserted_identity})',
+                   service_sids=service_sids,
+                   code=KDC_ERR_POLICY,
+                   status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                   event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                   reason=AuditReason.ACCESS_DENIED,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_with_aa_asserted_identity(self):
+        service_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.aa_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        expected_groups = service_sids | {
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Member_of SID({self.aa_asserted_identity})',
+                   service_sids=service_sids,
+                   expected_groups=expected_groups)
+
+        self._rbcd(target_policy=f'Member_of SID({self.aa_asserted_identity})',
+                   service_sids=service_sids,
+                   expected_groups=expected_groups)
+
+    def test_rbcd_without_service_asserted_identity(self):
+        service_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Member_of SID({self.service_asserted_identity})',
+                   service_sids=service_sids,
+                   code=KDC_ERR_BADOPTION,
+                   status=ntstatus.NT_STATUS_UNSUCCESSFUL,
+                   edata=self.expect_padata_outer)
+
+        self._rbcd(target_policy=f'Member_of SID({self.service_asserted_identity})',
+                   service_sids=service_sids,
+                   code=KDC_ERR_POLICY,
+                   status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                   event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                   reason=AuditReason.ACCESS_DENIED,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_with_service_asserted_identity(self):
+        service_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.service_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        expected_groups = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            # The Application Authority Asserted Identity SID has replaced the
+            # Service Asserted Identity SID.
+            (self.aa_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Member_of SID({self.service_asserted_identity})',
+                   service_sids=service_sids,
+                   expected_groups=expected_groups)
+
+        self._rbcd(target_policy=f'Member_of SID({self.service_asserted_identity})',
+                   service_sids=service_sids,
+                   expected_groups=expected_groups)
+
+    def test_rbcd_without_claims_valid(self):
+        service_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                   service_sids=service_sids,
+                   code=KDC_ERR_BADOPTION,
+                   status=ntstatus.NT_STATUS_UNSUCCESSFUL,
+                   edata=self.expect_padata_outer)
+
+        self._rbcd(target_policy=f'Member_of SID({security.SID_CLAIMS_VALID})',
+                   service_sids=service_sids,
+                   code=KDC_ERR_POLICY,
+                   status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                   event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                   reason=AuditReason.ACCESS_DENIED,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_with_claims_valid(self):
+        service_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        expected_groups = service_sids | {
+            (self.aa_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                   service_sids=service_sids,
+                   expected_groups=expected_groups)
+
+        self._rbcd(target_policy=f'Member_of SID({security.SID_CLAIMS_VALID})',
+                   service_sids=service_sids,
+                   expected_groups=expected_groups)
+
+    def test_rbcd_without_compounded_authentication(self):
+        service_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   service_sids=service_sids,
+                   code=KDC_ERR_BADOPTION,
+                   status=ntstatus.NT_STATUS_UNSUCCESSFUL,
+                   edata=self.expect_padata_outer)
+
+        self._rbcd(target_policy=f'Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   service_sids=service_sids,
+                   code=KDC_ERR_POLICY,
+                   status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                   event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                   reason=AuditReason.ACCESS_DENIED,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_with_compounded_authentication(self):
+        service_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (security.SID_COMPOUNDED_AUTHENTICATION, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        expected_groups = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.aa_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   service_sids=service_sids,
+                   expected_groups=expected_groups)
+
+        self._rbcd(target_policy=f'Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   service_sids=service_sids,
+                   expected_groups=expected_groups)
+
+    def test_rbcd_client_without_aa_asserted_identity(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Member_of SID({self.aa_asserted_identity})',
+                   client_sids=client_sids)
+
+        self._rbcd(target_policy=f'Member_of SID({self.aa_asserted_identity})',
+                   client_sids=client_sids)
+
+    def test_rbcd_client_with_aa_asserted_identity(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.aa_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Member_of SID({self.aa_asserted_identity})',
+                   client_sids=client_sids,
+                   expected_groups=client_sids)
+
+        self._rbcd(target_policy=f'Member_of SID({self.aa_asserted_identity})',
+                   client_sids=client_sids,
+                   expected_groups=client_sids)
+
+    def test_rbcd_client_without_service_asserted_identity(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Member_of SID({self.service_asserted_identity})',
+                   client_sids=client_sids,
+                   code=KDC_ERR_BADOPTION,
+                   status=ntstatus.NT_STATUS_UNSUCCESSFUL,
+                   edata=self.expect_padata_outer)
+
+        self._rbcd(target_policy=f'Member_of SID({self.service_asserted_identity})',
+                   client_sids=client_sids,
+                   code=KDC_ERR_POLICY,
+                   status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                   event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                   reason=AuditReason.ACCESS_DENIED,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_client_with_service_asserted_identity(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.service_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Not_Member_of SID({self.service_asserted_identity})',
+                   client_sids=client_sids,
+                   expected_groups=client_sids)
+
+        self._rbcd(target_policy=f'Not_Member_of SID({self.service_asserted_identity})',
+                   client_sids=client_sids,
+                   expected_groups=client_sids)
+
+    def test_rbcd_client_without_claims_valid(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                   client_sids=client_sids)
+
+        self._rbcd(target_policy=f'Member_of SID({security.SID_CLAIMS_VALID})',
+                   client_sids=client_sids)
+
+    def test_rbcd_client_with_claims_valid(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                   client_sids=client_sids,
+                   expected_groups=client_sids)
+
+        self._rbcd(target_policy=f'Member_of SID({security.SID_CLAIMS_VALID})',
+                   client_sids=client_sids,
+                   expected_groups=client_sids)
+
+    def test_rbcd_client_without_compounded_authentication(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   client_sids=client_sids,
+                   code=KDC_ERR_BADOPTION,
+                   status=ntstatus.NT_STATUS_UNSUCCESSFUL,
+                   edata=self.expect_padata_outer)
+
+        self._rbcd(target_policy=f'Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   client_sids=client_sids,
+                   code=KDC_ERR_POLICY,
+                   status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                   event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                   reason=AuditReason.ACCESS_DENIED,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_client_with_compounded_authentication(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (security.SID_COMPOUNDED_AUTHENTICATION, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Not_Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   client_sids=client_sids,
+                   expected_groups=client_sids)
+
+        self._rbcd(target_policy=f'Not_Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   client_sids=client_sids,
+                   expected_groups=client_sids)
+
+    def test_rbcd_device_without_aa_asserted_identity(self):
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Device_Member_of SID({self.aa_asserted_identity})',
+                   device_sids=device_sids,
+                   code=KDC_ERR_BADOPTION,
+                   status=ntstatus.NT_STATUS_UNSUCCESSFUL,
+                   edata=self.expect_padata_outer)
+
+        self._rbcd(target_policy=f'Device_Member_of SID({self.aa_asserted_identity})',
+                   device_sids=device_sids,
+                   code=KDC_ERR_POLICY,
+                   status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                   event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                   reason=AuditReason.ACCESS_DENIED,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_device_without_aa_asserted_identity_not_memberof(self):
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Not_Device_Member_of SID({self.aa_asserted_identity})',
+                   device_sids=device_sids)
+
+        self._rbcd(target_policy=f'Not_Device_Member_of SID({self.aa_asserted_identity})',
+                   device_sids=device_sids)
+
+    def test_rbcd_device_with_aa_asserted_identity(self):
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.aa_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Device_Member_of SID({self.aa_asserted_identity})',
+                   device_sids=device_sids)
+
+        self._rbcd(target_policy=f'Device_Member_of SID({self.aa_asserted_identity})',
+                   device_sids=device_sids)
+
+    def test_rbcd_device_without_service_asserted_identity(self):
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Device_Member_of SID({self.service_asserted_identity})',
+                   device_sids=device_sids,
+                   code=KDC_ERR_BADOPTION,
+                   status=ntstatus.NT_STATUS_UNSUCCESSFUL,
+                   edata=self.expect_padata_outer)
+
+        self._rbcd(target_policy=f'Device_Member_of SID({self.service_asserted_identity})',
+                   device_sids=device_sids,
+                   code=KDC_ERR_POLICY,
+                   status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                   event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                   reason=AuditReason.ACCESS_DENIED,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_device_with_service_asserted_identity(self):
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.service_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Device_Member_of SID({self.service_asserted_identity})',
+                   device_sids=device_sids)
+
+        self._rbcd(target_policy=f'Device_Member_of SID({self.service_asserted_identity})',
+                   device_sids=device_sids)
+
+    def test_rbcd_device_without_claims_valid(self):
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Device_Member_of SID({security.SID_CLAIMS_VALID})',
+                   device_sids=device_sids,
+                   code=KDC_ERR_BADOPTION,
+                   status=ntstatus.NT_STATUS_UNSUCCESSFUL,
+                   edata=self.expect_padata_outer)
+
+        self._rbcd(target_policy=f'Device_Member_of SID({security.SID_CLAIMS_VALID})',
+                   device_sids=device_sids,
+                   code=KDC_ERR_POLICY,
+                   status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                   event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                   reason=AuditReason.ACCESS_DENIED,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_device_with_claims_valid(self):
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Device_Member_of SID({security.SID_CLAIMS_VALID})',
+                   device_sids=device_sids)
+
+        self._rbcd(target_policy=f'Device_Member_of SID({security.SID_CLAIMS_VALID})',
+                   device_sids=device_sids)
+
+    def test_rbcd_device_without_compounded_auth(self):
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._rbcd(f'Device_Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   device_sids=device_sids,
+                   code=KDC_ERR_BADOPTION,
+                   status=ntstatus.NT_STATUS_UNSUCCESSFUL,
+                   edata=self.expect_padata_outer)
+
+        self._rbcd(target_policy=f'Device_Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   device_sids=device_sids,
+                   code=KDC_ERR_POLICY,
+                   status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                   event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                   reason=AuditReason.ACCESS_DENIED,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_device_with_compounded_auth(self):
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (security.SID_COMPOUNDED_AUTHENTICATION, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._rbcd(f'Device_Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   device_sids=device_sids)
+
+        self._rbcd(target_policy=f'Device_Member_of SID({security.SID_COMPOUNDED_AUTHENTICATION})',
+                   device_sids=device_sids)
+
+    def test_rbcd(self):
+        self._rbcd('Member_of SID({service_sid})')
+
+    def test_rbcd_device_from_rodc(self):
+        self._rbcd('Member_of SID({service_sid})',
+                   device_from_rodc=True,
+                   code=CRASHES_WINDOWS)
+
+    def test_rbcd_service_from_rodc(self):
+        self._rbcd('Member_of SID({service_sid})',
+                   service_from_rodc=True,
+                   code=KDC_ERR_BADOPTION,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_device_and_service_from_rodc(self):
+        self._rbcd('Member_of SID({service_sid})',
+                   service_from_rodc=True,
+                   device_from_rodc=True,
+                   code=CRASHES_WINDOWS)
+
+    def test_rbcd_client_from_rodc(self):
+        self._rbcd('Member_of SID({service_sid})',
+                   client_from_rodc=True,
+                   code=KDC_ERR_MODIFIED,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_client_and_device_from_rodc(self):
+        self._rbcd('Member_of SID({service_sid})',
+                   client_from_rodc=True,
+                   device_from_rodc=True,
+                   code=CRASHES_WINDOWS)
+
+    def test_rbcd_client_and_service_from_rodc(self):
+        self._rbcd('Member_of SID({service_sid})',
+                   client_from_rodc=True,
+                   service_from_rodc=True,
+                   code=KDC_ERR_BADOPTION,
+                   edata=self.expect_padata_outer)
+
+    def test_rbcd_all_from_rodc(self):
+        self._rbcd('Member_of SID({service_sid})',
+                   client_from_rodc=True,
+                   service_from_rodc=True,
+                   device_from_rodc=True,
+                   code=CRASHES_WINDOWS)
+
+    def _rbcd(self,
+              rbcd_expression=None,
+              *,
+              code=0,
+              status=None,
+              event=AuditEvent.OK,
+              reason=AuditReason.NONE,
+              edata=False,
+              target_policy=None,
+              client_from_rodc=False,
+              service_from_rodc=False,
+              device_from_rodc=False,
+              client_sids=None,
+              client_claims=None,
+              service_sids=None,
+              service_claims=None,
+              device_sids=None,
+              device_claims=None,
+              expected_groups=None,
+              expected_device_groups=None,
+              expected_claims=None):
+        if code is CRASHES_WINDOWS and not self.crash_windows:
+            self.skipTest('test crashes Windows servers')
+
+        samdb = self.get_samdb()
+        functional_level = self.get_domain_functional_level(samdb)
+
+        if functional_level < dsdb.DS_DOMAIN_FUNCTION_2008:
+            self.skipTest('RBCD requires FL2008')
+
+        domain_sid_str = samdb.get_domain_sid()
+        domain_sid = security.dom_sid(domain_sid_str)
+
+        client_creds = self.get_cached_creds(
+            account_type=self.AccountType.USER,
+            opts={
+                'allowed_replication_mock': client_from_rodc,
+                'revealed_to_mock_rodc': client_from_rodc,
+            })
+        client_sid = client_creds.get_sid()
+
+        client_username = client_creds.get_username()
+        client_cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
+                                                 names=[client_username])
+
+        client_tkt_options = 'forwardable'
+        expected_flags = krb5_asn1.TicketFlags(client_tkt_options)
+
+        checksum_key = self.get_krbtgt_checksum_key()
+
+        if client_from_rodc or service_from_rodc or device_from_rodc:
+            rodc_krbtgt_creds = self.get_mock_rodc_krbtgt_creds()
+            rodc_krbtgt_key = self.TicketDecryptionKey_from_creds(rodc_krbtgt_creds)
+            rodc_checksum_key = {
+                krb5pac.PAC_TYPE_KDC_CHECKSUM: rodc_krbtgt_key,
+            }
+
+        client_tgt = self.get_tgt(client_creds,
+                                  kdc_options=client_tkt_options,
+                                  expected_flags=expected_flags)
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={
+                'allowed_replication_mock': device_from_rodc,
+                'revealed_to_mock_rodc': device_from_rodc,
+            })
+        mach_tgt = self.get_tgt(mach_creds)
+        device_modify_pac_fn = []
+        if device_sids is not None:
+            device_modify_pac_fn.append(partial(self.set_pac_sids,
+                                                new_sids=device_sids))
+        if device_claims is not None:
+            device_modify_pac_fn.append(partial(self.set_pac_claims,
+                                                client_claims=device_claims))
+        mach_tgt = self.modified_ticket(
+            mach_tgt,
+            modify_pac_fn=device_modify_pac_fn,
+            new_ticket_key=rodc_krbtgt_key if device_from_rodc else None,
+            checksum_keys=rodc_checksum_key if device_from_rodc else checksum_key)
+
+        service_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={
+                'id': 1,
+                'allowed_replication_mock': service_from_rodc,
+                'revealed_to_mock_rodc': service_from_rodc,
+            })
+        service_tgt = self.get_tgt(service_creds)
+
+        service_modify_pac_fn = []
+        if service_sids is not None:
+            service_modify_pac_fn.append(partial(self.set_pac_sids,
+                                                 new_sids=service_sids))
+        if service_claims is not None:
+            service_modify_pac_fn.append(partial(self.set_pac_claims,
+                                                 client_claims=service_claims))
+        service_tgt = self.modified_ticket(
+            service_tgt,
+            modify_pac_fn=service_modify_pac_fn,
+            new_ticket_key=rodc_krbtgt_key if service_from_rodc else None,
+            checksum_keys=rodc_checksum_key if service_from_rodc else checksum_key)
+
+        if target_policy is None:
+            policy = None
+            assigned_policy = None
+        else:
+            sddl = f'O:SYD:(XA;;CR;;;WD;({target_policy.format(service_sid=service_creds.get_sid())}))'
+            policy = self.create_authn_policy(enforced=True,
+                                              computer_allowed_to=sddl)
+            assigned_policy = str(policy.dn)
+
+        if rbcd_expression is not None:
+            sddl = f'O:SYD:(XA;;CR;;;WD;({rbcd_expression.format(service_sid=service_creds.get_sid())}))'
+        else:
+            sddl = 'O:SYD:(A;;CR;;;WD)'
+        descriptor = security.descriptor.from_sddl(sddl, domain_sid)
+        descriptor = ndr_pack(descriptor)
+
+        # Create a target account with the assigned policy.
+        target_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={
+                'assigned_policy': assigned_policy,
+                'additional_details': (
+                    ('msDS-AllowedToActOnBehalfOfOtherIdentity', descriptor),
+                ),
+            })
+
+        client_service_tkt = self.get_service_ticket(
+            client_tgt,
+            service_creds,
+            kdc_options=client_tkt_options,
+            expected_flags=expected_flags)
+        client_modify_pac_fn = []
+        if client_sids is not None:
+            client_modify_pac_fn.append(partial(self.set_pac_sids,
+                                                new_sids=client_sids))
+        if client_claims is not None:
+            client_modify_pac_fn.append(partial(self.set_pac_claims,
+                                                client_claims=client_claims))
+        client_service_tkt = self.modified_ticket(client_service_tkt,
+                                                  modify_pac_fn=client_modify_pac_fn,
+                                                  checksum_keys=rodc_checksum_key if client_from_rodc else checksum_key)
+
+        kdc_options = str(krb5_asn1.KDCOptions('cname-in-addl-tkt'))
+
+        target_decryption_key = self.TicketDecryptionKey_from_creds(
+            target_creds)
+        target_etypes = target_creds.tgs_supported_enctypes
+
+        service_name = service_creds.get_username()
+        if service_name[-1] == '$':
+            service_name = service_name[:-1]
+        expected_transited_services = [
+            f'host/{service_name}@{service_creds.get_realm()}'
+        ]
+
+        expected_groups = self.map_sids(expected_groups, None, domain_sid_str)
+        expected_device_groups = self.map_sids(expected_device_groups, None, domain_sid_str)
+
+        # Show that obtaining a service ticket with RBCD is allowed.
+        self._tgs_req(service_tgt, code, service_creds, target_creds,
+                      armor_tgt=mach_tgt,
+                      kdc_options=kdc_options,
+                      pac_options='1001',  # supports claims, RBCD
+                      expected_cname=client_cname,
+                      expected_account_name=client_username,
+                      additional_ticket=client_service_tkt,
+                      decryption_key=target_decryption_key,
+                      expected_sid=client_sid,
+                      expected_groups=expected_groups,
+                      expect_device_info=bool(expected_device_groups) or None,
+                      expected_device_domain_sid=domain_sid_str,
+                      expected_device_groups=expected_device_groups,
+                      expect_client_claims=bool(expected_claims) or None,
+                      expected_client_claims=expected_claims,
+                      expected_supported_etypes=target_etypes,
+                      expected_proxy_target=target_creds.get_spn(),
+                      expected_transited_services=expected_transited_services,
+                      expected_status=status,
+                      expect_edata=edata)
+
+        if code:
+            effective_client_creds = service_creds
+        else:
+            effective_client_creds = client_creds
+
+        self.check_tgs_log(effective_client_creds, target_creds,
+                           policy=policy,
+                           checked_creds=service_creds,
+                           status=status,
+                           event=event,
+                           reason=reason)
+
+    def test_tgs_without_aa_asserted_identity(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({self.aa_asserted_identity})',
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=KDC_ERR_POLICY,
+                  status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                  event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                  reason=AuditReason.ACCESS_DENIED,
+                  edata=self.expect_padata_outer)
+
+    def test_tgs_without_aa_asserted_identity_client_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({self.aa_asserted_identity})',
+                  client_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=KDC_ERR_POLICY,
+                  edata=self.expect_padata_outer)
+
+    def test_tgs_without_aa_asserted_identity_device_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({self.aa_asserted_identity})',
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def test_tgs_without_aa_asserted_identity_both_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({self.aa_asserted_identity})',
+                  client_from_rodc=True,
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def test_tgs_with_aa_asserted_identity(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.aa_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({self.aa_asserted_identity})',
+                  client_sids=client_sids,
+                  expected_groups=client_sids)
+
+    def test_tgs_with_aa_asserted_identity_client_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.aa_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({self.aa_asserted_identity})',
+                  client_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=KDC_ERR_POLICY,
+                  edata=self.expect_padata_outer)
+
+    def test_tgs_with_aa_asserted_identity_device_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.aa_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({self.aa_asserted_identity})',
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def test_tgs_with_aa_asserted_identity_both_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.aa_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({self.aa_asserted_identity})',
+                  client_from_rodc=True,
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def test_tgs_without_service_asserted_identity(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({self.service_asserted_identity})',
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=KDC_ERR_POLICY,
+                  status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                  event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                  reason=AuditReason.ACCESS_DENIED,
+                  edata=self.expect_padata_outer)
+
+    def test_tgs_without_service_asserted_identity_client_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({self.service_asserted_identity})',
+                  client_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=KDC_ERR_POLICY,
+                  edata=self.expect_padata_outer)
+
+    def test_tgs_without_service_asserted_identity_device_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({self.service_asserted_identity})',
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def test_tgs_without_service_asserted_identity_both_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({self.service_asserted_identity})',
+                  client_from_rodc=True,
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def test_tgs_with_service_asserted_identity(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.service_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({self.service_asserted_identity})',
+                  client_sids=client_sids,
+                  expected_groups=client_sids)
+
+    def test_tgs_with_service_asserted_identity_client_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.service_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({self.service_asserted_identity})',
+                  client_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=KDC_ERR_POLICY,
+                  edata=self.expect_padata_outer)
+
+    def test_tgs_with_service_asserted_identity_device_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.service_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({self.service_asserted_identity})',
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def test_tgs_with_service_asserted_identity_both_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (self.service_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({self.service_asserted_identity})',
+                  client_from_rodc=True,
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def test_tgs_without_claims_valid(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=KDC_ERR_POLICY,
+                  status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+                  event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+                  reason=AuditReason.ACCESS_DENIED,
+                  edata=self.expect_padata_outer)
+
+    def test_tgs_without_claims_valid_client_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                  client_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=KDC_ERR_POLICY,
+                  edata=self.expect_padata_outer)
+
+    def test_tgs_without_claims_valid_device_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def test_tgs_without_claims_valid_both_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        self._tgs(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                  client_from_rodc=True,
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def test_tgs_with_claims_valid(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                  client_sids=client_sids,
+                  expected_groups=client_sids)
+
+    def test_tgs_with_claims_valid_client_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                  client_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=KDC_ERR_POLICY,
+                  edata=self.expect_padata_outer)
+
+    def test_tgs_with_claims_valid_device_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def test_tgs_with_claims_valid_both_from_rodc(self):
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        self._tgs(f'Member_of SID({security.SID_CLAIMS_VALID})',
+                  client_from_rodc=True,
+                  device_from_rodc=True,
+                  client_sids=client_sids,
+                  expected_groups=client_sids,
+                  code=CRASHES_WINDOWS)
+
+    def _tgs(self,
+             target_policy=None,
+             *,
+             code=0,
+             event=AuditEvent.OK,
+             reason=AuditReason.NONE,
+             status=None,
+             edata=False,
+             client_from_rodc=False,
+             device_from_rodc=False,
+             client_sids=None,
+             client_claims=None,
+             device_sids=None,
+             device_claims=None,
+             expected_groups=None,
+             expected_device_groups=None,
+             expected_claims=None):
+        if code is CRASHES_WINDOWS and not self.crash_windows:
+            self.skipTest('test crashes Windows servers')
+
+        samdb = self.get_samdb()
+        functional_level = self.get_domain_functional_level(samdb)
+
+        if functional_level < dsdb.DS_DOMAIN_FUNCTION_2008:
+            self.skipTest('RBCD requires FL2008')
+
+        domain_sid_str = samdb.get_domain_sid()
+
+        client_creds = self.get_cached_creds(
+            account_type=self.AccountType.USER,
+            opts={
+                'allowed_replication_mock': client_from_rodc,
+                'revealed_to_mock_rodc': client_from_rodc,
+            })
+        client_sid = client_creds.get_sid()
+
+        client_username = client_creds.get_username()
+        client_cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
+                                                 names=[client_username])
+
+        client_tkt_options = 'forwardable'
+        expected_flags = krb5_asn1.TicketFlags(client_tkt_options)
+
+        checksum_key = self.get_krbtgt_checksum_key()
+
+        if client_from_rodc or device_from_rodc:
+            rodc_krbtgt_creds = self.get_mock_rodc_krbtgt_creds()
+            rodc_krbtgt_key = self.TicketDecryptionKey_from_creds(rodc_krbtgt_creds)
+            rodc_checksum_key = {
+                krb5pac.PAC_TYPE_KDC_CHECKSUM: rodc_krbtgt_key,
+            }
+
+        client_tgt = self.get_tgt(client_creds,
+                                  kdc_options=client_tkt_options,
+                                  expected_flags=expected_flags)
+
+        client_modify_pac_fn = []
+        if client_sids is not None:
+            client_modify_pac_fn.append(partial(self.set_pac_sids,
+                                                new_sids=client_sids))
+        if client_claims is not None:
+            client_modify_pac_fn.append(partial(self.set_pac_claims,
+                                                client_claims=client_claims))
+        client_tgt = self.modified_ticket(
+            client_tgt,
+            modify_pac_fn=client_modify_pac_fn,
+            new_ticket_key=rodc_krbtgt_key if client_from_rodc else None,
+            checksum_keys=rodc_checksum_key if client_from_rodc else checksum_key)
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={
+                'allowed_replication_mock': device_from_rodc,
+                'revealed_to_mock_rodc': device_from_rodc,
+            })
+        mach_tgt = self.get_tgt(mach_creds)
+        device_modify_pac_fn = []
+        if device_sids is not None:
+            device_modify_pac_fn.append(partial(self.set_pac_sids,
+                                                new_sids=device_sids))
+        if device_claims is not None:
+            device_modify_pac_fn.append(partial(self.set_pac_claims,
+                                                client_claims=device_claims))
+        mach_tgt = self.modified_ticket(
+            mach_tgt,
+            modify_pac_fn=device_modify_pac_fn,
+            new_ticket_key=rodc_krbtgt_key if device_from_rodc else None,
+            checksum_keys=rodc_checksum_key if device_from_rodc else checksum_key)
+
+        if target_policy is None:
+            policy = None
+            assigned_policy = None
+        else:
+            sddl = f'O:SYD:(XA;;CR;;;WD;({target_policy.format(client_sid=client_creds.get_sid())}))'
+            policy = self.create_authn_policy(enforced=True,
+                                              computer_allowed_to=sddl)
+            assigned_policy = str(policy.dn)
+
+        # Create a target account with the assigned policy.
+        target_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'assigned_policy': assigned_policy})
+
+        target_decryption_key = self.TicketDecryptionKey_from_creds(
+            target_creds)
+        target_etypes = target_creds.tgs_supported_enctypes
+
+        expected_groups = self.map_sids(expected_groups, None, domain_sid_str)
+        expected_device_groups = self.map_sids(expected_device_groups, None, domain_sid_str)
+
+        # Show that obtaining a service ticket is allowed.
+        self._tgs_req(client_tgt, code, client_creds, target_creds,
+                      armor_tgt=mach_tgt,
+                      expected_cname=client_cname,
+                      expected_account_name=client_username,
+                      decryption_key=target_decryption_key,
+                      expected_sid=client_sid,
+                      expected_groups=expected_groups,
+                      expect_device_info=bool(expected_device_groups) or None,
+                      expected_device_domain_sid=domain_sid_str,
+                      expected_device_groups=expected_device_groups,
+                      expect_client_claims=bool(expected_claims) or None,
+                      expected_client_claims=expected_claims,
+                      expected_supported_etypes=target_etypes,
+                      expected_status=status,
+                      expect_edata=edata)
+
+        self.check_tgs_log(client_creds, target_creds,
+                           policy=policy,
+                           checked_creds=client_creds,
+                           status=status,
+                           event=event,
+                           reason=reason)
+
+    def test_conditional_ace_allowed_from_user_deny(self):
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER)
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Create an authentication policy that explicitly denies the machine
+        # account for a user.
+        allowed = 'O:SYD:(A;;CR;;;WD)'
+        denied = f'O:SYD:(XD;;CR;;;{mach_creds.get_sid()};(abc))'
+        policy = self.create_authn_policy(enforced=True,
+                                          user_allowed_from=denied,
+                                          service_allowed_from=allowed)
+
+        # Create a user account with the assigned policy.
+        client_creds = self._get_creds(account_type=self.AccountType.USER,
+                                       assigned_policy=policy)
+
+        # Show that we get a policy error when trying to authenticate.
+        self._get_tgt(client_creds, armor_tgt=mach_tgt,
+                      expected_error=KDC_ERR_POLICY)
+
+        self.check_as_log(
+            client_creds,
+            armor_creds=mach_creds,
+            client_policy=policy,
+            client_policy_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+            event=AuditEvent.KERBEROS_DEVICE_RESTRICTION,
+            reason=AuditReason.ACCESS_DENIED,
+            status=ntstatus.NT_STATUS_INVALID_WORKSTATION)
+
+
+class TgsReqServicePolicyTests(ConditionalAceBaseTests):
+    def test_pac_groups_not_present(self):
+        """Test that authorization succeeds if the client does not belong to
+        some required groups.
+        """
+
+        required_sids = {
+            ('S-1-2-3-4', SidType.EXTRA_SID, self.default_attrs),
+            ('S-1-9-8-7', SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Create an authentication policy that requires the client to belong to
+        # certain groups.
+        target_policy_sddl = self.allow_if(
+            f'Member_of {self.sddl_array_from_sids(required_sids)}')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy)
+
+        # Show that authorization fails.
+        self._tgs_req(
+            client_tgt, KDC_ERR_POLICY, client_creds, target_creds,
+            armor_tgt=mach_tgt,
+            expect_edata=self.expect_padata_outer,
+            # We aren’t particular about whether or not we get an NTSTATUS.
+            expect_status=None,
+            expected_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)
+
+        self.check_tgs_log(
+            client_creds, target_creds,
+            policy=target_policy,
+            status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+            event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+            reason=AuditReason.ACCESS_DENIED)
+
+    def test_pac_groups_present(self):
+        """Test that authorization succeeds if the client belongs to some
+        required groups.
+        """
+
+        required_sids = {
+            ('S-1-2-3-4', SidType.EXTRA_SID, self.default_attrs),
+            ('S-1-9-8-7', SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        client_sids = required_sids | {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Add the required groups to the client’s TGT.
+        client_tgt = self.modified_ticket(
+            client_tgt,
+            modify_pac_fn=partial(self.set_pac_sids,
+                                  new_sids=client_sids),
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Create an authentication policy that requires the client to belong to
+        # certain groups.
+        target_policy_sddl = self.allow_if(
+            f'Member_of {self.sddl_array_from_sids(required_sids)}')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy)
+
+        # Show that authorization succeeds.
+        self._tgs_req(client_tgt, 0, client_creds, target_creds, armor_tgt=mach_tgt)
+
+        self.check_tgs_log(client_creds, target_creds,
+                           policy=target_policy)
+
+    def test_pac_resource_groups_present_to_service_sid_compression(self):
+        """Test that authorization succeeds if the client belongs to some
+        required resource groups, and the request is to a service that supports
+        SID compression.
+        """
+
+        required_sids = {
+            ('S-1-2-3-4', SidType.RESOURCE_SID, self.resource_attrs),
+            ('S-1-2-3-5', SidType.RESOURCE_SID, self.resource_attrs),
+            ('S-1-2-3-6', SidType.RESOURCE_SID, self.resource_attrs),
+        }
+
+        client_sids = required_sids | {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Add the required groups to the client’s TGT.
+        client_tgt = self.modified_ticket(
+            client_tgt,
+            modify_pac_fn=partial(self.set_pac_sids,
+                                  new_sids=client_sids),
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Create an authentication policy that requires the client to belong to
+        # certain groups.
+        target_policy_sddl = self.allow_if(
+            f'Member_of {self.sddl_array_from_sids(required_sids)}')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy)
+
+        # Show that authorization fails.
+        self._tgs_req(
+            client_tgt, KDC_ERR_POLICY, client_creds, target_creds,
+            armor_tgt=mach_tgt,
+            expect_edata=self.expect_padata_outer,
+            # We aren’t particular about whether or not we get an NTSTATUS.
+            expect_status=None,
+            expected_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)
+
+        self.check_tgs_log(
+            client_creds, target_creds,
+            policy=target_policy,
+            status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+            event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+            reason=AuditReason.ACCESS_DENIED)
+
+    def test_pac_resource_groups_present_to_service_no_sid_compression(self):
+        """Test that authorization succeeds if the client belongs to some
+        required resource groups, and the request is to a service that does not
+        support SID compression.
+        """
+
+        required_sids = {
+            ('S-1-2-3-4', SidType.RESOURCE_SID, self.resource_attrs),
+            ('S-1-2-3-5', SidType.RESOURCE_SID, self.resource_attrs),
+            ('S-1-2-3-6', SidType.RESOURCE_SID, self.resource_attrs),
+        }
+
+        client_sids = required_sids | {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Add the required groups to the client’s TGT.
+        client_tgt = self.modified_ticket(
+            client_tgt,
+            modify_pac_fn=partial(self.set_pac_sids,
+                                  new_sids=client_sids),
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Create an authentication policy that requires the client to belong to
+        # certain groups.
+        target_policy_sddl = self.allow_if(
+            f'Member_of {self.sddl_array_from_sids(required_sids)}')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy,
+                                       additional_details={
+                                           'msDS-SupportedEncryptionTypes': str((
+                                               security.KERB_ENCTYPE_RC4_HMAC_MD5) | (
+                                                   security.KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96_SK) | (
+                                                       security.KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED))})
+
+        # Show that authorization fails.
+        self._tgs_req(
+            client_tgt, KDC_ERR_POLICY, client_creds, target_creds,
+            armor_tgt=mach_tgt,
+            expect_edata=self.expect_padata_outer,
+            # We aren’t particular about whether or not we get an NTSTATUS.
+            expect_status=None,
+            expected_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)
+
+        self.check_tgs_log(
+            client_creds, target_creds,
+            policy=target_policy,
+            status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+            event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+            reason=AuditReason.ACCESS_DENIED)
+
+    def test_pac_well_known_groups_not_present(self):
+        """Test that authorization fails if the client does not belong to one
+        or more required well‐known groups.
+        """
+
+        required_sids = {
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs),
+            (security.SID_COMPOUNDED_AUTHENTICATION, SidType.EXTRA_SID, self.default_attrs),
+            (self.aa_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+            (self.service_asserted_identity, SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Modify the client’s TGT to contain only the SID of the client’s
+        # primary group.
+        client_tgt = self.modified_ticket(
+            client_tgt,
+            modify_pac_fn=partial(self.set_pac_sids,
+                                  new_sids=client_sids),
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Create an authentication policy that requires the client to belong to
+        # certain groups.
+        target_policy_sddl = self.allow_if(
+            f'Member_of_any {self.sddl_array_from_sids(required_sids)}')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy)
+
+        # Show that authorization fails.
+        self._tgs_req(
+            client_tgt, KDC_ERR_POLICY, client_creds, target_creds,
+            armor_tgt=mach_tgt,
+            expect_edata=self.expect_padata_outer,
+            # We aren’t particular about whether or not we get an NTSTATUS.
+            expect_status=None,
+            expected_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)
+
+        self.check_tgs_log(
+            client_creds, target_creds,
+            policy=target_policy,
+            status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+            event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+            reason=AuditReason.ACCESS_DENIED)
+
+    def test_pac_device_info(self):
+        self._run_pac_device_info_test()
+
+    def test_pac_device_info_no_compound_id_support(self):
+        self._run_pac_device_info_test(compound_id_support=False)
+
+    def test_pac_device_info_no_claims_valid(self):
+        self._run_pac_device_info_test(device_claims_valid=False)
+
+    def _run_pac_device_info_test(self, compound_id_support=True, device_claims_valid=True):
+        """Test the groups of the client and the device after performing a
+        FAST‐armored TGS‐REQ.
+        """
+
+        client_claim_id = 'the name of the client’s client claim'
+        client_claim_value = 'the value of the client’s client claim'
+
+        client_claims = [
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                (client_claim_id, claims.CLAIM_TYPE_STRING, [client_claim_value]),
+            ]),
+        ]
+
+        expected_client_claims = {
+            client_claim_id: {
+                'source_type': claims.CLAIMS_SOURCE_TYPE_AD,
+                'type': claims.CLAIM_TYPE_STRING,
+                'values': (client_claim_value,),
+            },
+        }
+
+        device_claim_id = 'the name of the device’s client claim'
+        device_claim_value = 'the value of the device’s client claim'
+
+        device_claims = [
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                (device_claim_id, claims.CLAIM_TYPE_STRING, [device_claim_value]),
+            ]),
+        ]
+
+        if compound_id_support:
+            expected_device_claims = {
+                device_claim_id: {
+                    'source_type': claims.CLAIMS_SOURCE_TYPE_AD,
+                    'type': claims.CLAIM_TYPE_STRING,
+                    'values': (device_claim_value,),
+                },
+            }
+        else:
+            expected_device_claims = None
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            # This to ensure we have EXTRA_SIDS set already, as
+            # windows won't set that flag otherwise when adding one
+            # more
+            ('S-1-2-3-4', SidType.EXTRA_SID, self.default_attrs),
+        }
+
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            ('S-1-2-3-4', SidType.EXTRA_SID, self.resource_attrs),
+            ('S-1-3-4-5', SidType.EXTRA_SID, self.resource_attrs),
+        }
+
+        if device_claims_valid:
+            device_sids.add((security.SID_CLAIMS_VALID, SidType.EXTRA_SID, self.default_attrs))
+
+        # Modify the machine account’s TGT to contain only the SID of the
+        # machine account’s primary group.
+        mach_tgt = self.modified_ticket(
+            mach_tgt,
+            modify_pac_fn=[
+                partial(self.set_pac_sids,
+                        new_sids=device_sids),
+                partial(self.set_pac_claims, client_claims=device_claims),
+            ],
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Modify the client’s TGT to contain only the SID of the client’s
+        # primary group.
+        client_tgt = self.modified_ticket(
+            client_tgt,
+            modify_pac_fn=[
+                partial(self.set_pac_sids,
+                        new_sids=client_sids),
+                partial(self.set_pac_claims, client_claims=client_claims),
+            ],
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Indicate that Compound Identity is supported.
+        target_creds, _ = self.get_target(to_krbtgt=False, compound_id=compound_id_support)
+
+        expected_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            ('S-1-2-3-4', SidType.EXTRA_SID, self.default_attrs),
+            # The client’s groups are not to include the Asserted Identity and
+            # Claims Valid SIDs.
+        }
+
+        if compound_id_support:
+            expected_sids.add((security.SID_COMPOUNDED_AUTHENTICATION, SidType.EXTRA_SID, self.default_attrs))
+
+            expected_device_sids = {
+                (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+                (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+                ('S-1-2-3-4', SidType.EXTRA_SID, self.resource_attrs),
+                ('S-1-3-4-5', SidType.EXTRA_SID, self.resource_attrs),
+            }
+
+            if device_claims_valid:
+                expected_device_sids.add(frozenset([(security.SID_CLAIMS_VALID, SidType.RESOURCE_SID, self.default_attrs)]))
+        else:
+            expected_device_sids = None
+
+        samdb = self.get_samdb()
+        domain_sid_str = samdb.get_domain_sid()
+
+        expected_sids = self.map_sids(expected_sids, None, domain_sid_str)
+        # The device SIDs will be put into the PAC unmodified.
+        expected_device_sids = self.map_sids(expected_device_sids, None, domain_sid_str)
+
+        # Show that authorization succeeds.
+        self._tgs_req(client_tgt, 0, client_creds, target_creds, armor_tgt=mach_tgt,
+                      expected_groups=expected_sids,
+                      expect_device_info=bool(expected_device_sids) or None,
+                      expected_device_domain_sid=domain_sid_str,
+                      expected_device_groups=expected_device_sids,
+                      expect_client_claims=bool(expected_client_claims) or None,
+                      expected_client_claims=expected_client_claims,
+                      expect_device_claims=bool(expected_device_claims) or None,
+                      expected_device_claims=expected_device_claims)
+
+        self.check_tgs_log(client_creds, target_creds)
+
+    def test_pac_extra_sids_behaviour(self):
+        """Test the groups of the client and the device after performing a
+        FAST‐armored TGS‐REQ.
+        """
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        client_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Modify the client’s TGT to contain only the SID of the client’s
+        # primary group.
+        client_tgt = self.modified_ticket(
+            client_tgt,
+            modify_pac_fn=partial(self.set_pac_sids,
+                                  new_sids=client_sids),
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Indicate that Compound Identity is supported.
+        target_creds, _ = self.get_target(to_krbtgt=False, compound_id=True)
+
+        expected_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            (security.SID_COMPOUNDED_AUTHENTICATION, SidType.EXTRA_SID, self.default_attrs)
+            # The client’s groups are not to include the Asserted Identity and
+            # Claims Valid SIDs.
+        }
+
+        samdb = self.get_samdb()
+        domain_sid_str = samdb.get_domain_sid()
+
+        expected_sids = self.map_sids(expected_sids, None, domain_sid_str)
+
+        # Show that authorization succeeds.
+        self._tgs_req(client_tgt, 0, client_creds, target_creds, armor_tgt=mach_tgt,
+                      expected_groups=expected_sids)
+
+        self.check_tgs_log(client_creds, target_creds)
+
+    def test_pac_claims_not_present(self):
+        """Test that authentication fails if the device does not have a
+        required claim.
+        """
+
+        claim_id = 'the name of the claim'
+        claim_value = 'the value of the claim'
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Create an authentication policy that requires the device to have a
+        # certain claim.
+        target_policy_sddl = self.allow_if(
+            f'@User.{self.escaped_claim_id(claim_id)} == "{claim_value}"')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy)
+
+        # Show that authorization fails.
+        self._tgs_req(
+            client_tgt, KDC_ERR_POLICY, client_creds, target_creds,
+            armor_tgt=mach_tgt,
+            expect_edata=self.expect_padata_outer,
+            # We aren’t particular about whether or not we get an NTSTATUS.
+            expect_status=None,
+            expected_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)
+
+        self.check_tgs_log(
+            client_creds,
+            target_creds,
+            policy=target_policy,
+            status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+            event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+            reason=AuditReason.ACCESS_DENIED)
+
+    def test_pac_claims_present(self):
+        """Test that authentication succeeds if the user has a required
+        claim.
+        """
+
+        claim_id = 'the name of the claim'
+        claim_value = 'the value of the claim'
+
+        pac_claims = [
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                (claim_id, claims.CLAIM_TYPE_STRING, [claim_value]),
+            ]),
+        ]
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Create an authentication policy that requires the user to have a
+        # certain claim.
+        target_policy_sddl = self.allow_if(
+            f'@User.{self.escaped_claim_id(claim_id)} == "{claim_value}"')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Add the required claim to the client’s TGT.
+        client_tgt = self.modified_ticket(
+            client_tgt,
+            modify_pac_fn=partial(self.set_pac_claims,
+                                  client_claims=pac_claims),
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy)
+
+        # Show that authorization succeeds.
+        self._tgs_req(client_tgt, 0, client_creds, target_creds, armor_tgt=mach_tgt)
+
+        self.check_tgs_log(client_creds, target_creds,
+                           policy=target_policy)
+
+    def test_pac_claims_invalid(self):
+        """Test that authentication fails if the device’s required claim is not
+        valid.
+        """
+
+        claim_id = 'the name of the claim'
+        claim_value = 'the value of the claim'
+
+        pac_claims = [
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                (claim_id, claims.CLAIM_TYPE_STRING, [claim_value]),
+            ]),
+        ]
+
+        # The device’s SIDs do not include the Claims Valid SID.
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Create an authentication policy that requires the device to have a
+        # certain claim.
+        target_policy_sddl = self.allow_if(
+            f'@User.{self.escaped_claim_id(claim_id)} == "{claim_value}"')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Add the SIDs and the required claim to the client’s TGT.
+        client_tgt = self.modified_ticket(
+            client_tgt,
+            modify_pac_fn=[
+                partial(self.set_pac_claims, client_claims=pac_claims),
+                partial(self.set_pac_sids, new_sids=device_sids)],
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy)
+
+        # Show that authorization fails.
+        self._tgs_req(
+            client_tgt, KDC_ERR_POLICY, client_creds, target_creds,
+            armor_tgt=mach_tgt,
+            expect_edata=self.expect_padata_outer,
+            # We aren’t particular about whether or not we get an NTSTATUS.
+            expect_status=None,
+            expected_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)
+
+        self.check_tgs_log(
+            client_creds,
+            target_creds,
+            policy=target_policy,
+            status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+            event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+            reason=AuditReason.ACCESS_DENIED)
+
+    def test_pac_device_claims_not_present(self):
+        """Test that authorization fails if the device does not have a
+        required claim.
+        """
+
+        claim_id = 'the name of the claim'
+        claim_value = 'the value of the claim'
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Create an authentication policy that requires the device to have a
+        # certain device claim.
+        target_policy_sddl = self.allow_if(
+            f'@Device.{self.escaped_claim_id(claim_id)} == "{claim_value}"')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy)
+
+        # Show that authorization fails.
+        self._tgs_req(
+            client_tgt, KDC_ERR_POLICY, client_creds, target_creds,
+            armor_tgt=mach_tgt,
+            expect_edata=self.expect_padata_outer,
+            # We aren’t particular about whether or not we get an NTSTATUS.
+            expect_status=None,
+            expected_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)
+
+        self.check_tgs_log(
+            client_creds,
+            target_creds,
+            policy=target_policy,
+            status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+            event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+            reason=AuditReason.ACCESS_DENIED)
+
+    def test_pac_device_claims_present(self):
+        """Test that authorization succeeds if the device has a required claim.
+        """
+
+        claim_id = 'the name of the claim'
+        claim_value = 'the value of the claim'
+
+        pac_claims = [
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                (claim_id, claims.CLAIM_TYPE_STRING, [claim_value]),
+            ]),
+        ]
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Add the required claim to the machine account’s TGT.
+        mach_tgt = self.modified_ticket(
+            mach_tgt,
+            modify_pac_fn=partial(self.set_pac_claims,
+                                  client_claims=pac_claims),
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Create an authentication policy that requires the device to have a
+        # certain device claim.
+        target_policy_sddl = self.allow_if(
+            f'@Device.{self.escaped_claim_id(claim_id)} == "{claim_value}"')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy)
+
+        # Show that authorization succeeds.
+        self._tgs_req(client_tgt, 0, client_creds, target_creds, armor_tgt=mach_tgt)
+
+        self.check_tgs_log(client_creds, target_creds,
+                           policy=target_policy)
+
+    def test_pac_device_claims_invalid(self):
+        """Test that authorization fails if the device’s required claim is not
+        valid.
+        """
+
+        claim_id = 'the name of the claim'
+        claim_value = 'the value of the claim'
+
+        pac_claims = [
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                (claim_id, claims.CLAIM_TYPE_STRING, [claim_value]),
+            ]),
+        ]
+
+        # The device’s SIDs do not include the Claims Valid SID.
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+        }
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Add the SIDs and the required claim to the machine account’s TGT.
+        mach_tgt = self.modified_ticket(
+            mach_tgt,
+            modify_pac_fn=[
+                partial(self.set_pac_claims, client_claims=pac_claims),
+                partial(self.set_pac_sids, new_sids=device_sids)],
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Create an authentication policy that requires the device to have a
+        # certain claim.
+        target_policy_sddl = self.allow_if(
+            f'@Device.{self.escaped_claim_id(claim_id)} == "{claim_value}"')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy)
+
+        # Show that authorization fails.
+        self._tgs_req(
+            client_tgt, KDC_ERR_POLICY, client_creds, target_creds,
+            armor_tgt=mach_tgt,
+            expect_edata=self.expect_padata_outer,
+            # We aren’t particular about whether or not we get an NTSTATUS.
+            expect_status=None,
+            expected_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED)
+
+        self.check_tgs_log(
+            client_creds,
+            target_creds,
+            policy=target_policy,
+            status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED,
+            event=AuditEvent.KERBEROS_SERVER_RESTRICTION,
+            reason=AuditReason.ACCESS_DENIED)
+
+    def test_pac_device_claims_invalid_no_attrs(self):
+        """Test that authorization fails if the device’s required claim is not
+        valid.
+        """
+
+        claim_id = 'the name of the claim'
+        claim_value = 'the value of the claim'
+
+        pac_claims = [
+            (claims.CLAIMS_SOURCE_TYPE_AD, [
+                (claim_id, claims.CLAIM_TYPE_STRING, [claim_value]),
+            ]),
+        ]
+
+        device_sids = {
+            (security.DOMAIN_RID_USERS, SidType.BASE_SID, self.default_attrs),
+            (security.DOMAIN_RID_USERS, SidType.PRIMARY_GID, None),
+            # The device’s SIDs include the Claims Valid SID, but it has no
+            # attributes.
+            (security.SID_CLAIMS_VALID, SidType.EXTRA_SID, 0),
+        }
+
+        # Create a machine account with which to perform FAST.
+        mach_creds = self.get_cached_creds(
+            account_type=self.AccountType.COMPUTER,
+            opts={'id': 'device'})
+        mach_tgt = self.get_tgt(mach_creds)
+
+        # Add the SIDs and the required claim to the machine account’s TGT.
+        mach_tgt = self.modified_ticket(
+            mach_tgt,
+            modify_pac_fn=[
+                partial(self.set_pac_claims, client_claims=pac_claims),
+                partial(self.set_pac_sids, new_sids=device_sids)],
+            checksum_keys=self.get_krbtgt_checksum_key())
+
+        # Create an authentication policy that requires the device to have a
+        # certain claim.
+        target_policy_sddl = self.allow_if(
+            f'@Device.{self.escaped_claim_id(claim_id)} == "{claim_value}"')
+        target_policy = self.create_authn_policy(
+            enforced=True, computer_allowed_to=target_policy_sddl)
+
+        # Create a user account.
+        client_creds = self._get_creds(account_type=self.AccountType.USER)
+        client_tgt = self.get_tgt(client_creds)
+
+        # Create a target account with the assigned policy.
+        target_creds = self._get_creds(account_type=self.AccountType.COMPUTER,
+                                       assigned_policy=target_policy)
+
+        # Show that authorization succeeds.
+        self._tgs_req(client_tgt, 0, client_creds, target_creds, armor_tgt=mach_tgt)
+
+        self.check_tgs_log(client_creds, target_creds,
+                           policy=target_policy)
+
+
+if __name__ == '__main__':
+    global_asn1_print = False
+    global_hexdump = False
+    import unittest
+    unittest.main()
index 27c7ee38cc6401b1c42377e73022a4630fc21854..b4a21f8500a2a7c1c33b01894d38549c08d63b05 100755 (executable)
@@ -193,6 +193,16 @@ class KdcTgsBaseTests(KDCBaseTest):
                  expect_requester_sid=None,
                  expect_edata=False,
                  expected_sid=None,
+                 expected_groups=None,
+                 unexpected_groups=None,
+                 expect_device_info=None,
+                 expected_device_domain_sid=None,
+                 expected_device_groups=None,
+                 expect_client_claims=None,
+                 expected_client_claims=None,
+                 unexpected_client_claims=None,
+                 expect_device_claims=None,
+                 expected_device_claims=None,
                  expect_status=None,
                  expected_status=None,
                  expected_proxy_target=None,
@@ -303,6 +313,16 @@ class KdcTgsBaseTests(KDCBaseTest):
             expect_pac_attrs_pac_request=expect_pac_attrs_pac_request,
             expect_requester_sid=expect_requester_sid,
             expected_sid=expected_sid,
+            expected_groups=expected_groups,
+            unexpected_groups=unexpected_groups,
+            expect_device_info=expect_device_info,
+            expected_device_domain_sid=expected_device_domain_sid,
+            expected_device_groups=expected_device_groups,
+            expect_client_claims=expect_client_claims,
+            expected_client_claims=expected_client_claims,
+            unexpected_client_claims=unexpected_client_claims,
+            expect_device_claims=expect_device_claims,
+            expected_device_claims=expected_device_claims,
             expected_proxy_target=expected_proxy_target,
             expected_transited_services=expected_transited_services,
             check_patypes=check_patypes)
index 975ab55da0dfd4eade22f692127d573f06da46ba..07bb1e94363e7270cea6b7248947daca7242a62d 100644 (file)
 ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_sha256_certificate_signature_win2k.ad_dc
 ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_sha256_signature_win2k.ad_dc
 ^samba.tests.krb5.pkinit_tests.samba.tests.krb5.pkinit_tests.PkInitTests.test_pkinit_win2k.ad_dc
+#
+# Conditional ACE tests
+#
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_claim_equals_claim\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_enforced_silo_equals\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_enforced_silo_not_equals_deny\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_unenforced_silo_equals_deny\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_unenforced_silo_not_equals\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_to_client_equals\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_to_device_equals\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_42_equals_literal__42_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_A_is_less_than__\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__123_456__equals_literal__123_456_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__apple_banana__equals_literal__APPLE_BANANA_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__apple_banana__equals_literal__BANANA_APPLE_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__apple_banana__equals_literal__apple_banana_apple_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains_FOO\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains__foo_bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains_literal__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains_literal__foo_bar_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains_literal__foo_bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__does_not_contain__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__does_not_contain__foo_bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__does_not_contain_literal__foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__does_not_equal__foo_bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__does_not_equal_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__equals__FOO_BAR_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__equals__bar_foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__equals__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__equals__foo_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_any_of_BAR\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_any_of__bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_any_of_baz\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_any_of_literal__bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_any_of_literal__baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_none_of__bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_none_of_baz\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_a_is_less_than__\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_bar_contains_literal__bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_bar_equals_literal__bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_bar_matches_any_of_literal__bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_cat_exceeds_dog\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_cat_exceeds_or_equals_dog\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_cat_is_less_than_dog\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_cat_is_less_than_or_equals_dog\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_cat_is_less_than_ćàț\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_bar_equals_Foo_BAR\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_bar_equals_literal__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_bar_exceeds_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_contains_literal__foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_does_not_equal_bar\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_does_not_equal_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_equals_bar\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_equals_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_exceeds_or_equals_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_is_less_than_foo_bar\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_is_less_than_or_equals_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_is_less_than_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_matches_any_of_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ß_exceeds_SS\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ß_is_less_than_ẞ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ćàș_is_less_than_ĆÀȚ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ćàț_equals_ĆÀȚ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ɜ_is_less_than_Ɜ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ʞ_is_less_than_ʟ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ʞ_is_less_than_Ʞ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ԛԣ_equals_ԚԢ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ḽ_equals_Ḽ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ⅸ_equals_Ⅸ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ⱦ_equals_Ⱦ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ⳬ_exceeds_Ⳬ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ꙭ_equals_Ꙭ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ꞧ_exceeds_Ꞧ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ퟻ_is_less_than_豈\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ퟻ_is_less_than_𐀀\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_bar_equals_FOO_BAR\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_𐀀_is_less_than_豈\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_1000_unicode_3_a___1000_unicode_equals_a
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_1_180388626432___a_equals_180388626432\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_1_42_42_42___a_equals_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_1_42___a_equals_42\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_0___a_equals_3\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_1_2_3___a_equals_1_2_3_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_4294967296___a_exceeds_0\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_42_42___a_equals_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_42___a_equals_42\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_4_5_6___a_does_not_equal_1_2_3_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_3_FOO_foo___a_equals_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_3_foo_bar___a_equals_foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_3_foo_bar_b_3_FOO_BAR___a_equals_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_3_foo_foo___a_equals_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_3_this_is_not_the_value_a_3
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0_0___a_equals_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0__not_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0__not_a_and_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0__not_a_or_not_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0__not_not_not_not_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0_b_6_0___a_and_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0_b_6_0___a_or_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0_b_6_1___a_and_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1___a_or_a_or_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1__not_a_or_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1_b_6_0___a_and_not_b_or_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1_b_6_0___a_or_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1_b_6_1___a_and_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1_b_6_1___a_equals_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1_b_6_1___a_or_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_2_b_6_3___a_equals_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_b_3_FOO_BAR_BAZ_a_3_foo_bar_baz___a_does_not_equal_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_b_6_1___b_or_b_or_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_dotty_claim_3_a___dotty_claim_equals_a___dotty_claim_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_dup_3_foo_dup_3_foo_2_dup_2_42_dup_2_42_2_dup_3_foo_dup_3_foo_dup_3_foo_bar_dup_3_foo_bar___dup_equals_dup_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_empty_string_3___empty_string_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_escaped_claim_3_claim_value___escaped_claim_equals_claim_value___escaped_claim_foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_false_and_true_boolean_6_0_1___false_and_true_boolean_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_false_boolean_6_0___false_boolean_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_false_booleans_6_0_0___false_booleans_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_higher_unicode_3_a___higher_unicode_equals_a
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_invalid_octet_string_16___invalid_octet_string_equals_invalid_octet_string_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_invalid_sid_5___invalid_sid_equals_invalid_sid_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_large_claim_3_zzzzzzzzzzzzzzzzzzzz
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_larger_claim_3_zzzzzzzzzzzzzzzzzzz
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_long_name_3_a___long_name_equals_a
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_many_claims_2_0_1_2_3_4_5_6_7_8_9_10
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_non_empty_string_3_foo_bar___non_empty_string_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_nonzero_int_1_1___nonzero_int_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_nonzero_uint_2_1___nonzero_uint_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_one_and_zero_int_1_1_0___one_and_zero_int_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_one_and_zero_uint_2_1_0___one_and_zero_uint_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_true_and_false_boolean_6_1_0___true_and_false_boolean_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_true_boolean_6_1___true_boolean_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_and_one_int_1_0_1___zero_and_one_int_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_and_one_uint_2_0_1___zero_and_one_uint_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_int_1_0___zero_int_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_ints_1_0_0___zero_ints_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_uint_2_0___zero_uint_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_uints_2_0_0___zero_uints_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__2_a_3_foo___a_equals_foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_all_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_and_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_and_service_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_with_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_with_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_with_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_and_service_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_with_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_with_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_with_compounded_auth\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_with_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_service_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_with_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_with_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_with_compounded_authentication\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_with_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_aa_asserted_identity_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_aa_asserted_identity_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_aa_asserted_identity_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_claims_valid_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_claims_valid_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_claims_valid_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_service_asserted_identity_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_service_asserted_identity_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_service_asserted_identity_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_aa_asserted_identity_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_aa_asserted_identity_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_aa_asserted_identity_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_claims_valid_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_claims_valid_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_claims_valid_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_service_asserted_identity_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_service_asserted_identity_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_service_asserted_identity_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_claims_present\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_device_claims_invalid_no_attrs\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_device_claims_present\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_device_info_no_compound_id_support\(ad_dc\)
index cc0292ff28ec539bf9539242ef7c51c20275c92a..d241b61581104c007003c9d2173e63125ae5689d 100644 (file)
@@ -3807,3 +3807,251 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
 # with the last password, see https://bugzilla.samba.org/show_bug.cgi?id=14054
 #
 ^samba4.ldap.login_basics.python.*.__main__.BasicUserAuthTests.test_login_basics_krb5
+#
+# Conditional ACE tests
+#
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_claim_equals_claim\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_enforced_silo_equals\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_enforced_silo_not_equals_deny\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_unenforced_silo_equals_deny\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_unenforced_silo_not_equals\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_to_client_equals\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_to_device_equals\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_42_equals_literal__42_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_A_is_less_than__\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__123_456__equals_literal__123_456_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__apple_banana__equals_literal__APPLE_BANANA_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__apple_banana__equals_literal__BANANA_APPLE_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__apple_banana__equals_literal__apple_banana_apple_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains_FOO\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains__foo_bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains_literal__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains_literal__foo_bar_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains_literal__foo_bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__does_not_contain__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__does_not_contain__foo_bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__does_not_contain_literal__foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__does_not_equal__foo_bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__does_not_equal_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__equals__FOO_BAR_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__equals__bar_foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__equals__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__equals__foo_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_any_of_BAR\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_any_of__bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_any_of_baz\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_any_of_literal__bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_any_of_literal__baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_none_of__bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_none_of_baz\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_a_is_less_than__\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_bar_contains_literal__bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_bar_equals_literal__bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_bar_matches_any_of_literal__bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_cat_exceeds_dog\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_cat_exceeds_or_equals_dog\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_cat_is_less_than_dog\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_cat_is_less_than_or_equals_dog\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_cat_is_less_than_ćàț\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_bar_equals_Foo_BAR\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_bar_equals_literal__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_bar_exceeds_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_contains_literal__foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_does_not_equal_bar\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_does_not_equal_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_equals_bar\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_equals_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_exceeds_or_equals_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_is_less_than_foo_bar\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_is_less_than_or_equals_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_is_less_than_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_matches_any_of_foo\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ß_exceeds_SS\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ß_is_less_than_ẞ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ćàș_is_less_than_ĆÀȚ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ćàț_equals_ĆÀȚ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ɜ_is_less_than_Ɜ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ʞ_is_less_than_ʟ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ʞ_is_less_than_Ʞ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ԛԣ_equals_ԚԢ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ḽ_equals_Ḽ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ⅸ_equals_Ⅸ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ⱦ_equals_Ⱦ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ⳬ_exceeds_Ⳬ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ꙭ_equals_Ꙭ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ꞧ_exceeds_Ꞧ\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ퟻ_is_less_than_豈\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_ퟻ_is_less_than_𐀀\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_bar_equals_FOO_BAR\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_𐀀_is_less_than_豈\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_1000_unicode_3_a___1000_unicode_equals_a
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_1_180388626432___a_equals_180388626432\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_1_42_42_42___a_equals_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_1_42___a_equals_42\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_0___a_equals_3\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_1_2_3___a_equals_1_2_3_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_4294967296___a_exceeds_0\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_42_42___a_equals_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_42___a_equals_42\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_2_4_5_6___a_does_not_equal_1_2_3_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_3_FOO_foo___a_equals_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_3_foo_bar___a_equals_foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_3_foo_bar_b_3_FOO_BAR___a_equals_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_3_foo_foo___a_equals_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_3_this_is_not_the_value_a_3
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0_0___a_equals_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0__not_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0__not_a_and_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0__not_a_or_not_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0__not_not_not_not_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0_b_6_0___a_and_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0_b_6_0___a_or_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_0_b_6_1___a_and_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1___a_or_a_or_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1__not_a_or_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1_b_6_0___a_and_not_b_or_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1_b_6_0___a_or_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1_b_6_1___a_and_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1_b_6_1___a_equals_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1_b_6_1___a_or_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_2_b_6_3___a_equals_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_b_3_FOO_BAR_BAZ_a_3_foo_bar_baz___a_does_not_equal_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_b_6_1___b_or_b_or_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_dotty_claim_3_a___dotty_claim_equals_a___dotty_claim_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_dup_3_foo_dup_3_foo_2_dup_2_42_dup_2_42_2_dup_3_foo_dup_3_foo_dup_3_foo_bar_dup_3_foo_bar___dup_equals_dup_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_empty_string_3___empty_string_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_escaped_claim_3_claim_value___escaped_claim_equals_claim_value___escaped_claim_foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_false_and_true_boolean_6_0_1___false_and_true_boolean_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_false_boolean_6_0___false_boolean_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_false_booleans_6_0_0___false_booleans_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_higher_unicode_3_a___higher_unicode_equals_a
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_invalid_octet_string_16___invalid_octet_string_equals_invalid_octet_string_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_invalid_sid_5___invalid_sid_equals_invalid_sid_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_large_claim_3_zzzzzzzzzzzzzzzzzzzz
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_larger_claim_3_zzzzzzzzzzzzzzzzzzz
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_long_name_3_a___long_name_equals_a
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_many_claims_2_0_1_2_3_4_5_6_7_8_9_10
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_non_empty_string_3_foo_bar___non_empty_string_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_nonzero_int_1_1___nonzero_int_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_nonzero_uint_2_1___nonzero_uint_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_one_and_zero_int_1_1_0___one_and_zero_int_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_one_and_zero_uint_2_1_0___one_and_zero_uint_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_true_and_false_boolean_6_1_0___true_and_false_boolean_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_true_boolean_6_1___true_boolean_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_and_one_int_1_0_1___zero_and_one_int_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_and_one_uint_2_0_1___zero_and_one_uint_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_int_1_0___zero_int_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_ints_1_0_0___zero_ints_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_uint_2_0___zero_uint_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_zero_uints_2_0_0___zero_uints_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__2_a_3_foo___a_equals_foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_all_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_and_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_and_service_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_with_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_with_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_with_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_and_service_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_with_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_with_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_with_compounded_auth\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_with_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_service_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_with_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_with_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_with_compounded_authentication\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_with_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_aa_asserted_identity_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_aa_asserted_identity_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_aa_asserted_identity_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_claims_valid_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_claims_valid_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_claims_valid_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_service_asserted_identity_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_service_asserted_identity_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_with_service_asserted_identity_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_aa_asserted_identity_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_aa_asserted_identity_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_aa_asserted_identity_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_claims_valid_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_claims_valid_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_claims_valid_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_service_asserted_identity_both_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_service_asserted_identity_client_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_service_asserted_identity_device_from_rodc\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_claims_present\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_device_claims_invalid_no_attrs\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_device_claims_present\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_device_info_no_compound_id_support\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_enforced_silo_equals_deny\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_enforced_silo_not_equals\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_member_of_any\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_member_of_any_deny\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_member_of_each\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_member_of_each_deny\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_not_member_of_any\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_not_member_of_any_deny\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_not_member_of_each\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_not_member_of_each_deny\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_unenforced_silo_equals\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_allowed_from_unenforced_silo_not_equals_deny\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_42_equals_literal_42\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__654_321__equals_literal__654_321_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp___contains_literal__foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp___does_not_contain_literal__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp___does_not_equal__\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp___equals__\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp___equals_b\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__contains_literal__1_2_3_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__exceeds__foo_bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__exceeds_or_equals__foo_bar_baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__is_less_than__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__is_less_than_or_equals__foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp__foo_bar__matches_none_of_literal__baz_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_a_equals__\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_cmp_foo_matches_any_of_literal__foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_conditional_ace_allowed_from_user_deny\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__0_a_3_foo___a_equals_foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1___a_equals_42\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1___a_or_not_a_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_a_6_1_b_6_0___a_and_not_b_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_empty_array_1_empty_array_2_empty_array_6_empty_array_3___empty_array_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_empty_claim_boolean_6__there_exists_empty_claim_boolean_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__1_empty_claim_string_3___empty_claim_string_does_not_equal_foo_bar_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_pac_claim_cmp__3_a_3_foo___a_equals_foo_\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_with_compounded_authentication\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_without_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_without_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_without_compounded_authentication\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_client_without_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_without_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_without_aa_asserted_identity_not_memberof\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_without_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_without_compounded_auth\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_device_without_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_without_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_without_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_without_compounded_authentication\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_rbcd_without_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_aa_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.ConditionalAceTests.test_tgs_without_service_asserted_identity\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_claims_invalid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_claims_not_present\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_device_claims_invalid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_device_claims_not_present\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_device_info\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_device_info_no_claims_valid\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_extra_sids_behaviour\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_groups_not_present\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_groups_present\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_resource_groups_present_to_service_no_sid_compression\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_resource_groups_present_to_service_sid_compression\(ad_dc\)
+^samba.tests.krb5.conditional_ace_tests.samba.tests.krb5.conditional_ace_tests.TgsReqServicePolicyTests.test_pac_well_known_groups_not_present\(ad_dc\)
index 73f7b1d067a45f06e324f3091b78c295aa2cc040..41cbffa2063aa3196338541c08b2cad6fdd23a23 100755 (executable)
@@ -2018,6 +2018,10 @@ planoldpythontestsuite(
     'ad_dc',
     'samba.tests.krb5.pkinit_tests',
     environ=krb5_environ)
+planoldpythontestsuite(
+    'ad_dc',
+    'samba.tests.krb5.conditional_ace_tests',
+    environ=krb5_environ)
 
 for env in [
         'vampire_dc',