[SCM] Samba Shared Repository - branch master updated

Andrew Bartlett abartlet at samba.org
Thu Sep 28 04:36:02 UTC 2023


The branch, master has been updated
       via  08b9d5c7b9f tests/krb5: Add samba.tests.krb5.conditional_ace_tests
       via  0e7e46c396b tests/krb5: Add method to replace client or device claims in a PAC
       via  6f5368dd326 tests/krb5: Add method to replace the device SIDs in a PAC
       via  2d0bdb5ce92 tests/krb5: Have set_pac_sids() accept lone RIDs as well as full SIDs
       via  cc1dd00d0fb tests/krb5: Make optional ‘domain_sid’ parameter to set_pac_sids()
       via  9fb0380cb82 tests/krb5: Make optional ‘user_rid’ parameter to set_pac_sids()
       via  34e721030df tests/krb5: Make set_pac_sids() parameters keyword‐only
       via  d6ec0e4f405 tests/krb5: Allow passing mapping=None to map_to_sid()
       via  dfd2027d7e5 tests/krb5: Don’t bother regenerating the PAC if modify_pac_fn or update_pac_checksums are false
       via  d054f583ead tests/krb5: Allow multiple ticket modification functions
       via  60e479d855d tests/krb5: Allow filter for tests that crash Windows
       via  939a74e39b0 tests/krb5: Allow variation in PADATA_PW_SALT
       via  c33ce174547 tests/krb5: Sort imports
      from  ad76bb2e0c6 streams_depot: Goto done if FSETXATTR SAMBA_XATTR_MARKER failed

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 08b9d5c7b9f0d25a278f46c567b3703a1f90ecc6
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 14:10:16 2023 +1300

    tests/krb5: Add samba.tests.krb5.conditional_ace_tests
    
    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 at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Pair-programmed-by: Andrew Bartlett <abartlet at samba.org>
    
    Autobuild-User(master): Andrew Bartlett <abartlet at samba.org>
    Autobuild-Date(master): Thu Sep 28 04:35:05 UTC 2023 on atb-devel-224

commit 0e7e46c396b973e5d49e0f6eb17ad30135db5c05
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 16:13:08 2023 +1300

    tests/krb5: Add method to replace client or device claims in a PAC
    
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 6f5368dd32689019fff8071ec4601971712dd1d2
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 16:12:46 2023 +1300

    tests/krb5: Add method to replace the device SIDs in a PAC
    
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 2d0bdb5ce92ea87f7228d6bb8918ec2fcf414af7
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 16:09:06 2023 +1300

    tests/krb5: Have set_pac_sids() accept lone RIDs as well as full SIDs
    
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit cc1dd00d0fb89997b31dcef181fba16c3732a816
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 16:08:25 2023 +1300

    tests/krb5: Make optional ‘domain_sid’ parameter to set_pac_sids()
    
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 9fb0380cb8239ba9421f14ce23f12c133f716eb6
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 16:07:43 2023 +1300

    tests/krb5: Make optional ‘user_rid’ parameter to set_pac_sids()
    
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 34e721030dffe3ffad98e1a9b7c581897c9436eb
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 16:06:01 2023 +1300

    tests/krb5: Make set_pac_sids() parameters keyword‐only
    
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit d6ec0e4f4053549193248a6b6974af993130f264
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 16:03:09 2023 +1300

    tests/krb5: Allow passing mapping=None to map_to_sid()
    
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit dfd2027d7e57e64b7b477706ce214cfec77586bb
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 15:51:35 2023 +1300

    tests/krb5: Don’t bother regenerating the PAC if modify_pac_fn or update_pac_checksums are false
    
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit d054f583ead4c4a41d235db463dc968d67039313
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 14:52:11 2023 +1300

    tests/krb5: Allow multiple ticket modification functions
    
    This means that callers can specify a stack of possible modifications.
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 60e479d855d0e28ec27f28610d6cb1f5617bdfac
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 14:50:39 2023 +1300

    tests/krb5: Allow filter for tests that crash Windows
    
    Set CRASH_WINDOWS=0 when running against a Windows DC.  These crashes are
    only possible because we can modify the PAC, but having these tests allows
    us to lock down Samba behaviour, so we include them.
    
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 939a74e39b06535fa5f25a6933825366325fbc62
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Thu Sep 28 14:49:11 2023 +1300

    tests/krb5: Allow variation in PADATA_PW_SALT
    
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit c33ce17454707fd036009ef711cee59eba620b54
Author: Joseph Sutton <josephsutton at catalyst.net.nz>
Date:   Wed Sep 27 13:43:53 2023 +1300

    tests/krb5: Sort imports
    
    Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

-----------------------------------------------------------------------

Summary of changes:
 python/samba/tests/krb5/conditional_ace_tests.py | 3731 ++++++++++++++++++++++
 python/samba/tests/krb5/kdc_base_test.py         |  296 +-
 python/samba/tests/krb5/kdc_tgs_tests.py         |   20 +
 python/samba/tests/krb5/raw_testcase.py          |   56 +-
 selftest/knownfail_heimdal_kdc                   |  182 ++
 selftest/knownfail_mit_kdc                       |  248 ++
 source4/selftest/tests.py                        |    4 +
 7 files changed, 4500 insertions(+), 37 deletions(-)
 create mode 100755 python/samba/tests/krb5/conditional_ace_tests.py


Changeset truncated at 500 lines:

diff --git a/python/samba/tests/krb5/conditional_ace_tests.py b/python/samba/tests/krb5/conditional_ace_tests.py
new file mode 100755
index 00000000000..0b351ae253b
--- /dev/null
+++ b/python/samba/tests/krb5/conditional_ace_tests.py
@@ -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)
+
+
+ at 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 '


-- 
Samba Shared Repository



More information about the samba-cvs mailing list