From eae2e585b976ec56ee6c1cff07084be2d711eb04 Mon Sep 17 00:00:00 2001 From: Jo Sutton Date: Tue, 2 May 2023 15:42:24 +1200 Subject: [PATCH] tests/krb5: Add tests for AllowedToAuthenticateTo with an AS-REQ BUG: https://bugzilla.samba.org/show_bug.cgi?id=15607 Signed-off-by: Jo Sutton Reviewed-by: Andrew Bartlett Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Thu Mar 21 04:19:18 UTC 2024 on atb-devel-224 (cherry picked from commit 4f0ed9b00389fa641a423b88ab5462b32dd7bbca) --- python/samba/tests/krb5/authn_policy_tests.py | 372 ++++++++++++++++++ selftest/knownfail_mit_kdc | 8 + 2 files changed, 380 insertions(+) diff --git a/python/samba/tests/krb5/authn_policy_tests.py b/python/samba/tests/krb5/authn_policy_tests.py index 2f15f8b2417..43db839cee7 100755 --- a/python/samba/tests/krb5/authn_policy_tests.py +++ b/python/samba/tests/krb5/authn_policy_tests.py @@ -295,6 +295,115 @@ class AuthnPolicyBaseTests(AuthLogTestBase, KdcTgsBaseTests): opts=opts, use_cache=cached) + def _fast_as_req(self, + client_creds, + target_creds, + armor_tgt, + expected_error=0, + expect_status=None, + expected_status=None, + expected_groups=None, + expect_device_info=None, + expected_device_groups=None, + expect_device_claims=None, + expected_device_claims=None): + client_username = client_creds.get_username() + client_realm = client_creds.get_realm() + client_cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, + names=[client_username]) + + target_name = target_creds.get_username() + target_sname = self.PrincipalName_create( + name_type=NT_PRINCIPAL, names=[target_name]) + target_realm = target_creds.get_realm() + target_decryption_key = self.TicketDecryptionKey_from_creds( + target_creds) + target_etypes = target_creds.tgs_supported_enctypes + + authenticator_subkey = self.RandomKey(kcrypto.Enctype.AES256) + armor_key = self.generate_armor_key(authenticator_subkey, + armor_tgt.session_key) + + preauth_key = self.PasswordKey_from_creds(client_creds, + kcrypto.Enctype.AES256) + + client_challenge_key = ( + self.generate_client_challenge_key(armor_key, preauth_key)) + fast_padata = [self.get_challenge_pa_data(client_challenge_key)] + + def _generate_fast_padata(kdc_exchange_dict, + _callback_dict, + req_body): + return list(fast_padata), req_body + + etypes = kcrypto.Enctype.AES256, kcrypto.Enctype.RC4 + + if expected_error: + check_error_fn = self.generic_check_kdc_error + check_rep_fn = None + else: + check_error_fn = None + check_rep_fn = self.generic_check_kdc_rep + + pac_options = '1' # claims support + + samdb = self.get_samdb() + domain_sid_str = samdb.get_domain_sid() + + if expected_groups is not None: + expected_groups = self.map_sids(expected_groups, None, domain_sid_str) + + if expected_device_groups is not None: + expected_device_groups = self.map_sids(expected_device_groups, None, domain_sid_str) + + kdc_exchange_dict = self.as_exchange_dict( + creds=client_creds, + expected_crealm=client_realm, + expected_cname=client_cname, + expected_srealm=target_realm, + expected_sname=target_sname, + expected_supported_etypes=target_etypes, + ticket_decryption_key=target_decryption_key, + generate_fast_fn=self.generate_simple_fast, + generate_fast_armor_fn=self.generate_ap_req, + generate_fast_padata_fn=_generate_fast_padata, + fast_armor_type=FX_FAST_ARMOR_AP_REQUEST, + check_error_fn=check_error_fn, + check_rep_fn=check_rep_fn, + check_kdc_private_fn=self.generic_check_kdc_private, + expected_error_mode=expected_error, + expected_salt=client_creds.get_salt(), + expect_status=expect_status, + expected_status=expected_status, + expected_groups=expected_groups, + expect_device_info=expect_device_info, + expected_device_domain_sid=domain_sid_str, + expected_device_groups=expected_device_groups, + expect_device_claims=expect_device_claims, + expected_device_claims=expected_device_claims, + authenticator_subkey=authenticator_subkey, + preauth_key=preauth_key, + armor_key=armor_key, + armor_tgt=armor_tgt, + armor_subkey=authenticator_subkey, + kdc_options='0', + pac_options=pac_options, + # PA-DATA types are not important for these tests. + check_patypes=False) + + rep = self._generic_kdc_exchange( + kdc_exchange_dict, + cname=client_cname, + realm=client_realm, + sname=target_sname, + etypes=etypes) + if expected_error: + self.check_error_rep(rep, expected_error) + return None + else: + self.check_as_reply(rep) + return kdc_exchange_dict['rep_ticket_creds'] + @staticmethod def audit_type(msg): return AuditType(msg['type']) @@ -6532,6 +6641,269 @@ class AuthnPolicyTests(AuthnPolicyBaseTests): self.check_tgs_log(client_creds, target_creds, policy=policy) + def test_authn_policy_allowed_to_computer_allow_as_req(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 a user account. + client_creds = self.get_cached_creds( + account_type=self.AccountType.USER) + + # Create an authentication policy that applies to a computer and + # explicitly allows the user account to obtain a service ticket. + allowed = f'O:SYD:(A;;CR;;;{client_creds.get_sid()})' + denied = 'O:SYD:(D;;CR;;;WD)' + policy = self.create_authn_policy(enforced=True, + user_allowed_to=denied, + computer_allowed_to=allowed, + service_allowed_to=denied) + + # Create a computer account with the assigned policy. + target_creds = self._get_creds(account_type=self.AccountType.COMPUTER, + assigned_policy=policy) + + # Show that obtaining a service ticket with an AS-REQ is allowed. + self._fast_as_req(client_creds, target_creds, mach_tgt) + + self.check_as_log(client_creds, + server_policy=policy) + + def test_authn_policy_allowed_to_computer_deny_as_req(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 a user account. + client_creds = self.get_cached_creds( + account_type=self.AccountType.USER) + + # Create an authentication policy that applies to a computer and + # explicitly denies the user account to obtain a service ticket. + denied = f'O:SYD:(D;;CR;;;{client_creds.get_sid()})' + allowed = 'O:SYD:(A;;CR;;;WD)' + policy = self.create_authn_policy(enforced=True, + user_allowed_to=allowed, + computer_allowed_to=denied, + service_allowed_to=allowed) + + # Create a computer account with the assigned policy. + target_creds = self._get_creds(account_type=self.AccountType.COMPUTER, + assigned_policy=policy) + + # Show that obtaining a service ticket with an AS-REQ is denied. + self._fast_as_req( + client_creds, target_creds, mach_tgt, + expected_error=KDC_ERR_POLICY, + # 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_as_log( + client_creds, + server_policy=policy, + server_policy_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED, + event=AuditEvent.KERBEROS_SERVER_RESTRICTION, + reason=AuditReason.ACCESS_DENIED, + status=ntstatus.NT_STATUS_INVALID_WORKSTATION) + + def test_authn_policy_allowed_to_user_allow_as_req(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 a user account. + client_creds = self.get_cached_creds( + account_type=self.AccountType.USER) + + # Create an authentication policy that applies to a user and explicitly + # allows the user account to obtain a service ticket. + allowed = f'O:SYD:(A;;CR;;;{client_creds.get_sid()})' + denied = 'O:SYD:(D;;CR;;;WD)' + policy = self.create_authn_policy(enforced=True, + user_allowed_to=allowed, + computer_allowed_to=denied, + service_allowed_to=denied) + + # Create a user account with the assigned policy. + target_creds = self._get_creds(account_type=self.AccountType.USER, + assigned_policy=policy, + spn='host/{account}') + + # Show that obtaining a service ticket with an AS-REQ is allowed. + self._fast_as_req(client_creds, target_creds, mach_tgt) + + self.check_as_log(client_creds, + server_policy=policy) + + def test_authn_policy_allowed_to_user_deny_as_req(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 a user account. + client_creds = self.get_cached_creds( + account_type=self.AccountType.USER) + + # Create an authentication policy that applies to a user and + # explicitly denies the user account to obtain a service ticket. + denied = f'O:SYD:(D;;CR;;;{client_creds.get_sid()})' + allowed = 'O:SYD:(A;;CR;;;WD)' + policy = self.create_authn_policy(enforced=True, + user_allowed_to=denied, + computer_allowed_to=allowed, + service_allowed_to=allowed) + + # Create a user account with the assigned policy. + target_creds = self._get_creds(account_type=self.AccountType.USER, + assigned_policy=policy, + spn='host/{account}') + + # Show that obtaining a service ticket with an AS-REQ is denied. + self._fast_as_req( + client_creds, target_creds, mach_tgt, + expected_error=KDC_ERR_POLICY, + # 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_as_log( + client_creds, + server_policy=policy, + server_policy_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED, + event=AuditEvent.KERBEROS_SERVER_RESTRICTION, + reason=AuditReason.ACCESS_DENIED, + status=ntstatus.NT_STATUS_INVALID_WORKSTATION) + + def test_authn_policy_allowed_to_service_allow_as_req(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 a user account. + client_creds = self.get_cached_creds( + account_type=self.AccountType.USER) + + # Create an authentication policy that applies to a managed service and + # explicitly allows the user account to obtain a service ticket. + allowed = f'O:SYD:(A;;CR;;;{client_creds.get_sid()})' + denied = 'O:SYD:(D;;CR;;;WD)' + policy = self.create_authn_policy(enforced=True, + user_allowed_to=denied, + computer_allowed_to=denied, + service_allowed_to=allowed) + + # Create a managed service account with the assigned policy. + target_creds = self._get_creds( + account_type=self.AccountType.MANAGED_SERVICE, + assigned_policy=policy) + + # Show that obtaining a service ticket with an AS-REQ is allowed. + self._fast_as_req(client_creds, target_creds, mach_tgt) + + self.check_as_log(client_creds, + server_policy=policy) + + def test_authn_policy_allowed_to_service_deny_as_req(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 a user account. + client_creds = self.get_cached_creds( + account_type=self.AccountType.USER) + + # Create an authentication policy that applies to a managed service and + # explicitly denies the user account to obtain a service ticket. + denied = f'O:SYD:(D;;CR;;;{client_creds.get_sid()})' + allowed = 'O:SYD:(A;;CR;;;WD)' + policy = self.create_authn_policy(enforced=True, + user_allowed_to=allowed, + computer_allowed_to=allowed, + service_allowed_to=denied) + + # Create a managed service account with the assigned policy. + target_creds = self._get_creds( + account_type=self.AccountType.MANAGED_SERVICE, + assigned_policy=policy) + + # Show that obtaining a service ticket with an AS-REQ is denied. + self._fast_as_req( + client_creds, target_creds, mach_tgt, + expected_error=KDC_ERR_POLICY, + # 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_as_log( + client_creds, + server_policy=policy, + server_policy_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED, + event=AuditEvent.KERBEROS_SERVER_RESTRICTION, + reason=AuditReason.ACCESS_DENIED, + status=ntstatus.NT_STATUS_INVALID_WORKSTATION) + + def test_authn_policy_allowed_to_computer_allow_as_req_no_fast(self): + # Create a user account. + client_creds = self.get_cached_creds( + account_type=self.AccountType.USER) + + # Create an authentication policy that applies to a computer and + # explicitly allows the user account to obtain a service ticket. + allowed = f'O:SYD:(A;;CR;;;{client_creds.get_sid()})' + denied = 'O:SYD:(D;;CR;;;WD)' + policy = self.create_authn_policy(enforced=True, + user_allowed_to=denied, + computer_allowed_to=allowed, + service_allowed_to=denied) + + # Create a computer account with the assigned policy. + target_creds = self._get_creds(account_type=self.AccountType.COMPUTER, + assigned_policy=policy) + + # Show that obtaining a service ticket with an AS-REQ is allowed. + self._as_req(client_creds, 0, target_creds, + etype=(kcrypto.Enctype.AES256,)) + + self.check_as_log(client_creds, + server_policy=policy) + + def test_authn_policy_allowed_to_computer_deny_as_req_no_fast(self): + # Create a user account. + client_creds = self.get_cached_creds( + account_type=self.AccountType.USER) + + # Create an authentication policy that applies to a computer and + # explicitly denies the user account to obtain a service ticket. + denied = f'O:SYD:(D;;CR;;;{client_creds.get_sid()})' + allowed = 'O:SYD:(A;;CR;;;WD)' + policy = self.create_authn_policy(enforced=True, + user_allowed_to=allowed, + computer_allowed_to=denied, + service_allowed_to=allowed) + + # Create a computer account with the assigned policy. + target_creds = self._get_creds(account_type=self.AccountType.COMPUTER, + assigned_policy=policy) + + # Show that obtaining a service ticket with an AS-REQ is denied. + self._as_req(client_creds, KDC_ERR_POLICY, target_creds, + etype=(kcrypto.Enctype.AES256,)) + + self.check_as_log( + client_creds, + server_policy=policy, + server_policy_status=ntstatus.NT_STATUS_AUTHENTICATION_FIREWALL_FAILED, + event=AuditEvent.KERBEROS_SERVER_RESTRICTION, + reason=AuditReason.ACCESS_DENIED, + status=ntstatus.NT_STATUS_INVALID_WORKSTATION) + def test_authn_policy_ntlm_allow_user(self): # Create an authentication policy allowing NTLM authentication for # users. diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc index de2569e0f62..7256fb3f110 100644 --- a/selftest/knownfail_mit_kdc +++ b/selftest/knownfail_mit_kdc @@ -3738,6 +3738,8 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_from_user_deny.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_from_user_deny_from_rodc.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_allow.ad_dc +^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_allow_as_req.ad_dc +^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_allow_as_req_no_fast.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_allow_asserted_identity.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_allow_asserted_identity_from_rodc.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_allow_authenticated_users.ad_dc @@ -3755,6 +3757,8 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_allow_to_self_with_self.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_allow_user2user.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_deny.ad_dc +^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_deny_as_req.ad_dc +^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_deny_as_req_no_fast.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_deny_from_rodc.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_deny_to_self.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_computer_deny_to_self_with_self.ad_dc @@ -3766,11 +3770,14 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_owner_anon.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_owner_self.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_service_allow.ad_dc +^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_service_allow_as_req.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_service_allow_from_rodc.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_service_deny.ad_dc +^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_service_deny_as_req.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_service_deny_from_rodc.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_service_derived_class_allow.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_allow.ad_dc +^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_allow_as_req.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_allow_constrained_delegation.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_allow_constrained_delegation_to_self.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_allow_constrained_delegation_wrong_sname.ad_dc @@ -3787,6 +3794,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_allow_s4u2self.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_allow_s4u2self_inner_fast.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_deny.ad_dc +^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_deny_as_req.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_deny_constrained_delegation.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_deny_constrained_delegation_to_self.ad_dc ^samba.tests.krb5.authn_policy_tests.samba.tests.krb5.authn_policy_tests.AuthnPolicyTests.test_authn_policy_allowed_to_user_deny_from_rodc.ad_dc -- 2.25.1