The Samba-Bugzilla – Attachment 16894 Details for
Bug 14834
[SECURITY] Andrew's Kerberos Concerns and other issues (Nov 9 2021 Release bug)
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
patch for master (WIP) v1
andrew-kerberos-master-WIP-v1.patch (text/plain), 1.04 MB, created by
Andrew Bartlett
on 2021-10-29 10:30:43 UTC
(
hide
)
Description:
patch for master (WIP) v1
Filename:
MIME Type:
Creator:
Andrew Bartlett
Created:
2021-10-29 10:30:43 UTC
Size:
1.04 MB
patch
obsolete
>From 35e6c1d7050b28a742190ebddb18c618666d5347 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Tue, 10 Aug 2021 22:31:02 +1200 >Subject: [PATCH 001/190] CVE-2020-25722 dsdb: Tests for our known set of > privileged attributes > >This, except for where we choose to disagree, does pass >against Windows 2019. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14703 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14778 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14775 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/priv_attr | 54 ++++ > source4/dsdb/tests/python/priv_attrs.py | 388 ++++++++++++++++++++++++ > source4/selftest/tests.py | 2 + > 3 files changed, 444 insertions(+) > create mode 100644 selftest/knownfail.d/priv_attr > create mode 100644 source4/dsdb/tests/python/priv_attrs.py > >diff --git a/selftest/knownfail.d/priv_attr b/selftest/knownfail.d/priv_attr >new file mode 100644 >index 00000000000..31b9cb23b44 >--- /dev/null >+++ b/selftest/knownfail.d/priv_attr >@@ -0,0 +1,54 @@ >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_WP_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_WP_user >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_default_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_default_user >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_user >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_default_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_default_user >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_WP_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_WP_user >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_default_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_default_user >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_WP_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_WP_user >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_user >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_WP_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_WP_user >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_default_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_default_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_WP_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_WP_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_default_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_default_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_WP_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_WP_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_default_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_default_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_WP_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_WP_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_default_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_default_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_default_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_default_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_WP_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_WP_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_default_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_default_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-RODC_add_CC_WP_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-RODC_add_CC_default_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-computer_add_CC_WP_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-computer_add_CC_default_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_CC_WP_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_CC_WP_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_CC_default_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_CC_default_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_WP_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_WP_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_mod-del-add_CC_default_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_mod-replace_CC_default_computer >diff --git a/source4/dsdb/tests/python/priv_attrs.py b/source4/dsdb/tests/python/priv_attrs.py >new file mode 100644 >index 00000000000..ec2b13045e5 >--- /dev/null >+++ b/source4/dsdb/tests/python/priv_attrs.py >@@ -0,0 +1,388 @@ >+#!/usr/bin/env python3 >+# -*- coding: utf-8 -*- >+# This tests the restrictions on userAccountControl that apply even if write access is permitted >+# >+# Copyright Samuel Cabrero 2014 <samuelcabrero@kernevil.me> >+# Copyright Andrew Bartlett 2014 <abartlet@samba.org> >+# >+# Licenced under the GPLv3 >+# >+ >+import optparse >+import sys >+import unittest >+import samba >+import samba.getopt as options >+import samba.tests >+import ldb >+import base64 >+ >+sys.path.insert(0, "bin/python") >+from samba.tests.subunitrun import TestProgram, SubunitOptions >+from samba.tests import DynamicTestCase >+from samba.subunit.run import SubunitTestRunner >+from samba.auth import system_session >+from samba.samdb import SamDB >+from samba.dcerpc import samr, security, lsa >+from samba.credentials import Credentials >+from samba.ndr import ndr_unpack, ndr_pack >+from samba.tests import delete_force >+from samba import gensec, sd_utils >+from samba.credentials import DONT_USE_KERBEROS >+from ldb import SCOPE_SUBTREE, SCOPE_BASE, LdbError >+from ldb import Message, MessageElement, Dn >+from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE >+from samba.dsdb import UF_SCRIPT, UF_ACCOUNTDISABLE, UF_00000004, UF_HOMEDIR_REQUIRED, \ >+ UF_LOCKOUT, UF_PASSWD_NOTREQD, UF_PASSWD_CANT_CHANGE, UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED,\ >+ UF_TEMP_DUPLICATE_ACCOUNT, UF_NORMAL_ACCOUNT, UF_00000400, UF_INTERDOMAIN_TRUST_ACCOUNT, \ >+ UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT, UF_00004000, \ >+ UF_00008000, UF_DONT_EXPIRE_PASSWD, UF_MNS_LOGON_ACCOUNT, UF_SMARTCARD_REQUIRED, \ >+ UF_TRUSTED_FOR_DELEGATION, UF_NOT_DELEGATED, UF_USE_DES_KEY_ONLY, UF_DONT_REQUIRE_PREAUTH, \ >+ UF_PASSWORD_EXPIRED, UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION, UF_NO_AUTH_DATA_REQUIRED, \ >+ UF_PARTIAL_SECRETS_ACCOUNT, UF_USE_AES_KEYS >+ >+ >+parser = optparse.OptionParser("user_account_control.py [options] <host>") >+sambaopts = options.SambaOptions(parser) >+parser.add_option_group(sambaopts) >+parser.add_option_group(options.VersionOptions(parser)) >+ >+# use command line creds if available >+credopts = options.CredentialsOptions(parser) >+parser.add_option_group(credopts) >+opts, args = parser.parse_args() >+ >+if len(args) < 1: >+ parser.print_usage() >+ sys.exit(1) >+host = args[0] >+ >+if "://" not in host: >+ ldaphost = "ldap://%s" % host >+else: >+ ldaphost = host >+ start = host.rindex("://") >+ host = host.lstrip(start + 3) >+ >+lp = sambaopts.get_loadparm() >+creds = credopts.get_credentials(lp) >+creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL) >+ >+ >+""" >+Check the combinations of: >+ >+rodc kdc >+a2d2 >+useraccountcontrol (trusted for delegation) >+sidHistory >+ >+x >+ >+add >+modify(replace) >+modify(add) >+ >+x >+ >+sd WP on add >+cc default perms >+admin created, WP to user >+ >+x >+ >+computer >+user >+""" >+ >+attrs = {"sidHistory": >+ {"value": ndr_pack(security.dom_sid(security.SID_BUILTIN_ADMINISTRATORS)), >+ "priv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, >+ "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS}, >+ "msDS-AllowedToDelegateTo": >+ {"value": f"host/{host}", >+ "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS}, >+ "userAccountControl-a2d-user": >+ {"attr": "userAccountControl", >+ "value": str(UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION|UF_NORMAL_ACCOUNT), >+ "priv-error": ldb.ERR_UNWILLING_TO_PERFORM, >+ "unpriv-add-error": ldb.ERR_UNWILLING_TO_PERFORM, >+ "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS}, >+ "userAccountControl-a2d-computer": >+ {"attr": "userAccountControl", >+ "value": str(UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION|UF_WORKSTATION_TRUST_ACCOUNT), >+ "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, >+ "only-1": "computer"}, >+ "userAccountControl-DC": >+ {"attr": "userAccountControl", >+ "value": str(UF_SERVER_TRUST_ACCOUNT), >+ "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, >+ "only-2": "computer"}, >+ "userAccountControl-RODC": >+ {"attr": "userAccountControl", >+ "value": str(UF_PARTIAL_SECRETS_ACCOUNT|UF_WORKSTATION_TRUST_ACCOUNT), >+ "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, >+ "only-1": "computer"}, >+ "msDS-SecondaryKrbTgtNumber": >+ {"value": "65536", >+ "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS}, >+ "primaryGroupID": >+ {"value": str(security.DOMAIN_RID_ADMINS), >+ "priv-error": ldb.ERR_UNWILLING_TO_PERFORM, >+ "unpriv-add-error": ldb.ERR_UNWILLING_TO_PERFORM, >+ "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS} >+ } >+ >+ >+ >+@DynamicTestCase >+class PrivAttrsTests(samba.tests.TestCase): >+ >+ def get_creds(self, target_username, target_password): >+ creds_tmp = Credentials() >+ creds_tmp.set_username(target_username) >+ creds_tmp.set_password(target_password) >+ creds_tmp.set_domain(creds.get_domain()) >+ creds_tmp.set_realm(creds.get_realm()) >+ creds_tmp.set_workstation(creds.get_workstation()) >+ creds_tmp.set_gensec_features(creds_tmp.get_gensec_features() >+ | gensec.FEATURE_SEAL) >+ creds_tmp.set_kerberos_state(DONT_USE_KERBEROS) # kinit is too expensive to use in a tight loop >+ return creds_tmp >+ >+ def assertGotLdbError(self, got, wanted): >+ if not self.strict_checking: >+ self.assertNotEqual(got, ldb.SUCCESS) >+ else: >+ self.assertEqual(got, wanted) >+ >+ def setUp(self): >+ super().setUp() >+ >+ strict_checking = samba.tests.env_get_var_value('STRICT_CHECKING', allow_missing=True) >+ if strict_checking is None: >+ strict_checking = '1' >+ self.strict_checking = bool(int(strict_checking)) >+ >+ self.admin_creds = creds >+ self.admin_samdb = SamDB(url=ldaphost, credentials=self.admin_creds, lp=lp) >+ self.domain_sid = security.dom_sid(self.admin_samdb.get_domain_sid()) >+ self.base_dn = self.admin_samdb.domain_dn() >+ >+ self.unpriv_user = "testuser1" >+ self.unpriv_user_pw = "samba123@" >+ self.unpriv_creds = self.get_creds(self.unpriv_user, self.unpriv_user_pw) >+ >+ self.admin_sd_utils = sd_utils.SDUtils(self.admin_samdb) >+ >+ self.test_ou_name = "OU=test_priv_attrs" >+ self.test_ou = self.test_ou_name + "," + self.base_dn >+ >+ delete_force(self.admin_samdb, self.test_ou, controls=["tree_delete:0"]) >+ >+ self.admin_samdb.create_ou(self.test_ou) >+ >+ expected_user_dn = f"CN={self.unpriv_user},{self.test_ou_name},{self.base_dn}" >+ >+ self.admin_samdb.newuser(self.unpriv_user, self.unpriv_user_pw, userou=self.test_ou_name) >+ res = self.admin_samdb.search(expected_user_dn, >+ scope=SCOPE_BASE, >+ attrs=["objectSid"]) >+ >+ self.assertEqual(1, len(res)) >+ >+ self.unpriv_user_dn = res[0].dn >+ self.addCleanup(delete_force, self.admin_samdb, self.unpriv_user_dn, controls=["tree_delete:0"]) >+ >+ self.unpriv_user_sid = self.admin_sd_utils.get_object_sid(self.unpriv_user_dn) >+ >+ self.unpriv_samdb = SamDB(url=ldaphost, credentials=self.unpriv_creds, lp=lp) >+ >+ @classmethod >+ def setUpDynamicTestCases(cls): >+ for test_name in attrs.keys(): >+ for add_or_mod in ["add", "mod-del-add", "mod-replace"]: >+ for permission in ["admin-add", "CC"]: >+ for sd in ["default", "WP"]: >+ for objectclass in ["computer", "user"]: >+ tname = f"{test_name}_{add_or_mod}_{permission}_{sd}_{objectclass}" >+ targs = (test_name, >+ add_or_mod, >+ permission, >+ sd, >+ objectclass) >+ cls.generate_dynamic_test("test_priv_attr", >+ tname, >+ *targs) >+ >+ def add_computer_ldap(self, computername, others=None, samdb=None): >+ dn = "CN=%s,%s" % (computername, self.test_ou) >+ domainname = ldb.Dn(samdb, samdb.domain_dn()).canonical_str().replace("/", "") >+ samaccountname = "%s$" % computername >+ dnshostname = "%s.%s" % (computername, domainname) >+ msg_dict = { >+ "dn": dn, >+ "objectclass": "computer"} >+ if others is not None: >+ msg_dict = dict(list(msg_dict.items()) + list(others.items())) >+ >+ msg = ldb.Message.from_dict(samdb, msg_dict) >+ msg["sAMAccountName"] = samaccountname >+ >+ print("Adding computer account %s" % computername) >+ try: >+ samdb.add(msg) >+ except ldb.LdbError: >+ print(msg) >+ raise >+ return msg.dn >+ >+ def add_user_ldap(self, username, others=None, samdb=None): >+ dn = "CN=%s,%s" % (username, self.test_ou) >+ domainname = ldb.Dn(samdb, samdb.domain_dn()).canonical_str().replace("/", "") >+ samaccountname = "%s$" % username >+ msg_dict = { >+ "dn": dn, >+ "objectclass": "user"} >+ if others is not None: >+ msg_dict = dict(list(msg_dict.items()) + list(others.items())) >+ >+ msg = ldb.Message.from_dict(samdb, msg_dict) >+ msg["sAMAccountName"] = samaccountname >+ >+ print("Adding user account %s" % username) >+ try: >+ samdb.add(msg) >+ except ldb.LdbError: >+ print(msg) >+ raise >+ return msg.dn >+ >+ def add_thing_ldap(self, user, others, samdb, objectclass): >+ if objectclass == "user": >+ dn = self.add_user_ldap(user, others, samdb=samdb) >+ elif objectclass == "computer": >+ dn = self.add_computer_ldap(user, others, samdb=samdb) >+ return dn >+ >+ def _test_priv_attr_with_args(self, test_name, add_or_mod, permission, sd, objectclass): >+ user="privattrs" >+ if "attr" in attrs[test_name]: >+ attr = attrs[test_name]["attr"] >+ else: >+ attr = test_name >+ if add_or_mod == "add": >+ others = {attr: attrs[test_name]["value"]} >+ else: >+ others = {} >+ >+ if permission == "CC": >+ samdb = self.unpriv_samdb >+ # Set CC on container to allow user add >+ mod = "(OA;CI;CC;bf967aba-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.unpriv_user_sid) >+ self.admin_sd_utils.dacl_add_ace(self.test_ou, mod) >+ mod = "(OA;CI;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.unpriv_user_sid) >+ self.admin_sd_utils.dacl_add_ace(self.test_ou, mod) >+ >+ else: >+ samdb = self.admin_samdb >+ >+ if sd == "WP": >+ # Set SD to WP to the target user as part of add >+ sd = "O:%sG:DUD:(OA;CIID;RPWP;;;%s)(OA;;CR;00299570-246d-11d0-a768-00aa006e0529;;%s)" % (self.unpriv_user_sid, self.unpriv_user_sid, self.unpriv_user_sid) >+ tmp_desc = security.descriptor.from_sddl(sd, self.domain_sid) >+ others["ntSecurityDescriptor"] = ndr_pack(tmp_desc) >+ >+ if add_or_mod == "add": >+ >+ # only-1 and only-2 are due to windows behaviour >+ >+ if "only-1" in attrs[test_name] and \ >+ attrs[test_name]["only-1"] != objectclass: >+ try: >+ dn = self.add_thing_ldap(user, others, samdb, objectclass) >+ self.fail(f"{test_name}: Unexpectedly able to set {attr} on new {objectclass} as ADMIN (should fail LDAP_OBJECT_CLASS_VIOLATION)") >+ except LdbError as e5: >+ (enum, estr) = e5.args >+ self.assertGotLdbError(ldb.ERR_OBJECT_CLASS_VIOLATION, enum) >+ elif permission == "CC": >+ try: >+ dn = self.add_thing_ldap(user, others, samdb, objectclass) >+ self.fail(f"{test_name}: Unexpectedly able to set {attr} on new {objectclass}") >+ except LdbError as e5: >+ (enum, estr) = e5.args >+ if "unpriv-add-error" in attrs[test_name]: >+ self.assertGotLdbError(attrs[test_name]["unpriv-add-error"], \ >+ enum) >+ else: >+ self.assertGotLdbError(attrs[test_name]["unpriv-error"], \ >+ enum) >+ elif "only-2" in attrs[test_name] and \ >+ attrs[test_name]["only-2"] != objectclass: >+ try: >+ dn = self.add_thing_ldap(user, others, samdb, objectclass) >+ self.fail(f"{test_name}: Unexpectedly able to set {attr} on new {objectclass} as ADMIN (should fail LDAP_OBJECT_CLASS_VIOLATION)") >+ except LdbError as e5: >+ (enum, estr) = e5.args >+ self.assertGotLdbError(ldb.ERR_OBJECT_CLASS_VIOLATION, enum) >+ elif "priv-error" in attrs[test_name]: >+ try: >+ dn = self.add_thing_ldap(user, others, samdb, objectclass) >+ self.fail(f"{test_name}: Unexpectedly able to set {attr} on new {objectclass} as ADMIN") >+ except LdbError as e5: >+ (enum, estr) = e5.args >+ self.assertGotLdbError(attrs[test_name]["priv-error"], enum) >+ else: >+ try: >+ dn = self.add_thing_ldap(user, others, samdb, objectclass) >+ except LdbError as e5: >+ (enum, estr) = e5.args >+ self.fail(f"Failed to add account {user} as objectclass {objectclass}") >+ else: >+ try: >+ dn = self.add_thing_ldap(user, others, samdb, objectclass) >+ except LdbError as e5: >+ (enum, estr) = e5.args >+ self.fail(f"Failed to add account {user} as objectclass {objectclass}") >+ >+ if add_or_mod == "add": >+ return >+ >+ m = ldb.Message() >+ m.dn = dn >+ >+ # Do modify >+ if add_or_mod == "mod-del-add": >+ m["0"] = ldb.MessageElement([], >+ ldb.FLAG_MOD_DELETE, >+ attr) >+ m["1"] = ldb.MessageElement(attrs[test_name]["value"], >+ ldb.FLAG_MOD_ADD, >+ attr) >+ else: >+ m["0"] = ldb.MessageElement(attrs[test_name]["value"], >+ ldb.FLAG_MOD_REPLACE, >+ attr) >+ >+ try: >+ self.unpriv_samdb.modify(m) >+ self.fail(f"{test_name}: Unexpectedly able to set {attr} on {m.dn}") >+ except LdbError as e5: >+ (enum, estr) = e5.args >+ if attr == "userAccountControl" and sd == "default": >+ # We get a different error if we try and swap between >+ # being a computer back to being a user when created with "Create child" permissions >+ if (int(attrs[test_name]["value"]) & UF_NORMAL_ACCOUNT) \ >+ and objectclass == "computer" and permission == "CC": >+ self.assertGotLdbError(ldb.ERR_UNWILLING_TO_PERFORM, enum) >+ return >+ self.assertGotLdbError(attrs[test_name]["unpriv-error"], enum) >+ >+ >+ >+ >+runner = SubunitTestRunner() >+rc = 0 >+if not runner.run(unittest.makeSuite(PrivAttrsTests)).wasSuccessful(): >+ rc = 1 >+sys.exit(rc) >diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py >index 21b1086a70a..21894812422 100755 >--- a/source4/selftest/tests.py >+++ b/source4/selftest/tests.py >@@ -1179,6 +1179,8 @@ plantestsuite("samba4.sam.python(fl2008r2dc)", "fl2008r2dc", [python, os.path.jo > plantestsuite("samba4.sam.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "sam.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) > plantestsuite("samba4.asq.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "asq.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) > plantestsuite("samba4.user_account_control.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "user_account_control.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) >+plantestsuite("samba4.priv_attrs.python(ad_dc_default)", "ad_dc_default", ["STRICT_CHECKING=0", python, os.path.join(DSDB_PYTEST_DIR, "priv_attrs.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) >+plantestsuite("samba4.priv_attrs.strict.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "priv_attrs.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) > > for env in ['ad_dc_default:local', 'schema_dc:local']: > planoldpythontestsuite(env, "dsdb_schema_info", >-- >2.25.1 > > >From 43f18b20fe6e6bb6c13ef51aa7bb043fc131a6c4 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 12 Aug 2021 11:10:09 +1200 >Subject: [PATCH 002/190] CVE-2020-25722 dsdb: Move krbtgt password setup after > the point of checking if any passwords are changed > >This allows the add of an RODC, before setting the password, to avoid >this module, which helps isolate testing of security around the >msDS-SecondaryKrbTgtNumber attribute. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14703 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/priv_attr | 12 +- > .../dsdb/samdb/ldb_modules/password_hash.c | 106 +++++++++--------- > 2 files changed, 57 insertions(+), 61 deletions(-) > >diff --git a/selftest/knownfail.d/priv_attr b/selftest/knownfail.d/priv_attr >index 31b9cb23b44..ab6db192aae 100644 >--- a/selftest/knownfail.d/priv_attr >+++ b/selftest/knownfail.d/priv_attr >@@ -14,14 +14,10 @@ samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccoun > samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_WP_user > samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_computer > samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_user >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_WP_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_WP_user >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_default_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_default_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_WP_computer >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_WP_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_default_computer >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_admin-add_default_user >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_WP_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_WP_user >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_default_computer >+samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_default_user > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_WP_computer > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_WP_user > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_default_computer >diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c >index 7ecf8c43040..47af8c8733c 100644 >--- a/source4/dsdb/samdb/ldb_modules/password_hash.c >+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c >@@ -2496,6 +2496,59 @@ static int setup_password_fields(struct setup_password_fields_io *io) > return LDB_SUCCESS; > } > >+ if (io->u.is_krbtgt) { >+ size_t min = 196; >+ size_t max = 255; >+ size_t diff = max - min; >+ size_t len = max; >+ struct ldb_val *krbtgt_utf16 = NULL; >+ >+ if (!io->ac->pwd_reset) { >+ return dsdb_module_werror(io->ac->module, >+ LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS, >+ WERR_DS_ATT_ALREADY_EXISTS, >+ "Password change on krbtgt not permitted!"); >+ } >+ >+ if (io->n.cleartext_utf16 == NULL) { >+ return dsdb_module_werror(io->ac->module, >+ LDB_ERR_UNWILLING_TO_PERFORM, >+ WERR_DS_INVALID_ATTRIBUTE_SYNTAX, >+ "Password reset on krbtgt requires UTF16!"); >+ } >+ >+ /* >+ * Instead of taking the callers value, >+ * we just generate a new random value here. >+ * >+ * Include null termination in the array. >+ */ >+ if (diff > 0) { >+ size_t tmp; >+ >+ generate_random_buffer((uint8_t *)&tmp, sizeof(tmp)); >+ >+ tmp %= diff; >+ >+ len = min + tmp; >+ } >+ >+ krbtgt_utf16 = talloc_zero(io->ac, struct ldb_val); >+ if (krbtgt_utf16 == NULL) { >+ return ldb_oom(ldb); >+ } >+ >+ *krbtgt_utf16 = data_blob_talloc_zero(krbtgt_utf16, >+ (len+1)*2); >+ if (krbtgt_utf16->data == NULL) { >+ return ldb_oom(ldb); >+ } >+ krbtgt_utf16->length = len * 2; >+ generate_secret_buffer(krbtgt_utf16->data, >+ krbtgt_utf16->length); >+ io->n.cleartext_utf16 = krbtgt_utf16; >+ } >+ > /* transform the old password (for password changes) */ > ret = setup_given_passwords(io, &io->og); > if (ret != LDB_SUCCESS) { >@@ -3673,59 +3726,6 @@ static int setup_io(struct ph_context *ac, > return ldb_operr(ldb); > } > >- if (io->u.is_krbtgt) { >- size_t min = 196; >- size_t max = 255; >- size_t diff = max - min; >- size_t len = max; >- struct ldb_val *krbtgt_utf16 = NULL; >- >- if (!ac->pwd_reset) { >- return dsdb_module_werror(ac->module, >- LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS, >- WERR_DS_ATT_ALREADY_EXISTS, >- "Password change on krbtgt not permitted!"); >- } >- >- if (io->n.cleartext_utf16 == NULL) { >- return dsdb_module_werror(ac->module, >- LDB_ERR_UNWILLING_TO_PERFORM, >- WERR_DS_INVALID_ATTRIBUTE_SYNTAX, >- "Password reset on krbtgt requires UTF16!"); >- } >- >- /* >- * Instead of taking the callers value, >- * we just generate a new random value here. >- * >- * Include null termination in the array. >- */ >- if (diff > 0) { >- size_t tmp; >- >- generate_random_buffer((uint8_t *)&tmp, sizeof(tmp)); >- >- tmp %= diff; >- >- len = min + tmp; >- } >- >- krbtgt_utf16 = talloc_zero(io->ac, struct ldb_val); >- if (krbtgt_utf16 == NULL) { >- return ldb_oom(ldb); >- } >- >- *krbtgt_utf16 = data_blob_talloc_zero(krbtgt_utf16, >- (len+1)*2); >- if (krbtgt_utf16->data == NULL) { >- return ldb_oom(ldb); >- } >- krbtgt_utf16->length = len * 2; >- generate_secret_buffer(krbtgt_utf16->data, >- krbtgt_utf16->length); >- io->n.cleartext_utf16 = krbtgt_utf16; >- } >- > if (existing_msg != NULL) { > NTSTATUS status; > >-- >2.25.1 > > >From cdb14f498a3829bc49c119d0d32c6abd3727fb41 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Fri, 13 Aug 2021 17:42:23 +1200 >Subject: [PATCH 003/190] CVE-2020-25722 dsdb: Restrict the setting of > privileged attributes during LDAP add/modify > >The remaining failures in the priv_attrs (not the strict one) test are >due to missing objectclass constraints on the administrator which should >be addressed, but are not a security issue. > >A better test for confirming constraints between objectclass and >userAccountControl UF_NORMAL_ACCONT/UF_WORKSTATION_TRUST values would >be user_account_control.py. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14703 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14778 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14775 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/priv_attr | 24 ---- > source4/dsdb/samdb/ldb_modules/samldb.c | 148 +++++++++++++++++++++--- > 2 files changed, 129 insertions(+), 43 deletions(-) > >diff --git a/selftest/knownfail.d/priv_attr b/selftest/knownfail.d/priv_attr >index ab6db192aae..c3a779010d9 100644 >--- a/selftest/knownfail.d/priv_attr >+++ b/selftest/knownfail.d/priv_attr >@@ -1,31 +1,7 @@ >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_WP_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_WP_user >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_default_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_default_user >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_user >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_default_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_default_user >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_WP_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_WP_user >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_default_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_default_user > samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_WP_computer > samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_WP_user > samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_computer > samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_user >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_WP_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_WP_user >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_default_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_default_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_WP_computer >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_WP_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_default_computer >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-AllowedToDelegateTo_add_CC_default_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_WP_computer >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_WP_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_default_computer >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_msDS-SecondaryKrbTgtNumber_add_CC_default_user > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_computer > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_user > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_default_computer >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index e3081cd13dc..a4b4783955c 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -1980,6 +1980,29 @@ static int samldb_check_user_account_control_invariants(struct samldb_ctx *ac, > return ret; > } > >+static int samldb_get_domain_secdesc(struct samldb_ctx *ac, >+ struct security_descriptor **domain_sd) >+{ >+ const char * const sd_attrs[] = {"ntSecurityDescriptor", NULL}; >+ struct ldb_result *res; >+ struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module)); >+ int ret = dsdb_module_search_dn(ac->module, ac, &res, >+ domain_dn, >+ sd_attrs, >+ DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, >+ ac->req); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ if (res->count != 1) { >+ return ldb_module_operr(ac->module); >+ } >+ >+ return dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(ac->module), >+ ac, res->msgs[0], domain_sd); >+ >+} >+ > /** > * Validate that the restriction in point 5 of MS-SAMR 3.1.1.8.10 userAccountControl is honoured > * >@@ -1992,12 +2015,8 @@ static int samldb_check_user_account_control_acl(struct samldb_ctx *ac, > size_t i; > int ret = 0; > bool need_acl_check = false; >- struct ldb_result *res; >- const char * const sd_attrs[] = {"ntSecurityDescriptor", NULL}; > struct security_token *user_token; > struct security_descriptor *domain_sd; >- struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module)); >- struct ldb_context *ldb = ldb_module_get_ctx(ac->module); > const struct uac_to_guid { > uint32_t uac; > uint32_t priv_to_change_from; >@@ -2083,21 +2102,7 @@ static int samldb_check_user_account_control_acl(struct samldb_ctx *ac, > return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; > } > >- ret = dsdb_module_search_dn(ac->module, ac, &res, >- domain_dn, >- sd_attrs, >- DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, >- ac->req); >- if (ret != LDB_SUCCESS) { >- return ret; >- } >- if (res->count != 1) { >- return ldb_module_operr(ac->module); >- } >- >- ret = dsdb_get_sd_from_ldb_message(ldb, >- ac, res->msgs[0], &domain_sd); >- >+ ret = samldb_get_domain_secdesc(ac, &domain_sd); > if (ret != LDB_SUCCESS) { > return ret; > } >@@ -2159,6 +2164,8 @@ static int samldb_check_user_account_control_acl(struct samldb_ctx *ac, > return ldb_module_operr(ac->module); > } > if (map[i].guid) { >+ struct ldb_dn *domain_dn >+ = ldb_get_default_basedn(ldb_module_get_ctx(ac->module)); > dsdb_acl_debug(domain_sd, acl_user_token(ac->module), > domain_dn, > true, >@@ -3491,7 +3498,98 @@ static char *refer_if_rodc(struct ldb_context *ldb, struct ldb_request *req, > return NULL; > } > >+/* >+ * Restrict all access to sensitive attributes. >+ * >+ * We don't want to even inspect the values, so we can use the same >+ * routine for ADD and MODIFY. >+ * >+ */ >+ >+static int samldb_check_sensitive_attributes(struct samldb_ctx *ac) >+{ >+ struct ldb_message_element *el = NULL; >+ struct security_token *user_token = NULL; >+ int ret; >+ >+ if (dsdb_module_am_system(ac->module)) { >+ return LDB_SUCCESS; >+ } >+ >+ user_token = acl_user_token(ac->module); >+ if (user_token == NULL) { >+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; >+ } >+ >+ el = ldb_msg_find_element(ac->msg, "sidHistory"); >+ if (el) { >+ /* >+ * sidHistory is restricted to the (not implemented >+ * yet in Samba) DsAddSidHistory call (direct LDB access is >+ * as SYSTEM so will bypass this). >+ * >+ * If you want to modify this, say to merge domains, >+ * directly modify the sam.ldb as root. >+ */ >+ ldb_asprintf_errstring(ldb_module_get_ctx(ac->module), >+ "sidHistory " >+ "(entry %s) cannot be created " >+ "or changed over LDAP!", >+ ldb_dn_get_linearized(ac->msg->dn)); >+ return LDB_ERR_UNWILLING_TO_PERFORM; >+ } > >+ el = ldb_msg_find_element(ac->msg, "msDS-SecondaryKrbTgtNumber"); >+ if (el) { >+ struct security_descriptor *domain_sd; >+ /* >+ * msDS-SecondaryKrbTgtNumber allows the creator to >+ * become an RODC, this is trusted as an RODC >+ * account >+ */ >+ ret = samldb_get_domain_secdesc(ac, &domain_sd); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ ret = acl_check_extended_right(ac, domain_sd, >+ user_token, >+ GUID_DRS_DS_INSTALL_REPLICA, >+ SEC_ADS_CONTROL_ACCESS, >+ NULL); >+ if (ret != LDB_SUCCESS) { >+ ldb_asprintf_errstring(ldb_module_get_ctx(ac->module), >+ "msDS-SecondaryKrbTgtNumber " >+ "(entry %s) cannot be created " >+ "or changed without " >+ "DS-Install-Replica extended right!", >+ ldb_dn_get_linearized(ac->msg->dn)); >+ return ret; >+ } >+ } >+ >+ el = ldb_msg_find_element(ac->msg, "msDS-AllowedToDelegateTo"); >+ if (el) { >+ /* >+ * msDS-AllowedToDelegateTo is incredibly powerful, >+ * given that it allows a server to become ANY USER on >+ * the target server only listed by SPN so needs to be >+ * protected just as the userAccountControl >+ * UF_TRUSTED_FOR_DELEGATION is. >+ */ >+ >+ bool have_priv = security_token_has_privilege(user_token, >+ SEC_PRIV_ENABLE_DELEGATION); >+ if (have_priv == false) { >+ ldb_asprintf_errstring(ldb_module_get_ctx(ac->module), >+ "msDS-AllowedToDelegateTo " >+ "(entry %s) cannot be created " >+ "or changed without SePrivEnableDelegation!", >+ ldb_dn_get_linearized(ac->msg->dn)); >+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; >+ } >+ } >+ return LDB_SUCCESS; >+} > /* add */ > static int samldb_add(struct ldb_module *module, struct ldb_request *req) > { >@@ -3538,6 +3636,12 @@ static int samldb_add(struct ldb_module *module, struct ldb_request *req) > return ldb_operr(ldb); > } > >+ ret = samldb_check_sensitive_attributes(ac); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(ac); >+ return ret; >+ } >+ > el = ldb_msg_find_element(ac->msg, "fSMORoleOwner"); > if (el != NULL) { > ret = samldb_fsmo_role_owner_check(ac); >@@ -3745,6 +3849,12 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req) > return ldb_operr(ldb); > } > >+ ret = samldb_check_sensitive_attributes(ac); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(ac); >+ return ret; >+ } >+ > if (is_undelete == NULL) { > el = ldb_msg_find_element(ac->msg, "primaryGroupID"); > if (el != NULL) { >-- >2.25.1 > > >From e1f6194cd79c47cee1c652e3dcaffa4e4ab19671 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Mon, 13 Sep 2021 20:34:54 +1200 >Subject: [PATCH 004/190] CVE-2020-25722 selftest: Extend priv_attrs test - > work around UF_NORMAL_ACCOUNT rules on Windows 2019 (requires > |UF_PASSWD_NOTREQD or a password) - extend to also cover the sensitive > UF_TRUSTED_FOR_DELEGATION > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14703 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14778 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14775 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/priv_attr | 16 ++-------- > source4/dsdb/tests/python/priv_attrs.py | 40 +++++++++++++++---------- > 2 files changed, 27 insertions(+), 29 deletions(-) > >diff --git a/selftest/knownfail.d/priv_attr b/selftest/knownfail.d/priv_attr >index c3a779010d9..4b85a869089 100644 >--- a/selftest/knownfail.d/priv_attr >+++ b/selftest/knownfail.d/priv_attr >@@ -1,7 +1,3 @@ >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_WP_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_WP_user >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_computer >-samba4.priv_attrs.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_user > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_computer > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_user > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_default_computer >@@ -14,13 +10,5 @@ samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_use > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-RODC_add_CC_default_user > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-computer_add_CC_WP_user > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-computer_add_CC_default_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_CC_WP_computer >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_CC_WP_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_CC_default_computer >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_CC_default_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_WP_computer >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_WP_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_computer >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_admin-add_default_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_mod-del-add_CC_default_computer >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_mod-replace_CC_default_computer >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-t4d-computer_add_CC_WP_user >+samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-t4d-computer_add_CC_default_user >diff --git a/source4/dsdb/tests/python/priv_attrs.py b/source4/dsdb/tests/python/priv_attrs.py >index ec2b13045e5..aa35dcc1317 100644 >--- a/source4/dsdb/tests/python/priv_attrs.py >+++ b/source4/dsdb/tests/python/priv_attrs.py >@@ -99,30 +99,47 @@ attrs = {"sidHistory": > {"value": ndr_pack(security.dom_sid(security.SID_BUILTIN_ADMINISTRATORS)), > "priv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, > "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS}, >- "msDS-AllowedToDelegateTo": >+ >+ "msDS-AllowedToDelegateTo": > {"value": f"host/{host}", > "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS}, >- "userAccountControl-a2d-user": >+ >+ "userAccountControl-a2d-user": > {"attr": "userAccountControl", >- "value": str(UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION|UF_NORMAL_ACCOUNT), >- "priv-error": ldb.ERR_UNWILLING_TO_PERFORM, >- "unpriv-add-error": ldb.ERR_UNWILLING_TO_PERFORM, >+ "value": str(UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION|UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD), > "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS}, >- "userAccountControl-a2d-computer": >+ >+ "userAccountControl-a2d-computer": > {"attr": "userAccountControl", > "value": str(UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION|UF_WORKSTATION_TRUST_ACCOUNT), > "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, > "only-1": "computer"}, >- "userAccountControl-DC": >+ >+ # This flag makes many legitimate authenticated clients >+ # send a forwardable ticket-granting-ticket to the server >+ "userAccountControl-t4d-user": >+ {"attr": "userAccountControl", >+ "value": str(UF_TRUSTED_FOR_DELEGATION|UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD), >+ "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS}, >+ >+ "userAccountControl-t4d-computer": >+ {"attr": "userAccountControl", >+ "value": str(UF_TRUSTED_FOR_DELEGATION|UF_WORKSTATION_TRUST_ACCOUNT), >+ "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, >+ "only-1": "computer"}, >+ >+ "userAccountControl-DC": > {"attr": "userAccountControl", > "value": str(UF_SERVER_TRUST_ACCOUNT), > "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, > "only-2": "computer"}, >- "userAccountControl-RODC": >+ >+ "userAccountControl-RODC": > {"attr": "userAccountControl", > "value": str(UF_PARTIAL_SECRETS_ACCOUNT|UF_WORKSTATION_TRUST_ACCOUNT), > "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, > "only-1": "computer"}, >+ > "msDS-SecondaryKrbTgtNumber": > {"value": "65536", > "unpriv-error": ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS}, >@@ -369,13 +386,6 @@ class PrivAttrsTests(samba.tests.TestCase): > self.fail(f"{test_name}: Unexpectedly able to set {attr} on {m.dn}") > except LdbError as e5: > (enum, estr) = e5.args >- if attr == "userAccountControl" and sd == "default": >- # We get a different error if we try and swap between >- # being a computer back to being a user when created with "Create child" permissions >- if (int(attrs[test_name]["value"]) & UF_NORMAL_ACCOUNT) \ >- and objectclass == "computer" and permission == "CC": >- self.assertGotLdbError(ldb.ERR_UNWILLING_TO_PERFORM, enum) >- return > self.assertGotLdbError(attrs[test_name]["unpriv-error"], enum) > > >-- >2.25.1 > > >From 3c2fde375f33f44b3347addb7e19fa744afe5b43 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Mon, 13 Sep 2021 10:21:03 +1200 >Subject: [PATCH 005/190] CVE-2020-25722 selftest: Test combinations of account > type and objectclass for creating a user > >The idea here is to split out the restrictions seen on Windows 2019 >at the schema level, as seen when acting as an administrator. > >These pass against Windows 2019 except for the account type swapping >which is not wanted. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > .../user_account_control-uac_mod_lock | 11 ++ > .../dsdb/tests/python/user_account_control.py | 169 ++++++++++++++++++ > 2 files changed, 180 insertions(+) > create mode 100644 selftest/knownfail.d/user_account_control-uac_mod_lock > >diff --git a/selftest/knownfail.d/user_account_control-uac_mod_lock b/selftest/knownfail.d/user_account_control-uac_mod_lock >new file mode 100644 >index 00000000000..a70534506f3 >--- /dev/null >+++ b/selftest/knownfail.d/user_account_control-uac_mod_lock >@@ -0,0 +1,11 @@ >+# We do not want user account control account type swapping, so we mark these as knownfail >+^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_computer_replace >+^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_user_replace >+^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_SERVER_TRUST_ACCOUNT_computer_replace >+^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_replace >+^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_deladd >+^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_replace >+^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd >+^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace >+^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd >+^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace >diff --git a/source4/dsdb/tests/python/user_account_control.py b/source4/dsdb/tests/python/user_account_control.py >index 4602b3e97d4..30ff259f8b7 100755 >--- a/source4/dsdb/tests/python/user_account_control.py >+++ b/source4/dsdb/tests/python/user_account_control.py >@@ -90,6 +90,41 @@ account_types = set([UF_NORMAL_ACCOUNT, UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_ > class UserAccountControlTests(samba.tests.TestCase): > @classmethod > def setUpDynamicTestCases(cls): >+ for account_type in [UF_NORMAL_ACCOUNT, >+ UF_WORKSTATION_TRUST_ACCOUNT, >+ UF_SERVER_TRUST_ACCOUNT]: >+ account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type) >+ for objectclass in ["computer", "user"]: >+ test_name = f"{account_type_str}_{objectclass}" >+ cls.generate_dynamic_test("test_objectclass_uac_lock", >+ test_name, >+ account_type, >+ objectclass) >+ >+ for account_type in [UF_NORMAL_ACCOUNT, >+ UF_WORKSTATION_TRUST_ACCOUNT, >+ UF_SERVER_TRUST_ACCOUNT]: >+ account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type) >+ for account_type2 in [UF_NORMAL_ACCOUNT, >+ UF_WORKSTATION_TRUST_ACCOUNT, >+ UF_SERVER_TRUST_ACCOUNT]: >+ for how in ["replace", "deladd"]: >+ account_type2_str = dsdb.user_account_control_flag_bit_to_string(account_type2) >+ test_name = f"{account_type_str}_{account_type2_str}_{how}" >+ cls.generate_dynamic_test("test_objectclass_uac_mod_lock", >+ test_name, >+ account_type, >+ account_type2, >+ how) >+ for objectclass in ["user", "computer"]: >+ for how in ["replace", "deladd"]: >+ test_name = f"{account_type_str}_{objectclass}_{how}" >+ cls.generate_dynamic_test("test_objectclass_mod_lock", >+ test_name, >+ account_type, >+ objectclass, >+ how) >+ > for account_type in [UF_NORMAL_ACCOUNT, UF_WORKSTATION_TRUST_ACCOUNT]: > account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type) > cls.generate_dynamic_test("test_uac_bits_unrelated_modify", >@@ -843,6 +878,140 @@ class UserAccountControlTests(samba.tests.TestCase): > "primaryGroupID") > self.admin_samdb.modify(m) > >+ def _test_objectclass_uac_lock_with_args(self, >+ account_type, >+ objectclass): >+ name = "oc_uac_lock$" >+ dn = "CN=%s,%s" % (name, self.OU) >+ msg_dict = { >+ "dn": dn, >+ "objectclass": objectclass, >+ "samAccountName": name, >+ "userAccountControl": str(account_type | UF_PASSWD_NOTREQD)} >+ >+ account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type) >+ >+ print(f"Adding account {name} as {account_type_str} with objectclass {objectclass}") >+ >+ try: >+ self.admin_samdb.add(msg_dict) >+ if (objectclass == "user" \ >+ and account_type != UF_NORMAL_ACCOUNT): >+ self.fail("Able to create {account_type_str} on {objectclass}") >+ except LdbError as e: >+ (enum, estr) = e.args >+ self.assertEqual(enum, ldb.ERR_OBJECT_CLASS_VIOLATION) >+ >+ def _test_objectclass_uac_mod_lock_with_args(self, >+ account_type, >+ account_type2, >+ how): >+ name = "uac_mod_lock$" >+ dn = "CN=%s,%s" % (name, self.OU) >+ if account_type == UF_NORMAL_ACCOUNT: >+ objectclass = "user" >+ else: >+ objectclass = "computer" >+ >+ msg_dict = { >+ "dn": dn, >+ "objectclass": objectclass, >+ "samAccountName": name, >+ "userAccountControl": str(account_type | UF_PASSWD_NOTREQD)} >+ >+ account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type) >+ >+ print(f"Adding account {name} as {account_type_str} with objectclass {objectclass}") >+ >+ self.admin_samdb.add(msg_dict) >+ >+ m = ldb.Message() >+ m.dn = ldb.Dn(self.admin_samdb, dn) >+ if how == "replace": >+ m["userAccountControl"] = ldb.MessageElement(str(account_type2 | UF_PASSWD_NOTREQD), >+ ldb.FLAG_MOD_REPLACE, "userAccountControl") >+ elif how == "deladd": >+ m["0userAccountControl"] = ldb.MessageElement([], >+ ldb.FLAG_MOD_DELETE, "userAccountControl") >+ m["1userAccountControl"] = ldb.MessageElement(str(account_type2 | UF_PASSWD_NOTREQD), >+ ldb.FLAG_MOD_ADD, "userAccountControl") >+ else: >+ raise AssertionError(f"{how} was not a valid argument") >+ >+ try: >+ self.admin_samdb.modify(m) >+ if account_type == UF_NORMAL_ACCOUNT \ >+ and account_type2 in [UF_SERVER_TRUST_ACCOUNT, >+ UF_WORKSTATION_TRUST_ACCOUNT]: >+ account_type2_str \ >+ = dsdb.user_account_control_flag_bit_to_string(account_type2) >+ self.fail("Able to change {account_type_str} to {account_type2_str") >+ if account_type2 == UF_NORMAL_ACCOUNT \ >+ and account_type in [UF_SERVER_TRUST_ACCOUNT, >+ UF_WORKSTATION_TRUST_ACCOUNT]: >+ account_type2_str \ >+ = dsdb.user_account_control_flag_bit_to_string(account_type2) >+ self.fail("Able to change {account_type_str} to {account_type2_str") >+ except LdbError as e: >+ (enum, estr) = e.args >+ self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM) >+ >+ def _test_objectclass_mod_lock_with_args(self, >+ account_type, >+ objectclass, >+ how): >+ name = "uac_mod_lock$" >+ dn = "CN=%s,%s" % (name, self.OU) >+ if objectclass == "computer": >+ new_objectclass = ["top", >+ "person", >+ "organizationalPerson", >+ "user"] >+ elif objectclass == "user": >+ new_objectclass = ["top", >+ "person", >+ "organizationalPerson", >+ "user", >+ "computer"] >+ >+ msg_dict = { >+ "dn": dn, >+ "objectclass": objectclass, >+ "samAccountName": name, >+ "userAccountControl": str(account_type | UF_PASSWD_NOTREQD)} >+ >+ account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type) >+ >+ print(f"Adding account {name} as {account_type_str} with objectclass {objectclass}") >+ >+ try: >+ self.admin_samdb.add(msg_dict) >+ if (objectclass == "user" \ >+ and account_type != UF_NORMAL_ACCOUNT): >+ self.fail("Able to create {account_type_str} on {objectclass}") >+ except LdbError as e: >+ (enum, estr) = e.args >+ self.assertEqual(enum, ldb.ERR_OBJECT_CLASS_VIOLATION) >+ >+ if objectclass == "user" and account_type != UF_NORMAL_ACCOUNT: >+ return >+ >+ m = ldb.Message() >+ m.dn = ldb.Dn(self.admin_samdb, dn) >+ if how == "replace": >+ m["objectclass"] = ldb.MessageElement(new_objectclass, >+ ldb.FLAG_MOD_REPLACE, "objectclass") >+ elif how == "adddel": >+ m["0objectclass"] = ldb.MessageElement([], >+ ldb.FLAG_MOD_DELETE, "objectclass") >+ m["1objectclass"] = ldb.MessageElement(new_objectclass, >+ ldb.FLAG_MOD_ADD, "objectclass") >+ try: >+ self.admin_samdb.modify(m) >+ self.fail("Able to change objectclass of a {objectclass}") >+ except LdbError as e: >+ (enum, estr) = e.args >+ self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM) > > runner = SubunitTestRunner() > rc = 0 >-- >2.25.1 > > >From 0cd4dbb299584b7720fc61c7289328c814f08e16 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Mon, 13 Sep 2021 21:48:13 +1200 >Subject: [PATCH 006/190] CVE-2020-25722 selftest: Move to using > self.assertRaisesLdbError() in user_account_control.py > >This is easier to reason with regaridng which cases should work >and which cases should fail, avoiding issues where more failures >than expected would be OK. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > .../dsdb/tests/python/user_account_control.py | 56 +++++++++---------- > 1 file changed, 26 insertions(+), 30 deletions(-) > >diff --git a/source4/dsdb/tests/python/user_account_control.py b/source4/dsdb/tests/python/user_account_control.py >index 30ff259f8b7..d6698490af8 100755 >--- a/source4/dsdb/tests/python/user_account_control.py >+++ b/source4/dsdb/tests/python/user_account_control.py >@@ -893,14 +893,15 @@ class UserAccountControlTests(samba.tests.TestCase): > > print(f"Adding account {name} as {account_type_str} with objectclass {objectclass}") > >- try: >+ if (objectclass == "user" \ >+ and account_type == UF_NORMAL_ACCOUNT): > self.admin_samdb.add(msg_dict) >- if (objectclass == "user" \ >- and account_type != UF_NORMAL_ACCOUNT): >- self.fail("Able to create {account_type_str} on {objectclass}") >- except LdbError as e: >- (enum, estr) = e.args >- self.assertEqual(enum, ldb.ERR_OBJECT_CLASS_VIOLATION) >+ elif objectclass == "computer": >+ self.admin_samdb.add(msg_dict) >+ else: >+ self.assertRaisesLdbError(ldb.ERR_OBJECT_CLASS_VIOLATION, >+ "Should have been unable to {account_type_str} on {objectclass}", >+ self.admin_samdb.add, msg_dict) > > def _test_objectclass_uac_mod_lock_with_args(self, > account_type, >@@ -919,7 +920,10 @@ class UserAccountControlTests(samba.tests.TestCase): > "samAccountName": name, > "userAccountControl": str(account_type | UF_PASSWD_NOTREQD)} > >- account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type) >+ account_type_str \ >+ = dsdb.user_account_control_flag_bit_to_string(account_type) >+ account_type2_str \ >+ = dsdb.user_account_control_flag_bit_to_string(account_type2) > > print(f"Adding account {name} as {account_type_str} with objectclass {objectclass}") > >@@ -938,23 +942,17 @@ class UserAccountControlTests(samba.tests.TestCase): > else: > raise AssertionError(f"{how} was not a valid argument") > >- try: >+ if (account_type in [UF_SERVER_TRUST_ACCOUNT, >+ UF_WORKSTATION_TRUST_ACCOUNT]) and \ >+ (account_type2 in [UF_SERVER_TRUST_ACCOUNT, >+ UF_WORKSTATION_TRUST_ACCOUNT]): > self.admin_samdb.modify(m) >- if account_type == UF_NORMAL_ACCOUNT \ >- and account_type2 in [UF_SERVER_TRUST_ACCOUNT, >- UF_WORKSTATION_TRUST_ACCOUNT]: >- account_type2_str \ >- = dsdb.user_account_control_flag_bit_to_string(account_type2) >- self.fail("Able to change {account_type_str} to {account_type2_str") >- if account_type2 == UF_NORMAL_ACCOUNT \ >- and account_type in [UF_SERVER_TRUST_ACCOUNT, >- UF_WORKSTATION_TRUST_ACCOUNT]: >- account_type2_str \ >- = dsdb.user_account_control_flag_bit_to_string(account_type2) >- self.fail("Able to change {account_type_str} to {account_type2_str") >- except LdbError as e: >- (enum, estr) = e.args >- self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM) >+ elif (account_type == account_type2): >+ self.admin_samdb.modify(m) >+ else: >+ self.assertRaisesLdbError(ldb.ERR_UNWILLING_TO_PERFORM, >+ f"Should have been unable to change {account_type_str} to {account_type2_str}", >+ self.admin_samdb.modify, m) > > def _test_objectclass_mod_lock_with_args(self, > account_type, >@@ -1006,12 +1004,10 @@ class UserAccountControlTests(samba.tests.TestCase): > ldb.FLAG_MOD_DELETE, "objectclass") > m["1objectclass"] = ldb.MessageElement(new_objectclass, > ldb.FLAG_MOD_ADD, "objectclass") >- try: >- self.admin_samdb.modify(m) >- self.fail("Able to change objectclass of a {objectclass}") >- except LdbError as e: >- (enum, estr) = e.args >- self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM) >+ >+ self.assertRaisesLdbError(ldb.ERR_UNWILLING_TO_PERFORM, >+ "Should have been unable Able to change objectclass of a {objectclass}", >+ self.admin_samdb.modify, m) > > runner = SubunitTestRunner() > rc = 0 >-- >2.25.1 > > >From 460ee8e119370305d0c48978b8f2f343ef56e17d Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Mon, 20 Sep 2021 12:35:51 +1200 >Subject: [PATCH 007/190] CVE-2020-25722 selftest: allow for future failures in > BindTests.test_virtual_email_account_style_bind > >This allows for any failures here to be handled via the knownfail system. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > auth/credentials/tests/bind.py | 13 +++++++++++-- > 1 file changed, 11 insertions(+), 2 deletions(-) > >diff --git a/auth/credentials/tests/bind.py b/auth/credentials/tests/bind.py >index a38021b5ded..ce81b736e86 100755 >--- a/auth/credentials/tests/bind.py >+++ b/auth/credentials/tests/bind.py >@@ -92,7 +92,8 @@ class BindTests(samba.tests.TestCase): > # this test to detect when the LDAP DN is being double-parsed > # but must be in the user@realm style to allow the account to > # be created >- self.ldb.add_ldif(""" >+ try: >+ self.ldb.add_ldif(""" > dn: """ + self.virtual_user_dn + """ > cn: frednurk@""" + self.realm + """ > displayName: Fred Nurk >@@ -105,13 +106,21 @@ objectClass: person > objectClass: top > objectClass: user > """) >+ except LdbError as e: >+ (num, msg) = e.args >+ self.fail(f"Failed to create e-mail user: {msg}") >+ > self.addCleanup(delete_force, self.ldb, self.virtual_user_dn) >- self.ldb.modify_ldif(""" >+ try: >+ self.ldb.modify_ldif(""" > dn: """ + self.virtual_user_dn + """ > changetype: modify > replace: unicodePwd > unicodePwd:: """ + base64.b64encode(u"\"P@ssw0rd\"".encode('utf-16-le')).decode('utf8') + """ > """) >+ except LdbError as e: >+ (num, msg) = e.args >+ self.fail(f"Failed to set password on e-mail user: {msg}") > > self.ldb.enable_account('distinguishedName=%s' % self.virtual_user_dn) > >-- >2.25.1 > > >From dab919cdcb7be8dff0997780396936c4d5979e82 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Mon, 20 Sep 2021 14:54:03 +1200 >Subject: [PATCH 008/190] CVE-2020-25722 selftest: Catch possible errors in > PasswordSettingsTestCase.test_pso_none_applied() > >This allows future patches to restrict changing the account type >without triggering an error. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > .../dsdb/tests/python/password_settings.py | 34 +++++++++++++------ > 1 file changed, 23 insertions(+), 11 deletions(-) > >diff --git a/source4/dsdb/tests/python/password_settings.py b/source4/dsdb/tests/python/password_settings.py >index fcb671690c3..678822f7352 100644 >--- a/source4/dsdb/tests/python/password_settings.py >+++ b/source4/dsdb/tests/python/password_settings.py >@@ -594,19 +594,31 @@ class PasswordSettingsTestCase(PasswordTestCase): > dummy_pso.apply_to(user.dn) > self.assertTrue(user.get_resultant_PSO() == dummy_pso.dn) > >- # now clear the ADS_UF_NORMAL_ACCOUNT flag for the user, which should >- # mean a resultant PSO is no longer returned (we're essentially turning >- # the user into a DC here, which is a little overkill but tests >- # behaviour as per the Windows specification) >- self.set_attribute(user.dn, "userAccountControl", >- str(dsdb.UF_WORKSTATION_TRUST_ACCOUNT), >- operation=FLAG_MOD_REPLACE) >+ try: >+ # now clear the ADS_UF_NORMAL_ACCOUNT flag for the user, which should >+ # mean a resultant PSO is no longer returned (we're essentially turning >+ # the user into a DC here, which is a little overkill but tests >+ # behaviour as per the Windows specification) >+ self.set_attribute(user.dn, "userAccountControl", >+ str(dsdb.UF_WORKSTATION_TRUST_ACCOUNT), >+ operation=FLAG_MOD_REPLACE) >+ except ldb.LdbError as e: >+ (num, msg) = e.args >+ self.fail("Failed to change user into a workstation: {msg}") > self.assertIsNone(user.get_resultant_PSO()) > >- # reset it back to a normal user account >- self.set_attribute(user.dn, "userAccountControl", >- str(dsdb.UF_NORMAL_ACCOUNT), >- operation=FLAG_MOD_REPLACE) >+ try: >+ # now clear the ADS_UF_NORMAL_ACCOUNT flag for the user, which should >+ # mean a resultant PSO is no longer returned (we're essentially turning >+ # the user into a DC here, which is a little overkill but tests >+ # behaviour as per the Windows specification) >+ # reset it back to a normal user account >+ self.set_attribute(user.dn, "userAccountControl", >+ str(dsdb.UF_NORMAL_ACCOUNT), >+ operation=FLAG_MOD_REPLACE) >+ except ldb.LdbError as e: >+ (num, msg) = e.args >+ self.fail("Failed to change user back into a user: {msg}") > self.assertTrue(user.get_resultant_PSO() == dummy_pso.dn) > > # no PSO should be returned if RID is equal to DOMAIN_USER_RID_KRBTGT >-- >2.25.1 > > >From 177ecfedb518837f2c61038b099b95358209d279 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Fri, 17 Sep 2021 13:41:40 +1200 >Subject: [PATCH 009/190] CVE-2020-25722 selftest: Catch errors from > samdb.modify() in user_account_control tests > >This will allow these to be listed in a knownfail shortly. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > .../dsdb/tests/python/user_account_control.py | 31 ++++++++++++++++--- > 1 file changed, 26 insertions(+), 5 deletions(-) > >diff --git a/source4/dsdb/tests/python/user_account_control.py b/source4/dsdb/tests/python/user_account_control.py >index d6698490af8..2a3dd34c53a 100755 >--- a/source4/dsdb/tests/python/user_account_control.py >+++ b/source4/dsdb/tests/python/user_account_control.py >@@ -305,7 +305,11 @@ class UserAccountControlTests(samba.tests.TestCase): > m.dn = res[0].dn > m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD), > ldb.FLAG_MOD_REPLACE, "userAccountControl") >- self.samdb.modify(m) >+ try: >+ self.samdb.modify(m) >+ except LdbError as e: >+ (enum, estr) = e.args >+ self.fail(f"got {estr} setting userAccountControl to UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD") > > m = ldb.Message() > m.dn = res[0].dn >@@ -360,7 +364,11 @@ class UserAccountControlTests(samba.tests.TestCase): > m.dn = res[0].dn > m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD), > ldb.FLAG_MOD_REPLACE, "userAccountControl") >- self.samdb.modify(m) >+ try: >+ self.samdb.modify(m) >+ except LdbError as e: >+ (enum, estr) = e.args >+ self.fail(f"got {estr} setting userAccountControl to UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD") > > m = ldb.Message() > m.dn = res[0].dn >@@ -457,7 +465,11 @@ class UserAccountControlTests(samba.tests.TestCase): > m.dn = res[0].dn > m["userAccountControl"] = ldb.MessageElement(str(UF_ACCOUNTDISABLE), > ldb.FLAG_MOD_REPLACE, "userAccountControl") >- self.admin_samdb.modify(m) >+ try: >+ self.admin_samdb.modify(m) >+ except LdbError as e: >+ (enum, estr) = e.args >+ self.fail(f"got {estr} setting userAccountControl to UF_ACCOUNTDISABLE (as admin)") > > res = self.admin_samdb.search("%s" % self.base_dn, > expression="(&(objectClass=computer)(samAccountName=%s$))" % computername, >@@ -578,7 +590,11 @@ class UserAccountControlTests(samba.tests.TestCase): > m.dn = res[0].dn > m["userAccountControl"] = ldb.MessageElement(str(orig_uac), > ldb.FLAG_MOD_REPLACE, "userAccountControl") >- self.admin_samdb.modify(m) >+ try: >+ self.admin_samdb.modify(m) >+ except LdbError as e: >+ (enum, estr) = e.args >+ self.fail(f"got {estr} resetting userAccountControl to initial value {orig_uac:#08x}") > > res = self.admin_samdb.search("%s" % self.base_dn, > expression="(&(objectClass=computer)(samAccountName=%s$))" % computername, >@@ -897,7 +913,12 @@ class UserAccountControlTests(samba.tests.TestCase): > and account_type == UF_NORMAL_ACCOUNT): > self.admin_samdb.add(msg_dict) > elif objectclass == "computer": >- self.admin_samdb.add(msg_dict) >+ try: >+ self.admin_samdb.add(msg_dict) >+ except ldb.LdbError as e: >+ (num, msg) = e.args >+ self.fail("Failed to create {objectclass} account " >+ "with {account_type_string}") > else: > self.assertRaisesLdbError(ldb.ERR_OBJECT_CLASS_VIOLATION, > "Should have been unable to {account_type_str} on {objectclass}", >-- >2.25.1 > > >From 6ae98b9b8b2cae7e527d71a74f28c94aca31d306 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 16 Sep 2021 08:46:42 +1200 >Subject: [PATCH 010/190] CVE-2020-25722 dsdb: objectclass computer becomes > UF_WORKSTATION_TRUST by default > >There are a lot of knownfail entries added with this commit. These >all need to be addressed and removed in subsequent commits which >will restructure the tests to pass within this new reality. > >This default applies even to users with administrator rights, >as changing the default based on permissions would break >to many assumptions. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/uac_objectclass_restrict | 42 +++++++++++++++++++ > source4/dsdb/samdb/ldb_modules/samldb.c | 27 +++++++++--- > 2 files changed, 64 insertions(+), 5 deletions(-) > create mode 100644 selftest/knownfail.d/uac_objectclass_restrict > >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >new file mode 100644 >index 00000000000..a076f9cfedb >--- /dev/null >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -0,0 +1,42 @@ >+# Knownfail entries due to restricting the creation of computer/user >+# accounts (in terms of userAccountControl) that do not match the objectclass >+# >+# All these tests need to be fixed and the entries here removed >+ >+^samba4.sam.python\(fl2008r2dc\).__main__.SamTests.test_isCriticalSystemObject\(fl2008r2dc\) >+^samba4.sam.python\(fl2008r2dc\).__main__.SamTests.test_userAccountControl\(fl2008r2dc\) >+^samba4.sam.python\(fl2008r2dc\).__main__.SamTests.test_users_groups\(fl2008r2dc\) >+^samba4.ldap.python\(ad_dc_default\).__main__.BasicTests.test_all\(ad_dc_default\) >+^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_isCriticalSystemObject\(ad_dc_default\) >+^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_userAccountControl\(ad_dc_default\) >+^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_users_groups\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_add_computer_sd_cc\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_admin_mod_uac\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_computer_cc\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x10000000\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x20000000\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x40000000\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x80000000\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_00000004\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_00000400\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_00004000\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_00008000\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_ACCOUNTDISABLE\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_DONT_EXPIRE_PASSWD\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_DONT_REQUIRE_PREAUTH\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_HOMEDIR_REQUIRED\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_LOCKOUT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_MNS_LOGON_ACCOUNT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_NORMAL_ACCOUNT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_NOT_DELEGATED\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_NO_AUTH_DATA_REQUIRED\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_PASSWD_CANT_CHANGE\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_PASSWD_NOTREQD\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_PASSWORD_EXPIRED\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_SCRIPT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_SMARTCARD_REQUIRED\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_USE_AES_KEYS\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_USE_DES_KEY_ONLY\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_NORMAL_ACCOUNT\(ad_dc_default\) >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index a4b4783955c..da74462ec57 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -1416,19 +1416,33 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac) > > switch(ac->type) { > case SAMLDB_TYPE_USER: { >+ bool is_computer_objectclass; > bool uac_generated = false, uac_add_flags = false; >- >+ uint32_t default_user_account_control = UF_NORMAL_ACCOUNT; > /* Step 1.2: Default values */ > ret = dsdb_user_obj_set_defaults(ldb, ac->msg, ac->req); > if (ret != LDB_SUCCESS) return ret; > >+ is_computer_objectclass >+ = (samdb_find_attribute(ldb, >+ ac->msg, >+ "objectclass", >+ "computer") >+ != NULL); >+ >+ if (is_computer_objectclass) { >+ default_user_account_control >+ = UF_WORKSTATION_TRUST_ACCOUNT; >+ } >+ >+ > /* On add operations we might need to generate a > * "userAccountControl" (if it isn't specified). */ > el = ldb_msg_find_element(ac->msg, "userAccountControl"); > if ((el == NULL) && (ac->req->operation == LDB_ADD)) { > ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg, > "userAccountControl", >- UF_NORMAL_ACCOUNT); >+ default_user_account_control); > if (ret != LDB_SUCCESS) { > return ret; > } >@@ -1447,11 +1461,14 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac) > raw_uac = user_account_control; > /* > * "userAccountControl" = 0 or missing one of >- * the types means "UF_NORMAL_ACCOUNT". See >- * MS-SAMR 3.1.1.8.10 point 8 >+ * the types means "UF_NORMAL_ACCOUNT" >+ * or "UF_WORKSTATION_TRUST_ACCOUNT" (if a computer). >+ * See MS-SAMR 3.1.1.8.10 point 8 > */ > if ((user_account_control & UF_ACCOUNT_TYPE_MASK) == 0) { >- user_account_control = UF_NORMAL_ACCOUNT | user_account_control; >+ user_account_control >+ = default_user_account_control >+ | user_account_control; > uac_generated = true; > } > >-- >2.25.1 > > >From a5fe195b647746ee4314aa16c7aac3f310bd75b0 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Fri, 22 Oct 2021 15:42:08 +1300 >Subject: [PATCH 011/190] CVE-2020-25722 dsdb: Improve privileged and > unprivileged tests for objectclass/doller/UAC > >This helps ensure we cover off all the cases that matter >for objectclass/trailing-doller/userAccountControl > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/uac_dollar_lock | 1 + > selftest/knownfail.d/uac_objectclass_restrict | 18 +- > .../user_account_control-uac_mod_lock | 11 -- > .../dsdb/tests/python/user_account_control.py | 172 +++++++++++++----- > 4 files changed, 142 insertions(+), 60 deletions(-) > create mode 100644 selftest/knownfail.d/uac_dollar_lock > delete mode 100644 selftest/knownfail.d/user_account_control-uac_mod_lock > >diff --git a/selftest/knownfail.d/uac_dollar_lock b/selftest/knownfail.d/uac_dollar_lock >new file mode 100644 >index 00000000000..8c70c859fa4 >--- /dev/null >+++ b/selftest/knownfail.d/uac_dollar_lock >@@ -0,0 +1 @@ >+^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_cc_plain >\ No newline at end of file >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index a076f9cfedb..bb0787c1a48 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -13,6 +13,22 @@ > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_add_computer_sd_cc\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_admin_mod_uac\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_computer_cc\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_computer_replace\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_user_replace\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_SERVER_TRUST_ACCOUNT_computer_replace\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_replace\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_NORMAL_ACCOUNT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_NORMAL_ACCOUNT_computer_cc_plain\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_NORMAL_ACCOUNT_computer_cc_withdollar\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_SERVER_TRUST_ACCOUNT_user_cc_plain\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_SERVER_TRUST_ACCOUNT_user_cc_withdollar\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_deladd_wp\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_replace_wp\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd_wp\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace_wp\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd_wp\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace_wp\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x10000000\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x20000000\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x40000000\(ad_dc_default\) >@@ -38,5 +54,3 @@ > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_SMARTCARD_REQUIRED\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_USE_AES_KEYS\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_USE_DES_KEY_ONLY\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_NORMAL_ACCOUNT\(ad_dc_default\) >diff --git a/selftest/knownfail.d/user_account_control-uac_mod_lock b/selftest/knownfail.d/user_account_control-uac_mod_lock >deleted file mode 100644 >index a70534506f3..00000000000 >--- a/selftest/knownfail.d/user_account_control-uac_mod_lock >+++ /dev/null >@@ -1,11 +0,0 @@ >-# We do not want user account control account type swapping, so we mark these as knownfail >-^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_computer_replace >-^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_user_replace >-^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_SERVER_TRUST_ACCOUNT_computer_replace >-^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_replace >-^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_deladd >-^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_replace >-^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd >-^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace >-^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd >-^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace >diff --git a/source4/dsdb/tests/python/user_account_control.py b/source4/dsdb/tests/python/user_account_control.py >index 2a3dd34c53a..ecdb05b56e9 100755 >--- a/source4/dsdb/tests/python/user_account_control.py >+++ b/source4/dsdb/tests/python/user_account_control.py >@@ -90,32 +90,43 @@ account_types = set([UF_NORMAL_ACCOUNT, UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_ > class UserAccountControlTests(samba.tests.TestCase): > @classmethod > def setUpDynamicTestCases(cls): >+ for priv in [(True, "priv"), (False, "cc")]: >+ for account_type in [UF_NORMAL_ACCOUNT, >+ UF_WORKSTATION_TRUST_ACCOUNT, >+ UF_SERVER_TRUST_ACCOUNT]: >+ account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type) >+ for objectclass in ["computer", "user"]: >+ for name in [("oc_uac_lock$", "withdollar"), \ >+ ("oc_uac_lock", "plain")]: >+ test_name = f"{account_type_str}_{objectclass}_{priv[1]}_{name[1]}" >+ cls.generate_dynamic_test("test_objectclass_uac_dollar_lock", >+ test_name, >+ account_type, >+ objectclass, >+ name[0], >+ priv[0]) >+ >+ for priv in [(True, "priv"), (False, "wp")]: >+ for account_type in [UF_NORMAL_ACCOUNT, >+ UF_WORKSTATION_TRUST_ACCOUNT, >+ UF_SERVER_TRUST_ACCOUNT]: >+ account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type) >+ for account_type2 in [UF_NORMAL_ACCOUNT, >+ UF_WORKSTATION_TRUST_ACCOUNT, >+ UF_SERVER_TRUST_ACCOUNT]: >+ for how in ["replace", "deladd"]: >+ account_type2_str = dsdb.user_account_control_flag_bit_to_string(account_type2) >+ test_name = f"{account_type_str}_{account_type2_str}_{how}_{priv[1]}" >+ cls.generate_dynamic_test("test_objectclass_uac_mod_lock", >+ test_name, >+ account_type, >+ account_type2, >+ how, >+ priv[0]) > for account_type in [UF_NORMAL_ACCOUNT, > UF_WORKSTATION_TRUST_ACCOUNT, > UF_SERVER_TRUST_ACCOUNT]: > account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type) >- for objectclass in ["computer", "user"]: >- test_name = f"{account_type_str}_{objectclass}" >- cls.generate_dynamic_test("test_objectclass_uac_lock", >- test_name, >- account_type, >- objectclass) >- >- for account_type in [UF_NORMAL_ACCOUNT, >- UF_WORKSTATION_TRUST_ACCOUNT, >- UF_SERVER_TRUST_ACCOUNT]: >- account_type_str = dsdb.user_account_control_flag_bit_to_string(account_type) >- for account_type2 in [UF_NORMAL_ACCOUNT, >- UF_WORKSTATION_TRUST_ACCOUNT, >- UF_SERVER_TRUST_ACCOUNT]: >- for how in ["replace", "deladd"]: >- account_type2_str = dsdb.user_account_control_flag_bit_to_string(account_type2) >- test_name = f"{account_type_str}_{account_type2_str}_{how}" >- cls.generate_dynamic_test("test_objectclass_uac_mod_lock", >- test_name, >- account_type, >- account_type2, >- how) > for objectclass in ["user", "computer"]: > for how in ["replace", "deladd"]: > test_name = f"{account_type_str}_{objectclass}_{how}" >@@ -894,10 +905,11 @@ class UserAccountControlTests(samba.tests.TestCase): > "primaryGroupID") > self.admin_samdb.modify(m) > >- def _test_objectclass_uac_lock_with_args(self, >- account_type, >- objectclass): >- name = "oc_uac_lock$" >+ def _test_objectclass_uac_dollar_lock_with_args(self, >+ account_type, >+ objectclass, >+ name, >+ priv): > dn = "CN=%s,%s" % (name, self.OU) > msg_dict = { > "dn": dn, >@@ -909,25 +921,57 @@ class UserAccountControlTests(samba.tests.TestCase): > > print(f"Adding account {name} as {account_type_str} with objectclass {objectclass}") > >- if (objectclass == "user" \ >- and account_type == UF_NORMAL_ACCOUNT): >- self.admin_samdb.add(msg_dict) >- elif objectclass == "computer": >- try: >- self.admin_samdb.add(msg_dict) >- except ldb.LdbError as e: >- (num, msg) = e.args >- self.fail("Failed to create {objectclass} account " >- "with {account_type_string}") >+ if priv: >+ samdb = self.admin_samdb > else: >- self.assertRaisesLdbError(ldb.ERR_OBJECT_CLASS_VIOLATION, >- "Should have been unable to {account_type_str} on {objectclass}", >- self.admin_samdb.add, msg_dict) >+ user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn) >+ mod = "(OA;;CC;;;%s)" % str(user_sid) >+ >+ self.sd_utils.dacl_add_ace(self.OU, mod) >+ samdb = self.samdb >+ >+ enum = ldb.SUCCESS >+ try: >+ samdb.add(msg_dict) >+ except ldb.LdbError as e: >+ (enum, msg) = e.args >+ >+ if (account_type == UF_SERVER_TRUST_ACCOUNT >+ and objectclass != "computer"): >+ self.assertEqual(enum, ldb.ERR_OBJECT_CLASS_VIOLATION) >+ return >+ >+ if priv == False and account_type == UF_SERVER_TRUST_ACCOUNT: >+ self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS) >+ return >+ >+ if (objectclass == "user" >+ and account_type != UF_NORMAL_ACCOUNT): >+ self.assertEqual(enum, ldb.ERR_OBJECT_CLASS_VIOLATION) >+ return >+ >+ if (not priv and objectclass == "computer" >+ and account_type == UF_NORMAL_ACCOUNT): >+ self.assertEqual(enum, ldb.ERR_OBJECT_CLASS_VIOLATION) >+ return >+ >+ if priv and account_type == UF_NORMAL_ACCOUNT: >+ self.assertEqual(enum, 0) >+ return >+ >+ if (priv == False and >+ account_type != UF_NORMAL_ACCOUNT and >+ name[-1] != '$'): >+ self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM) >+ return >+ >+ self.assertEqual(enum, 0) > > def _test_objectclass_uac_mod_lock_with_args(self, > account_type, > account_type2, >- how): >+ how, >+ priv): > name = "uac_mod_lock$" > dn = "CN=%s,%s" % (name, self.OU) > if account_type == UF_NORMAL_ACCOUNT: >@@ -948,10 +992,25 @@ class UserAccountControlTests(samba.tests.TestCase): > > print(f"Adding account {name} as {account_type_str} with objectclass {objectclass}") > >+ if priv: >+ samdb = self.admin_samdb >+ else: >+ samdb = self.samdb >+ >+ user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn) >+ >+ # Create the object as admin > self.admin_samdb.add(msg_dict) > >+ # We want to test what the underlying rules for non-admins >+ # regardless of security descriptors are, so set this very, >+ # dangerously, broadly >+ mod = "(OA;;WP;;;%s)" % str(user_sid) >+ >+ self.sd_utils.dacl_add_ace(dn, mod) >+ > m = ldb.Message() >- m.dn = ldb.Dn(self.admin_samdb, dn) >+ m.dn = ldb.Dn(samdb, dn) > if how == "replace": > m["userAccountControl"] = ldb.MessageElement(str(account_type2 | UF_PASSWD_NOTREQD), > ldb.FLAG_MOD_REPLACE, "userAccountControl") >@@ -963,17 +1022,36 @@ class UserAccountControlTests(samba.tests.TestCase): > else: > raise AssertionError(f"{how} was not a valid argument") > >- if (account_type in [UF_SERVER_TRUST_ACCOUNT, >- UF_WORKSTATION_TRUST_ACCOUNT]) and \ >+ if (account_type == account_type2): >+ samdb.modify(m) >+ elif (account_type == UF_NORMAL_ACCOUNT) and \ >+ (account_type2 == UF_SERVER_TRUST_ACCOUNT) and not priv: >+ self.assertRaisesLdbError(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, >+ f"Should have been unable to change {account_type_str} to {account_type2_str}", >+ samdb.modify, m) >+ elif (account_type == UF_NORMAL_ACCOUNT) and \ >+ (account_type2 == UF_SERVER_TRUST_ACCOUNT) and priv: >+ self.assertRaisesLdbError(ldb.ERR_UNWILLING_TO_PERFORM, >+ f"Should have been unable to change {account_type_str} to {account_type2_str}", >+ samdb.modify, m) >+ elif (account_type == UF_WORKSTATION_TRUST_ACCOUNT) and \ >+ (account_type2 == UF_SERVER_TRUST_ACCOUNT) and not priv: >+ self.assertRaisesLdbError(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, >+ f"Should have been unable to change {account_type_str} to {account_type2_str}", >+ samdb.modify, m) >+ elif priv: >+ samdb.modify(m) >+ elif (account_type in [UF_SERVER_TRUST_ACCOUNT, >+ UF_WORKSTATION_TRUST_ACCOUNT]) and \ > (account_type2 in [UF_SERVER_TRUST_ACCOUNT, > UF_WORKSTATION_TRUST_ACCOUNT]): >- self.admin_samdb.modify(m) >+ samdb.modify(m) > elif (account_type == account_type2): >- self.admin_samdb.modify(m) >+ samdb.modify(m) > else: >- self.assertRaisesLdbError(ldb.ERR_UNWILLING_TO_PERFORM, >+ self.assertRaisesLdbError(ldb.ERR_OBJECT_CLASS_VIOLATION, > f"Should have been unable to change {account_type_str} to {account_type2_str}", >- self.admin_samdb.modify, m) >+ samdb.modify, m) > > def _test_objectclass_mod_lock_with_args(self, > account_type, >-- >2.25.1 > > >From 392a7d189f198f7f815f0e58754a0bc9c776be8f Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Fri, 22 Oct 2021 16:07:46 +1300 >Subject: [PATCH 012/190] CVE-2020-25722 dsdb: Prohibit mismatch between UF_ > account types and objectclass. > >There are a lot of knownfail entries added with this commit. These >all need to be addressed and removed in subsequent commits which >will restructure the tests to pass within this new reality. > >The restriction is not applied to users with administrator rights, >as this breaks a lot of tests and provides no security benifit. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/priv_attr | 6 - > selftest/knownfail.d/uac_objectclass_restrict | 35 ++-- > source4/dsdb/samdb/ldb_modules/samldb.c | 153 ++++++++++++++---- > 3 files changed, 145 insertions(+), 49 deletions(-) > >diff --git a/selftest/knownfail.d/priv_attr b/selftest/knownfail.d/priv_attr >index 4b85a869089..e0d6104cec9 100644 >--- a/selftest/knownfail.d/priv_attr >+++ b/selftest/knownfail.d/priv_attr >@@ -6,9 +6,3 @@ samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sid > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_WP_user > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_default_computer > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_admin-add_default_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-RODC_add_CC_WP_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-RODC_add_CC_default_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-computer_add_CC_WP_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-computer_add_CC_default_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-t4d-computer_add_CC_WP_user >-samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-t4d-computer_add_CC_default_user >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index bb0787c1a48..040c4eb219d 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -10,6 +10,16 @@ > ^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_isCriticalSystemObject\(ad_dc_default\) > ^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_userAccountControl\(ad_dc_default\) > ^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_users_groups\(ad_dc_default\) >+^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-DC_add_CC_WP_user\(ad_dc_default\) >+^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-DC_add_CC_default_user\(ad_dc_default\) >+^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_CC_WP_computer\(ad_dc_default\) >+^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_CC_default_computer\(ad_dc_default\) >+^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_mod-del-add_CC_default_computer\(ad_dc_default\) >+^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_mod-replace_CC_default_computer\(ad_dc_default\) >+^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-t4d-user_add_CC_WP_computer\(ad_dc_default\) >+^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-t4d-user_add_CC_default_computer\(ad_dc_default\) >+^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-t4d-user_mod-del-add_CC_default_computer\(ad_dc_default\) >+^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-t4d-user_mod-replace_CC_default_computer\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_add_computer_sd_cc\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_admin_mod_uac\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_computer_cc\(ad_dc_default\) >@@ -17,18 +27,6 @@ > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_user_replace\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_SERVER_TRUST_ACCOUNT_computer_replace\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_replace\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_NORMAL_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_NORMAL_ACCOUNT_computer_cc_plain\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_NORMAL_ACCOUNT_computer_cc_withdollar\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_SERVER_TRUST_ACCOUNT_user_cc_plain\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_SERVER_TRUST_ACCOUNT_user_cc_withdollar\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_deladd_wp\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_WORKSTATION_TRUST_ACCOUNT_replace_wp\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd_wp\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_SERVER_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace_wp\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_deladd_wp\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_UF_NORMAL_ACCOUNT_replace_wp\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x10000000\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x20000000\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x40000000\(ad_dc_default\) >@@ -54,3 +52,16 @@ > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_SMARTCARD_REQUIRED\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_USE_AES_KEYS\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_USE_DES_KEY_ONLY\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_NORMAL_ACCOUNT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_SERVER_TRUST_ACCOUNT_deladd_priv\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_SERVER_TRUST_ACCOUNT_deladd_wp\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_SERVER_TRUST_ACCOUNT_replace_priv\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_SERVER_TRUST_ACCOUNT_replace_wp\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_INTERDOMAIN_TRUST_ACCOUNT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_NORMAL_ACCOUNT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_NORMAL_ACCOUNT_UF_PASSWD_NOTREQD\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_INTERDOMAIN_TRUST_ACCOUNT\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_TRUSTED_FOR_DELEGATION\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION\(ad_dc_default\) >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index da74462ec57..d7e0a33be96 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -1368,7 +1368,8 @@ static int samldb_check_user_account_control_rules(struct samldb_ctx *ac, > struct dom_sid *sid, > uint32_t req_uac, > uint32_t user_account_control, >- uint32_t user_account_control_old); >+ uint32_t user_account_control_old, >+ bool is_computer_objectclass); > > /* > * "Objectclass" trigger (MS-SAMR 3.1.1.8.1) >@@ -1487,21 +1488,12 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac) > ret = samldb_check_user_account_control_rules(ac, NULL, > raw_uac, > user_account_control, >- 0); >+ 0, >+ is_computer_objectclass); > if (ret != LDB_SUCCESS) { > return ret; > } > >- /* Workstation and (read-only) DC objects do need objectclass "computer" */ >- if ((samdb_find_attribute(ldb, ac->msg, >- "objectclass", "computer") == NULL) && >- (user_account_control & >- (UF_SERVER_TRUST_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT))) { >- ldb_set_errstring(ldb, >- "samldb: Requested account type does need objectclass 'computer'!"); >- return LDB_ERR_OBJECT_CLASS_VIOLATION; >- } >- > /* add "sAMAccountType" attribute */ > ret = dsdb_user_obj_set_account_type(ldb, ac->msg, user_account_control, NULL); > if (ret != LDB_SUCCESS) { >@@ -1997,6 +1989,106 @@ static int samldb_check_user_account_control_invariants(struct samldb_ctx *ac, > return ret; > } > >+/* >+ * It would be best if these rules apply, always, but for now they >+ * apply only to non-admins >+ */ >+static int samldb_check_user_account_control_objectclass_invariants( >+ struct samldb_ctx *ac, >+ uint32_t user_account_control, >+ uint32_t user_account_control_old, >+ bool is_computer_objectclass) >+{ >+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module); >+ >+ uint32_t old_ufa = user_account_control_old & UF_ACCOUNT_TYPE_MASK; >+ uint32_t new_ufa = user_account_control & UF_ACCOUNT_TYPE_MASK; >+ >+ uint32_t old_rodc = user_account_control_old & UF_PARTIAL_SECRETS_ACCOUNT; >+ uint32_t new_rodc = user_account_control & UF_PARTIAL_SECRETS_ACCOUNT; >+ >+ bool is_admin; >+ struct security_token *user_token >+ = acl_user_token(ac->module); >+ if (user_token == NULL) { >+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; >+ } >+ >+ is_admin >+ = security_token_has_builtin_administrators(user_token); >+ >+ >+ /* >+ * We want to allow changes to (eg) disable an account >+ * that was created wrong, only checking the >+ * objectclass if the account type changes. >+ */ >+ if (old_ufa == new_ufa && old_rodc == new_rodc) { >+ return LDB_SUCCESS; >+ } >+ >+ switch (new_ufa) { >+ case UF_NORMAL_ACCOUNT: >+ if (is_computer_objectclass && !is_admin) { >+ ldb_asprintf_errstring(ldb, >+ "%08X: samldb: UF_NORMAL_ACCOUNT " >+ "requires objectclass 'user' not 'computer'!", >+ W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4)); >+ return LDB_ERR_OBJECT_CLASS_VIOLATION; >+ } >+ break; >+ >+ case UF_INTERDOMAIN_TRUST_ACCOUNT: >+ if (is_computer_objectclass) { >+ ldb_asprintf_errstring(ldb, >+ "%08X: samldb: UF_INTERDOMAIN_TRUST_ACCOUNT " >+ "requires objectclass 'user' not 'computer'!", >+ W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4)); >+ return LDB_ERR_OBJECT_CLASS_VIOLATION; >+ } >+ break; >+ >+ case UF_WORKSTATION_TRUST_ACCOUNT: >+ if (!is_computer_objectclass) { >+ /* >+ * Modify of a user account account into a >+ * workstation without objectclass computer >+ * as an admin is still permitted, but not >+ * to make an RODC >+ */ >+ if (is_admin >+ && ac->req->operation == LDB_MODIFY >+ && new_rodc == 0) { >+ break; >+ } >+ ldb_asprintf_errstring(ldb, >+ "%08X: samldb: UF_WORKSTATION_TRUST_ACCOUNT " >+ "requires objectclass 'computer'!", >+ W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4)); >+ return LDB_ERR_OBJECT_CLASS_VIOLATION; >+ } >+ break; >+ >+ case UF_SERVER_TRUST_ACCOUNT: >+ if (!is_computer_objectclass) { >+ ldb_asprintf_errstring(ldb, >+ "%08X: samldb: UF_SERVER_TRUST_ACCOUNT " >+ "requires objectclass 'computer'!", >+ W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4)); >+ return LDB_ERR_OBJECT_CLASS_VIOLATION; >+ } >+ break; >+ >+ default: >+ ldb_asprintf_errstring(ldb, >+ "%08X: samldb: invalid userAccountControl[0x%08X]", >+ W_ERROR_V(WERR_INVALID_PARAMETER), >+ user_account_control); >+ return LDB_ERR_OTHER; >+ } >+ return LDB_SUCCESS; >+} >+ > static int samldb_get_domain_secdesc(struct samldb_ctx *ac, > struct security_descriptor **domain_sd) > { >@@ -2196,7 +2288,8 @@ static int samldb_check_user_account_control_rules(struct samldb_ctx *ac, > struct dom_sid *sid, > uint32_t req_uac, > uint32_t user_account_control, >- uint32_t user_account_control_old) >+ uint32_t user_account_control_old, >+ bool is_computer_objectclass) > { > int ret; > struct dsdb_control_password_user_account_control *uac = NULL; >@@ -2205,6 +2298,14 @@ static int samldb_check_user_account_control_rules(struct samldb_ctx *ac, > if (ret != LDB_SUCCESS) { > return ret; > } >+ ret = samldb_check_user_account_control_objectclass_invariants(ac, >+ user_account_control, >+ user_account_control_old, >+ is_computer_objectclass); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ > ret = samldb_check_user_account_control_acl(ac, sid, user_account_control, user_account_control_old); > if (ret != LDB_SUCCESS) { > return ret; >@@ -2266,7 +2367,7 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) > "objectSid", > NULL > }; >- bool is_computer = false; >+ bool is_computer_objectclass = false; > bool old_is_critical = false; > bool new_is_critical = false; > >@@ -2321,7 +2422,10 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) > "lockoutTime", 0); > old_is_critical = ldb_msg_find_attr_as_bool(res->msgs[0], > "isCriticalSystemObject", 0); >- /* When we do not have objectclass "computer" we cannot switch to a (read-only) DC */ >+ /* >+ * When we do not have objectclass "computer" we cannot >+ * switch to a workstation or (RO)DC >+ */ > el = ldb_msg_find_element(res->msgs[0], "objectClass"); > if (el == NULL) { > return ldb_operr(ldb); >@@ -2329,7 +2433,7 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) > computer_val = data_blob_string_const("computer"); > val = ldb_msg_find_val(el, &computer_val); > if (val != NULL) { >- is_computer = true; >+ is_computer_objectclass = true; > } > > old_ufa = old_uac & UF_ACCOUNT_TYPE_MASK; >@@ -2354,7 +2458,8 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) > ret = samldb_check_user_account_control_rules(ac, sid, > raw_uac, > new_uac, >- old_uac); >+ old_uac, >+ is_computer_objectclass); > if (ret != LDB_SUCCESS) { > return ret; > } >@@ -2376,25 +2481,11 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) > case UF_WORKSTATION_TRUST_ACCOUNT: > new_is_critical = false; > if (new_uac & UF_PARTIAL_SECRETS_ACCOUNT) { >- if (!is_computer) { >- ldb_asprintf_errstring(ldb, >- "%08X: samldb: UF_PARTIAL_SECRETS_ACCOUNT " >- "requires objectclass 'computer'!", >- W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4)); >- return LDB_ERR_UNWILLING_TO_PERFORM; >- } > new_is_critical = true; > } > break; > > case UF_SERVER_TRUST_ACCOUNT: >- if (!is_computer) { >- ldb_asprintf_errstring(ldb, >- "%08X: samldb: UF_SERVER_TRUST_ACCOUNT " >- "requires objectclass 'computer'!", >- W_ERROR_V(WERR_DS_MACHINE_ACCOUNT_CREATED_PRENT4)); >- return LDB_ERR_UNWILLING_TO_PERFORM; >- } > new_is_critical = true; > break; > >-- >2.25.1 > > >From 72ca5491059a4c58f43bea0dc02c631b80009a57 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 28 Oct 2021 14:47:30 +1300 >Subject: [PATCH 013/190] CVE-2020-25722 selftest/priv_attrs: Mention that > these knownfails are OK (for now) > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14775 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/priv_attr | 5 +++++ > 1 file changed, 5 insertions(+) > >diff --git a/selftest/knownfail.d/priv_attr b/selftest/knownfail.d/priv_attr >index e0d6104cec9..5d3713eafe3 100644 >--- a/selftest/knownfail.d/priv_attr >+++ b/selftest/knownfail.d/priv_attr >@@ -1,3 +1,8 @@ >+# These priv_attrs tests would be good to fix, but are not fatal as >+# the testsuite is run twice, once with and once without STRICT_CHECKING=0 >+# >+# These knownfails show that we can improve our error matching against Windows. >+# > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_computer > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_WP_user > samba4.priv_attrs.strict.python\(.*\).__main__.PrivAttrsTests.test_priv_attr_sidHistory_add_CC_default_computer >-- >2.25.1 > > >From 3cf8d636038d58331af2a6b556fa7feca9ef49f1 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Fri, 22 Oct 2021 16:18:51 +1300 >Subject: [PATCH 014/190] CVE-2020-25722 selftest: Adapt selftest to > restriction on swapping account types > >This makes many of our tests pass again. We do not pass against Windows 2019 on all >as this does not have this restriction at this time. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/uac_objectclass_restrict | 29 +--------- > .../dsdb/tests/python/user_account_control.py | 54 +++++++++++++------ > 2 files changed, 39 insertions(+), 44 deletions(-) > >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index 040c4eb219d..32d8a99f950 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -27,31 +27,7 @@ > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_user_replace\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_SERVER_TRUST_ACCOUNT_computer_replace\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_replace\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x10000000\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x20000000\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x40000000\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_0x80000000\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_00000004\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_00000400\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_00004000\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_00008000\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_ACCOUNTDISABLE\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_DONT_EXPIRE_PASSWD\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_DONT_REQUIRE_PREAUTH\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_HOMEDIR_REQUIRED\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_LOCKOUT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_MNS_LOGON_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_NORMAL_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_NOT_DELEGATED\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_NO_AUTH_DATA_REQUIRED\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_PASSWD_CANT_CHANGE\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_PASSWD_NOTREQD\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_PASSWORD_EXPIRED\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_SCRIPT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_SMARTCARD_REQUIRED\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_USE_AES_KEYS\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_USE_DES_KEY_ONLY\(ad_dc_default\) >+^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_SERVER_TRUST_ACCOUNT\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_NORMAL_ACCOUNT\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) >@@ -62,6 +38,3 @@ > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_INTERDOMAIN_TRUST_ACCOUNT\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_NORMAL_ACCOUNT\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_NORMAL_ACCOUNT_UF_PASSWD_NOTREQD\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_INTERDOMAIN_TRUST_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_TRUSTED_FOR_DELEGATION\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION\(ad_dc_default\) >diff --git a/source4/dsdb/tests/python/user_account_control.py b/source4/dsdb/tests/python/user_account_control.py >index ecdb05b56e9..bda005e929b 100755 >--- a/source4/dsdb/tests/python/user_account_control.py >+++ b/source4/dsdb/tests/python/user_account_control.py >@@ -178,6 +178,23 @@ class UserAccountControlTests(samba.tests.TestCase): > print("Adding computer account %s" % computername) > samdb.add(msg) > >+ def add_user_ldap(self, username, others=None, samdb=None): >+ if samdb is None: >+ samdb = self.samdb >+ dn = "CN=%s,%s" % (username, self.OU) >+ samaccountname = "%s" % username >+ msg_dict = { >+ "dn": dn, >+ "objectclass": "user"} >+ if others is not None: >+ msg_dict = dict(list(msg_dict.items()) + list(others.items())) >+ >+ msg = ldb.Message.from_dict(self.samdb, msg_dict) >+ msg["sAMAccountName"] = samaccountname >+ >+ print("Adding user account %s" % username) >+ samdb.add(msg) >+ > def get_creds(self, target_username, target_password): > creds_tmp = Credentials() > creds_tmp.set_username(target_username) >@@ -491,17 +508,21 @@ class UserAccountControlTests(samba.tests.TestCase): > > def _test_uac_bits_set_with_args(self, bit, bit_str): > user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn) >- mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid) >+ # Allow the creation of any children and write to any >+ # attributes (this is not a test of ACLs, this is a test of >+ # non-ACL userAccountControl rules >+ mod = f"(OA;CI;WP;;;{user_sid})(OA;;CC;;;{user_sid})" > > old_sd = self.sd_utils.read_sd_on_dn(self.OU) > > self.sd_utils.dacl_add_ace(self.OU, mod) > >+ # We want to start with UF_NORMAL_ACCOUNT, so we make a user > computername = self.computernames[0] >- self.add_computer_ldap(computername) >+ self.add_user_ldap(computername) > > res = self.admin_samdb.search("%s" % self.base_dn, >- expression="(&(objectClass=computer)(samAccountName=%s$))" % computername, >+ expression="(&(objectClass=user)(cn=%s))" % computername, > scope=SCOPE_SUBTREE, > attrs=[]) > >@@ -547,7 +568,11 @@ class UserAccountControlTests(samba.tests.TestCase): > > def _test_uac_bits_unrelated_modify_with_args(self, account_type): > user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn) >- mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(user_sid) >+ >+ # Allow the creation of any children and write to any >+ # attributes (this is not a test of ACLs, this is a test of >+ # non-ACL userAccountControl rules >+ mod = f"(OA;CI;WP;;;{user_sid})(OA;;CC;;;{user_sid})" > > old_sd = self.sd_utils.read_sd_on_dn(self.OU) > >@@ -555,22 +580,19 @@ class UserAccountControlTests(samba.tests.TestCase): > > computername = self.computernames[0] > if account_type == UF_WORKSTATION_TRUST_ACCOUNT: >- self.add_computer_ldap(computername, others={"userAccountControl": [str(account_type)]}) >- else: > self.add_computer_ldap(computername) >+ else: >+ self.add_user_ldap(computername) > > res = self.admin_samdb.search(self.OU, >- expression=f"(cn={computername})", >+ expression=f"(&(objectclass=user)(cn={computername}))", > scope=SCOPE_SUBTREE, > attrs=["userAccountControl"]) > self.assertEqual(len(res), 1) > > orig_uac = int(res[0]["userAccountControl"][0]) >- if account_type == UF_WORKSTATION_TRUST_ACCOUNT: >- self.assertEqual(orig_uac, account_type) >- else: >- self.assertEqual(orig_uac & UF_NORMAL_ACCOUNT, >- account_type) >+ self.assertEqual(orig_uac & account_type, >+ account_type) > > m = ldb.Message() > m.dn = res[0].dn >@@ -608,7 +630,7 @@ class UserAccountControlTests(samba.tests.TestCase): > self.fail(f"got {estr} resetting userAccountControl to initial value {orig_uac:#08x}") > > res = self.admin_samdb.search("%s" % self.base_dn, >- expression="(&(objectClass=computer)(samAccountName=%s$))" % computername, >+ expression="(&(objectClass=user)(cn=%s))" % computername, > scope=SCOPE_SUBTREE, > attrs=["userAccountControl"]) > >@@ -655,7 +677,7 @@ class UserAccountControlTests(samba.tests.TestCase): > self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr)) > > res = self.admin_samdb.search("%s" % self.base_dn, >- expression="(&(objectClass=computer)(samAccountName=%s$))" % computername, >+ expression="(&(objectClass=user)(cn=%s))" % computername, > scope=SCOPE_SUBTREE, > attrs=["userAccountControl"]) > >@@ -685,7 +707,7 @@ class UserAccountControlTests(samba.tests.TestCase): > self.fail("Unable to set userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr)) > > res = self.admin_samdb.search("%s" % self.base_dn, >- expression="(&(objectClass=computer)(samAccountName=%s$))" % computername, >+ expression="(&(objectClass=user)(cn=%s))" % computername, > scope=SCOPE_SUBTREE, > attrs=["userAccountControl"]) > >@@ -726,7 +748,7 @@ class UserAccountControlTests(samba.tests.TestCase): > self.fail("Unexpectedly unable to remove userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr)) > > res = self.admin_samdb.search("%s" % self.base_dn, >- expression="(&(objectClass=computer)(samAccountName=%s$))" % computername, >+ expression="(&(objectClass=user)(cn=%s))" % computername, > scope=SCOPE_SUBTREE, > attrs=["userAccountControl"]) > >-- >2.25.1 > > >From 72280d4ea850825c27660265278369db2f774bdd Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Wed, 22 Sep 2021 11:28:05 +1200 >Subject: [PATCH 015/190] CVE-2020-25722 dsdb: samldb_objectclass_trigger() is > only called on ADD, so remove indentation > >This makes the code less indented and simpler to understand. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 187 ++++++++++++------------ > 1 file changed, 93 insertions(+), 94 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index d7e0a33be96..3eeab82126f 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -1374,9 +1374,9 @@ static int samldb_check_user_account_control_rules(struct samldb_ctx *ac, > /* > * "Objectclass" trigger (MS-SAMR 3.1.1.8.1) > * >- * Has to be invoked on "add" and "modify" operations on "user", "computer" and >+ * Has to be invoked on "add" operations on "user", "computer" and > * "group" objects. >- * ac->msg contains the "add"/"modify" message >+ * ac->msg contains the "add" > * ac->type contains the object type (main objectclass) > */ > static int samldb_objectclass_trigger(struct samldb_ctx *ac) >@@ -1417,6 +1417,8 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac) > > switch(ac->type) { > case SAMLDB_TYPE_USER: { >+ uint32_t raw_uac; >+ uint32_t user_account_control; > bool is_computer_objectclass; > bool uac_generated = false, uac_add_flags = false; > uint32_t default_user_account_control = UF_NORMAL_ACCOUNT; >@@ -1440,7 +1442,7 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac) > /* On add operations we might need to generate a > * "userAccountControl" (if it isn't specified). */ > el = ldb_msg_find_element(ac->msg, "userAccountControl"); >- if ((el == NULL) && (ac->req->operation == LDB_ADD)) { >+ if (el == NULL) { > ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg, > "userAccountControl", > default_user_account_control); >@@ -1452,114 +1454,111 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac) > } > > el = ldb_msg_find_element(ac->msg, "userAccountControl"); >- if (el != NULL) { >- uint32_t raw_uac; >- uint32_t user_account_control; >- /* Step 1.3: "userAccountControl" -> "sAMAccountType" mapping */ >- user_account_control = ldb_msg_find_attr_as_uint(ac->msg, >- "userAccountControl", >- 0); >- raw_uac = user_account_control; >- /* >- * "userAccountControl" = 0 or missing one of >- * the types means "UF_NORMAL_ACCOUNT" >- * or "UF_WORKSTATION_TRUST_ACCOUNT" (if a computer). >- * See MS-SAMR 3.1.1.8.10 point 8 >- */ >- if ((user_account_control & UF_ACCOUNT_TYPE_MASK) == 0) { >- user_account_control >- = default_user_account_control >- | user_account_control; >- uac_generated = true; >- } >+ SMB_ASSERT(el != NULL); > >- /* >- * As per MS-SAMR 3.1.1.8.10 these flags have not to be set >- */ >- if ((user_account_control & UF_LOCKOUT) != 0) { >- user_account_control &= ~UF_LOCKOUT; >- uac_generated = true; >- } >- if ((user_account_control & UF_PASSWORD_EXPIRED) != 0) { >- user_account_control &= ~UF_PASSWORD_EXPIRED; >- uac_generated = true; >- } >+ /* Step 1.3: "userAccountControl" -> "sAMAccountType" mapping */ >+ user_account_control = ldb_msg_find_attr_as_uint(ac->msg, >+ "userAccountControl", >+ 0); >+ raw_uac = user_account_control; >+ /* >+ * "userAccountControl" = 0 or missing one of >+ * the types means "UF_NORMAL_ACCOUNT" >+ * or "UF_WORKSTATION_TRUST_ACCOUNT" (if a computer). >+ * See MS-SAMR 3.1.1.8.10 point 8 >+ */ >+ if ((user_account_control & UF_ACCOUNT_TYPE_MASK) == 0) { >+ user_account_control >+ = default_user_account_control >+ | user_account_control; >+ uac_generated = true; >+ } >+ >+ /* >+ * As per MS-SAMR 3.1.1.8.10 these flags have not to be set >+ */ >+ if ((user_account_control & UF_LOCKOUT) != 0) { >+ user_account_control &= ~UF_LOCKOUT; >+ uac_generated = true; >+ } >+ if ((user_account_control & UF_PASSWORD_EXPIRED) != 0) { >+ user_account_control &= ~UF_PASSWORD_EXPIRED; >+ uac_generated = true; >+ } > >- ret = samldb_check_user_account_control_rules(ac, NULL, >- raw_uac, >- user_account_control, >- 0, >- is_computer_objectclass); >+ ret = samldb_check_user_account_control_rules(ac, NULL, >+ raw_uac, >+ user_account_control, >+ 0, >+ is_computer_objectclass); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ >+ /* add "sAMAccountType" attribute */ >+ ret = dsdb_user_obj_set_account_type(ldb, ac->msg, user_account_control, NULL); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ >+ /* "isCriticalSystemObject" might be set */ >+ if (user_account_control & >+ (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) { >+ ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject", >+ "TRUE"); > if (ret != LDB_SUCCESS) { > return ret; > } >- >- /* add "sAMAccountType" attribute */ >- ret = dsdb_user_obj_set_account_type(ldb, ac->msg, user_account_control, NULL); >+ el2 = ldb_msg_find_element(ac->msg, >+ "isCriticalSystemObject"); >+ el2->flags = LDB_FLAG_MOD_REPLACE; >+ } else if (user_account_control & UF_WORKSTATION_TRUST_ACCOUNT) { >+ ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject", >+ "FALSE"); > if (ret != LDB_SUCCESS) { > return ret; > } >+ el2 = ldb_msg_find_element(ac->msg, >+ "isCriticalSystemObject"); >+ el2->flags = LDB_FLAG_MOD_REPLACE; >+ } > >- /* "isCriticalSystemObject" might be set */ >- if (user_account_control & >- (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) { >- ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject", >- "TRUE"); >- if (ret != LDB_SUCCESS) { >- return ret; >- } >- el2 = ldb_msg_find_element(ac->msg, >- "isCriticalSystemObject"); >- el2->flags = LDB_FLAG_MOD_REPLACE; >- } else if (user_account_control & UF_WORKSTATION_TRUST_ACCOUNT) { >- ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject", >- "FALSE"); >- if (ret != LDB_SUCCESS) { >- return ret; >- } >- el2 = ldb_msg_find_element(ac->msg, >- "isCriticalSystemObject"); >- el2->flags = LDB_FLAG_MOD_REPLACE; >- } >- >- /* Step 1.4: "userAccountControl" -> "primaryGroupID" mapping */ >- if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) { >- uint32_t rid; >+ /* Step 1.4: "userAccountControl" -> "primaryGroupID" mapping */ >+ if (!ldb_msg_find_element(ac->msg, "primaryGroupID")) { >+ uint32_t rid; > >- ret = dsdb_user_obj_set_primary_group_id(ldb, ac->msg, user_account_control, &rid); >+ ret = dsdb_user_obj_set_primary_group_id(ldb, ac->msg, user_account_control, &rid); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ /* >+ * Older AD deployments don't know about the >+ * RODC group >+ */ >+ if (rid == DOMAIN_RID_READONLY_DCS) { >+ ret = samldb_prim_group_tester(ac, rid); > if (ret != LDB_SUCCESS) { > return ret; > } >- /* >- * Older AD deployments don't know about the >- * RODC group >- */ >- if (rid == DOMAIN_RID_READONLY_DCS) { >- ret = samldb_prim_group_tester(ac, rid); >- if (ret != LDB_SUCCESS) { >- return ret; >- } >- } > } >+ } > >- /* Step 1.5: Add additional flags when needed */ >- /* Obviously this is done when the "userAccountControl" >- * has been generated here (tested against Windows >- * Server) */ >- if (uac_generated) { >- if (uac_add_flags) { >- user_account_control |= UF_ACCOUNTDISABLE; >- user_account_control |= UF_PASSWD_NOTREQD; >- } >- >- ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg, >- "userAccountControl", >- user_account_control); >- if (ret != LDB_SUCCESS) { >- return ret; >- } >+ /* Step 1.5: Add additional flags when needed */ >+ /* Obviously this is done when the "userAccountControl" >+ * has been generated here (tested against Windows >+ * Server) */ >+ if (uac_generated) { >+ if (uac_add_flags) { >+ user_account_control |= UF_ACCOUNTDISABLE; >+ user_account_control |= UF_PASSWD_NOTREQD; > } > >+ ret = samdb_msg_set_uint(ldb, ac->msg, ac->msg, >+ "userAccountControl", >+ user_account_control); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } > } > break; > } >-- >2.25.1 > > >From e48919468a6cafb199c73bc978af52d5bc96c2e2 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Wed, 22 Sep 2021 11:29:02 +1200 >Subject: [PATCH 016/190] CVE-2020-25722 dsdb: Add restrictions on computer > accounts without a trailing $ > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/uac_dollar_lock | 1 - > source4/dsdb/samdb/ldb_modules/samldb.c | 171 +++++++++++++++++++++--- > 2 files changed, 154 insertions(+), 18 deletions(-) > delete mode 100644 selftest/knownfail.d/uac_dollar_lock > >diff --git a/selftest/knownfail.d/uac_dollar_lock b/selftest/knownfail.d/uac_dollar_lock >deleted file mode 100644 >index 8c70c859fa4..00000000000 >--- a/selftest/knownfail.d/uac_dollar_lock >+++ /dev/null >@@ -1 +0,0 @@ >-^samba4.user_account_control.python\(.*\).__main__.UserAccountControlTests.test_objectclass_uac_dollar_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_cc_plain >\ No newline at end of file >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 3eeab82126f..e947f788810 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -71,6 +71,13 @@ struct samldb_ctx { > /* used for add operations */ > enum samldb_add_type type; > >+ /* >+ * should we apply the need_trailing_dollar restriction to >+ * samAccountName >+ */ >+ >+ bool need_trailing_dollar; >+ > /* the resulting message */ > struct ldb_message *msg; > >@@ -235,12 +242,86 @@ static int samldb_unique_attr_check(struct samldb_ctx *ac, const char *attr, > > static int samldb_sam_accountname_valid_check(struct samldb_ctx *ac) > { >- int ret = samldb_unique_attr_check(ac, "samAccountName", NULL, >- ldb_get_default_basedn( >- ldb_module_get_ctx(ac->module))); >- if (ret == LDB_ERR_OBJECT_CLASS_VIOLATION) { >+ int ret = 0; >+ bool is_admin; >+ struct security_token *user_token = NULL; >+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module); >+ struct ldb_message_element *el = dsdb_get_single_valued_attr(ac->msg, "samAccountName", >+ ac->req->operation); >+ if (el == NULL || el->num_values == 0) { >+ ldb_asprintf_errstring(ldb, >+ "%08X: samldb: 'samAccountName' can't be deleted/empty!", >+ W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION)); >+ if (ac->req->operation == LDB_ADD) { >+ return LDB_ERR_CONSTRAINT_VIOLATION; >+ } else { >+ return LDB_ERR_UNWILLING_TO_PERFORM; >+ } >+ } >+ >+ ret = samldb_unique_attr_check(ac, "samAccountName", NULL, >+ ldb_get_default_basedn( >+ ldb_module_get_ctx(ac->module))); >+ >+ /* >+ * Error code munging to try and match what must be some quite >+ * strange code-paths in Windows >+ */ >+ if (ret == LDB_ERR_CONSTRAINT_VIOLATION >+ && ac->req->operation == LDB_MODIFY) { >+ ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; >+ } else if (ret == LDB_ERR_OBJECT_CLASS_VIOLATION) { > ret = LDB_ERR_CONSTRAINT_VIOLATION; > } >+ >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ >+ if (!ac->need_trailing_dollar) { >+ return LDB_SUCCESS; >+ } >+ >+ /* This does not permit a single $ */ >+ if (el->values[0].length < 2) { >+ ldb_asprintf_errstring(ldb, >+ "%08X: samldb: 'samAccountName' " >+ "can't just be one character!", >+ W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION)); >+ return LDB_ERR_UNWILLING_TO_PERFORM; >+ } >+ >+ user_token = acl_user_token(ac->module); >+ if (user_token == NULL) { >+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; >+ } >+ >+ is_admin >+ = security_token_has_builtin_administrators(user_token); >+ >+ if (is_admin) { >+ /* >+ * Administrators are allowed to select strange names. >+ * This is poor practice but not prevented. >+ */ >+ return false; >+ } >+ >+ if (el->values[0].data[el->values[0].length - 1] != '$') { >+ ldb_asprintf_errstring(ldb, >+ "%08X: samldb: 'samAccountName' " >+ "must have a trailing $!", >+ W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION)); >+ return LDB_ERR_UNWILLING_TO_PERFORM; >+ } >+ if (el->values[0].data[el->values[0].length - 2] == '$') { >+ ldb_asprintf_errstring(ldb, >+ "%08X: samldb: 'samAccountName' " >+ "must not have a double trailing $!", >+ W_ERROR_V(WERR_DS_ILLEGAL_MOD_OPERATION)); >+ return LDB_ERR_UNWILLING_TO_PERFORM; >+ } >+ > return ret; > } > >@@ -557,17 +638,31 @@ static int samldb_schema_add_handle_mapiid(struct samldb_ctx *ac) > } > > /* sAMAccountName handling */ >-static int samldb_generate_sAMAccountName(struct ldb_context *ldb, >+static int samldb_generate_sAMAccountName(struct samldb_ctx *ac, > struct ldb_message *msg) > { >+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module); > char *name; > >- /* Format: $000000-000000000000 */ >+ /* >+ * This is currently a Samba-only behaviour, to add a trailing >+ * $ even for the generated accounts. >+ */ >+ >+ if (ac->need_trailing_dollar) { >+ /* Format: $000000-00000000000$ */ >+ name = talloc_asprintf(msg, "$%.6X-%.6X%.5X$", >+ (unsigned int)generate_random(), >+ (unsigned int)generate_random(), >+ (unsigned int)generate_random()); >+ } else { >+ /* Format: $000000-000000000000 */ > >- name = talloc_asprintf(msg, "$%.6X-%.6X%.6X", >- (unsigned int)generate_random(), >- (unsigned int)generate_random(), >- (unsigned int)generate_random()); >+ name = talloc_asprintf(msg, "$%.6X-%.6X%.6X", >+ (unsigned int)generate_random(), >+ (unsigned int)generate_random(), >+ (unsigned int)generate_random()); >+ } > if (name == NULL) { > return ldb_oom(ldb); > } >@@ -576,11 +671,10 @@ static int samldb_generate_sAMAccountName(struct ldb_context *ldb, > > static int samldb_check_sAMAccountName(struct samldb_ctx *ac) > { >- struct ldb_context *ldb = ldb_module_get_ctx(ac->module); > int ret; > > if (ldb_msg_find_element(ac->msg, "sAMAccountName") == NULL) { >- ret = samldb_generate_sAMAccountName(ldb, ac->msg); >+ ret = samldb_generate_sAMAccountName(ac, ac->msg); > if (ret != LDB_SUCCESS) { > return ret; > } >@@ -1495,6 +1589,20 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac) > return ret; > } > >+ /* >+ * Require, for non-admin modifications, a trailing $ >+ * for either objectclass=computer or a trust account >+ * type in userAccountControl >+ */ >+ if ((user_account_control >+ & UF_TRUST_ACCOUNT_MASK) != 0) { >+ ac->need_trailing_dollar = true; >+ } >+ >+ if (is_computer_objectclass) { >+ ac->need_trailing_dollar = true; >+ } >+ > /* add "sAMAccountType" attribute */ > ret = dsdb_user_obj_set_account_type(ldb, ac->msg, user_account_control, NULL); > if (ret != LDB_SUCCESS) { >@@ -4010,12 +4118,41 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req) > > el = ldb_msg_find_element(ac->msg, "sAMAccountName"); > if (el != NULL) { >+ uint32_t user_account_control; >+ struct ldb_result *res = NULL; >+ const char * const attrs[] = { "userAccountControl", >+ "objectclass", >+ NULL }; >+ ret = dsdb_module_search_dn(ac->module, >+ ac, >+ &res, >+ ac->msg->dn, >+ attrs, >+ DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, >+ ac->req); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ >+ user_account_control >+ = ldb_msg_find_attr_as_uint(res->msgs[0], >+ "userAccountControl", >+ 0); >+ >+ if ((user_account_control >+ & UF_TRUST_ACCOUNT_MASK) != 0) { >+ ac->need_trailing_dollar = true; >+ >+ } else if (samdb_find_attribute(ldb, >+ res->msgs[0], >+ "objectclass", >+ "computer") >+ != NULL) { >+ ac->need_trailing_dollar = true; >+ } >+ > ret = samldb_sam_accountname_valid_check(ac); >- /* >- * Other errors are checked for elsewhere, we just >- * want to prevent duplicates >- */ >- if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) { >+ if (ret != LDB_SUCCESS) { > return ret; > } > } >-- >2.25.1 > > >From 61be58614ca603064f0ca3129e84452a0325152c Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 21 Oct 2021 11:57:22 +1300 >Subject: [PATCH 017/190] CVE-2020-25722 selftest: Adapt sam.py > test_isCriticalSystemObject to new UF_WORKSTATION_TRUST_ACCOUNT default > >Objects with objectclass computer now have UF_WORKSTATION_TRUST_ACCOUNT >by default and so this test must adapt. > >The changes to this test passes against Windows 2019 except for >the new behaviour around the UF_WORKSTATION_TRUST_ACCOUNT default. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > .../knownfail.d/sam-isCriticalSystemObject | 1 + > selftest/knownfail.d/uac_objectclass_restrict | 2 -- > source4/dsdb/tests/python/sam.py | 36 ++++++++++++++++++- > 3 files changed, 36 insertions(+), 3 deletions(-) > create mode 100644 selftest/knownfail.d/sam-isCriticalSystemObject > >diff --git a/selftest/knownfail.d/sam-isCriticalSystemObject b/selftest/knownfail.d/sam-isCriticalSystemObject >new file mode 100644 >index 00000000000..a6351a81907 >--- /dev/null >+++ b/selftest/knownfail.d/sam-isCriticalSystemObject >@@ -0,0 +1 @@ >+^samba4.sam.python\(.*\).__main__.SamTests.test_isCriticalSystemObject_user >\ No newline at end of file >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index 32d8a99f950..d093c631bd3 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -3,11 +3,9 @@ > # > # All these tests need to be fixed and the entries here removed > >-^samba4.sam.python\(fl2008r2dc\).__main__.SamTests.test_isCriticalSystemObject\(fl2008r2dc\) > ^samba4.sam.python\(fl2008r2dc\).__main__.SamTests.test_userAccountControl\(fl2008r2dc\) > ^samba4.sam.python\(fl2008r2dc\).__main__.SamTests.test_users_groups\(fl2008r2dc\) > ^samba4.ldap.python\(ad_dc_default\).__main__.BasicTests.test_all\(ad_dc_default\) >-^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_isCriticalSystemObject\(ad_dc_default\) > ^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_userAccountControl\(ad_dc_default\) > ^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_users_groups\(ad_dc_default\) > ^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-DC_add_CC_WP_user\(ad_dc_default\) >diff --git a/source4/dsdb/tests/python/sam.py b/source4/dsdb/tests/python/sam.py >index 1417505c4f5..5dd091fe475 100755 >--- a/source4/dsdb/tests/python/sam.py >+++ b/source4/dsdb/tests/python/sam.py >@@ -2925,6 +2925,39 @@ class SamTests(samba.tests.TestCase): > > delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > >+ def test_isCriticalSystemObject_user(self): >+ """Test the isCriticalSystemObject behaviour""" >+ print("Testing isCriticalSystemObject behaviour\n") >+ >+ # Add tests (of a user) >+ >+ ldb.add({ >+ "dn": "cn=ldaptestuser,cn=users," + self.base_dn, >+ "objectclass": "user"}) >+ >+ res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn, >+ scope=SCOPE_BASE, >+ attrs=["isCriticalSystemObject"]) >+ self.assertTrue(len(res1) == 1) >+ self.assertTrue("isCriticalSystemObject" not in res1[0]) >+ >+ # Modification tests >+ m = Message() >+ >+ m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) >+ m["userAccountControl"] = MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT), >+ FLAG_MOD_REPLACE, "userAccountControl") >+ ldb.modify(m) >+ >+ res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn, >+ scope=SCOPE_BASE, >+ attrs=["isCriticalSystemObject"]) >+ self.assertTrue(len(res1) == 1) >+ self.assertTrue("isCriticalSystemObject" in res1[0]) >+ self.assertEqual(str(res1[0]["isCriticalSystemObject"][0]), "FALSE") >+ >+ delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) >+ > def test_isCriticalSystemObject(self): > """Test the isCriticalSystemObject behaviour""" > print("Testing isCriticalSystemObject behaviour\n") >@@ -2939,7 +2972,8 @@ class SamTests(samba.tests.TestCase): > scope=SCOPE_BASE, > attrs=["isCriticalSystemObject"]) > self.assertTrue(len(res1) == 1) >- self.assertTrue("isCriticalSystemObject" not in res1[0]) >+ self.assertTrue("isCriticalSystemObject" in res1[0]) >+ self.assertEqual(str(res1[0]["isCriticalSystemObject"][0]), "FALSE") > > delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) > >-- >2.25.1 > > >From 51f17b1cb17905ffe14bb71d68a26f60c97924b2 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 21 Oct 2021 13:02:42 +1300 >Subject: [PATCH 018/190] CVE-2020-25722 samdb: Fill in isCriticalSystemObject > on any account type change > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/sam-isCriticalSystemObject | 1 - > source4/dsdb/samdb/ldb_modules/samldb.c | 10 ++++++++-- > 2 files changed, 8 insertions(+), 3 deletions(-) > delete mode 100644 selftest/knownfail.d/sam-isCriticalSystemObject > >diff --git a/selftest/knownfail.d/sam-isCriticalSystemObject b/selftest/knownfail.d/sam-isCriticalSystemObject >deleted file mode 100644 >index a6351a81907..00000000000 >--- a/selftest/knownfail.d/sam-isCriticalSystemObject >+++ /dev/null >@@ -1 +0,0 @@ >-^samba4.sam.python\(.*\).__main__.SamTests.test_isCriticalSystemObject_user >\ No newline at end of file >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index e947f788810..140cc22cc53 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -2626,8 +2626,14 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) > el->flags = LDB_FLAG_MOD_REPLACE; > } > >- /* "isCriticalSystemObject" might be set/changed */ >- if (old_is_critical != new_is_critical) { >+ /* >+ * "isCriticalSystemObject" might be set/changed >+ * >+ * Even a change from UF_NORMAL_ACCOUNT (implicitly FALSE) to >+ * UF_WORKSTATION_TRUST_ACCOUNT (actually FALSE) triggers >+ * creating the attribute. >+ */ >+ if (old_is_critical != new_is_critical || old_atype != new_atype) { > ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject", > new_is_critical ? "TRUE": "FALSE"); > if (ret != LDB_SUCCESS) { >-- >2.25.1 > > >From a3e2745e73003b9d4e0b5073c9ee2741d8c0c99e Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 21 Oct 2021 14:03:05 +1300 >Subject: [PATCH 019/190] CVE-2020-25722 selftest: Split > test_userAccountControl into unit tests > >The parts that create and delete a single object can be >safely split out into an individual test. > >At this point the parts that fail against Windows 2019 are: > >error: __main__.SamTests.test_userAccountControl_computer_add_normal [ >_ldb.LdbError: (53, 'LDAP error 53 LDAP_UNWILLING_TO_PERFORM - <0000052D: SvcErr: DSID-031A1236, problem 5003 (WILL_NOT_PERFORM), data 0\n> <>') >error: __main__.SamTests.test_userAccountControl_computer_modify [ >_ldb.LdbError: (53, 'LDAP error 53 LDAP_UNWILLING_TO_PERFORM - <0000052D: SvcErr: DSID-031A1236, problem 5003 (WILL_NOT_PERFORM), data 0\n> <>') >error: __main__.SamTests.test_userAccountControl_user_add_0_uac [ >_ldb.LdbError: (53, 'LDAP error 53 LDAP_UNWILLING_TO_PERFORM - <0000052D: SvcErr: DSID-031A1236, problem 5003 (WILL_NOT_PERFORM), data 0\n> <>') >error: __main__.SamTests.test_userAccountControl_user_add_normal [ >_ldb.LdbError: (53, 'LDAP error 53 LDAP_UNWILLING_TO_PERFORM - <0000052D: SvcErr: DSID-031A1236, problem 5003 (WILL_NOT_PERFORM), data 0\n> <>') >error: __main__.SamTests.test_userAccountControl_user_modify [ >_ldb.LdbError: (53, 'LDAP error 53 LDAP_UNWILLING_TO_PERFORM - <0000052D: SvcErr: DSID-031A1236, problem 5003 (WILL_NOT_PERFORM), data 0\n> <>') > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/uac_objectclass_restrict | 6 ++++-- > source4/dsdb/tests/python/sam.py | 21 ++++++++++++++++++- > 2 files changed, 24 insertions(+), 3 deletions(-) > >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index d093c631bd3..510dbbf96fa 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -3,10 +3,12 @@ > # > # All these tests need to be fixed and the entries here removed > >-^samba4.sam.python\(fl2008r2dc\).__main__.SamTests.test_userAccountControl\(fl2008r2dc\) >+^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_computer_add_trust >+^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_computer_modify >+^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_user_add_0_uac >+^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_user_modify > ^samba4.sam.python\(fl2008r2dc\).__main__.SamTests.test_users_groups\(fl2008r2dc\) > ^samba4.ldap.python\(ad_dc_default\).__main__.BasicTests.test_all\(ad_dc_default\) >-^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_userAccountControl\(ad_dc_default\) > ^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_users_groups\(ad_dc_default\) > ^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-DC_add_CC_WP_user\(ad_dc_default\) > ^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-DC_add_CC_default_user\(ad_dc_default\) >diff --git a/source4/dsdb/tests/python/sam.py b/source4/dsdb/tests/python/sam.py >index 5dd091fe475..425e51cf72c 100755 >--- a/source4/dsdb/tests/python/sam.py >+++ b/source4/dsdb/tests/python/sam.py >@@ -1884,7 +1884,7 @@ class SamTests(samba.tests.TestCase): > > delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > >- def test_userAccountControl(self): >+ def test_userAccountControl_user_add_0_uac(self): > """Test the userAccountControl behaviour""" > print("Testing userAccountControl behaviour\n") > >@@ -1912,12 +1912,15 @@ class SamTests(samba.tests.TestCase): > self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_PASSWD_NOTREQD == 0) > delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > >+ def test_userAccountControl_user_add_normal(self): >+ """Test the userAccountControl behaviour""" > ldb.add({ > "dn": "cn=ldaptestuser,cn=users," + self.base_dn, > "objectclass": "user", > "userAccountControl": str(UF_NORMAL_ACCOUNT)}) > delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > >+ def test_userAccountControl_user_add_normal_pwnotreq(self): > ldb.add({ > "dn": "cn=ldaptestuser,cn=users," + self.base_dn, > "objectclass": "user", >@@ -1932,6 +1935,7 @@ class SamTests(samba.tests.TestCase): > self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE == 0) > delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > >+ def test_userAccountControl_user_add_normal_pwnotreq_lockout_expired(self): > ldb.add({ > "dn": "cn=ldaptestuser,cn=users," + self.base_dn, > "objectclass": "user", >@@ -1951,6 +1955,7 @@ class SamTests(samba.tests.TestCase): > self.assertTrue(int(res1[0]["pwdLastSet"][0]) == 0) > delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > >+ def test_userAccountControl_user_add_temp_dup(self): > try: > ldb.add({ > "dn": "cn=ldaptestuser,cn=users," + self.base_dn, >@@ -1962,6 +1967,7 @@ class SamTests(samba.tests.TestCase): > self.assertEqual(num, ERR_OTHER) > delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > >+ def test_userAccountControl_user_add_server(self): > try: > ldb.add({ > "dn": "cn=ldaptestuser,cn=users," + self.base_dn, >@@ -1973,6 +1979,7 @@ class SamTests(samba.tests.TestCase): > self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > >+ def test_userAccountControl_user_add_workstation(self): > try: > ldb.add({ > "dn": "cn=ldaptestuser,cn=users," + self.base_dn, >@@ -1983,6 +1990,7 @@ class SamTests(samba.tests.TestCase): > self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > >+ def test_userAccountControl_user_add_rodc(self): > try: > ldb.add({ > "dn": "cn=ldaptestuser,cn=users," + self.base_dn, >@@ -1993,6 +2001,7 @@ class SamTests(samba.tests.TestCase): > self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > >+ def test_userAccountControl_user_add_trust(self): > try: > ldb.add({ > "dn": "cn=ldaptestuser,cn=users," + self.base_dn, >@@ -2006,6 +2015,7 @@ class SamTests(samba.tests.TestCase): > > # Modify operation > >+ def test_userAccountControl_user_modify(self): > ldb.add({ > "dn": "cn=ldaptestuser,cn=users," + self.base_dn, > "objectclass": "user"}) >@@ -2176,6 +2186,7 @@ class SamTests(samba.tests.TestCase): > (num, _) = e69.args > self.assertEqual(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) > >+ def test_userAccountControl_user_add_0_uac(self): > # With a computer object > > # Add operation >@@ -2200,12 +2211,14 @@ class SamTests(samba.tests.TestCase): > self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_PASSWD_NOTREQD == 0) > delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) > >+ def test_userAccountControl_computer_add_normal(self): > ldb.add({ > "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn, > "objectclass": "computer", > "userAccountControl": str(UF_NORMAL_ACCOUNT)}) > delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) > >+ def test_userAccountControl_computer_add_normal_pwnotreqd(self): > ldb.add({ > "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn, > "objectclass": "computer", >@@ -2220,6 +2233,7 @@ class SamTests(samba.tests.TestCase): > self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE == 0) > delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) > >+ def test_userAccountControl_computer_add_normal_pwnotreqd_lockout_expired(self): > ldb.add({ > "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn, > "objectclass": "computer", >@@ -2239,6 +2253,7 @@ class SamTests(samba.tests.TestCase): > self.assertTrue(int(res1[0]["pwdLastSet"][0]) == 0) > delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) > >+ def test_userAccountControl_computer_add_temp_dup(self): > try: > ldb.add({ > "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn, >@@ -2250,6 +2265,7 @@ class SamTests(samba.tests.TestCase): > self.assertEqual(num, ERR_OTHER) > delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) > >+ def test_userAccountControl_computer_add_server(self): > ldb.add({ > "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn, > "objectclass": "computer", >@@ -2262,6 +2278,7 @@ class SamTests(samba.tests.TestCase): > ATYPE_WORKSTATION_TRUST) > delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) > >+ def test_userAccountControl_computer_add_workstation(self): > try: > ldb.add({ > "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn, >@@ -2272,6 +2289,7 @@ class SamTests(samba.tests.TestCase): > self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) > >+ def test_userAccountControl_computer_add_trust(self): > try: > ldb.add({ > "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn, >@@ -2283,6 +2301,7 @@ class SamTests(samba.tests.TestCase): > self.assertEqual(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) > delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) > >+ def test_userAccountControl_computer_modify(self): > # Modify operation > > ldb.add({ >-- >2.25.1 > > >From 04d4bcf2e632df74849638d07b156f089c701287 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 21 Oct 2021 15:06:14 +1300 >Subject: [PATCH 020/190] CVE-2020-25722 selftest: Adjust sam.py > test_userAccountControl_computer_add_trust to new reality > >We now enforce that a trust account must be a user. > >These can not be added over LDAP anyway, and our C >code in the RPC server gets this right in any case. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/uac_objectclass_restrict | 1 - > source4/dsdb/tests/python/sam.py | 2 +- > 2 files changed, 1 insertion(+), 2 deletions(-) > >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index 510dbbf96fa..f697bc2b6e8 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -3,7 +3,6 @@ > # > # All these tests need to be fixed and the entries here removed > >-^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_computer_add_trust > ^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_computer_modify > ^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_user_add_0_uac > ^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_user_modify >diff --git a/source4/dsdb/tests/python/sam.py b/source4/dsdb/tests/python/sam.py >index 425e51cf72c..330885167bf 100755 >--- a/source4/dsdb/tests/python/sam.py >+++ b/source4/dsdb/tests/python/sam.py >@@ -2298,7 +2298,7 @@ class SamTests(samba.tests.TestCase): > self.fail() > except LdbError as e72: > (num, _) = e72.args >- self.assertEqual(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) >+ self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) > > def test_userAccountControl_computer_modify(self): >-- >2.25.1 > > >From 4cf554b5a8fa7a0ef45575c6be0d3c2eb24122c4 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 21 Oct 2021 15:14:28 +1300 >Subject: [PATCH 021/190] CVE-2020-25722 selftest: New objects of > objectclass=computer are workstations by default now > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/uac_objectclass_restrict | 1 - > source4/dsdb/tests/python/sam.py | 4 ++-- > 2 files changed, 2 insertions(+), 3 deletions(-) > >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index f697bc2b6e8..295818d6a1b 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -4,7 +4,6 @@ > # All these tests need to be fixed and the entries here removed > > ^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_computer_modify >-^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_user_add_0_uac > ^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_user_modify > ^samba4.sam.python\(fl2008r2dc\).__main__.SamTests.test_users_groups\(fl2008r2dc\) > ^samba4.ldap.python\(ad_dc_default\).__main__.BasicTests.test_all\(ad_dc_default\) >diff --git a/source4/dsdb/tests/python/sam.py b/source4/dsdb/tests/python/sam.py >index 330885167bf..d7f5ed2c7f9 100755 >--- a/source4/dsdb/tests/python/sam.py >+++ b/source4/dsdb/tests/python/sam.py >@@ -2206,7 +2206,7 @@ class SamTests(samba.tests.TestCase): > attrs=["sAMAccountType", "userAccountControl"]) > self.assertTrue(len(res1) == 1) > self.assertEqual(int(res1[0]["sAMAccountType"][0]), >- ATYPE_NORMAL_ACCOUNT) >+ ATYPE_WORKSTATION_TRUST) > self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE == 0) > self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_PASSWD_NOTREQD == 0) > delete_force(self.ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn) >@@ -2314,7 +2314,7 @@ class SamTests(samba.tests.TestCase): > attrs=["sAMAccountType", "userAccountControl"]) > self.assertTrue(len(res1) == 1) > self.assertEqual(int(res1[0]["sAMAccountType"][0]), >- ATYPE_NORMAL_ACCOUNT) >+ ATYPE_WORKSTATION_TRUST) > self.assertTrue(int(res1[0]["userAccountControl"][0]) & UF_ACCOUNTDISABLE != 0) > > # As computer you can switch from a normal account to a workstation >-- >2.25.1 > > >From 1229db7cb0a5f441fe2930b87004c0a5e904fd76 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 21 Oct 2021 15:19:19 +1300 >Subject: [PATCH 022/190] CVE-2020-25722 selftest: Adapt sam.py test to > userAccountControl/objectclass restrictions > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/uac_objectclass_restrict | 2 -- > source4/dsdb/tests/python/sam.py | 6 +++--- > 2 files changed, 3 insertions(+), 5 deletions(-) > >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index 295818d6a1b..0971c13c2f0 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -3,8 +3,6 @@ > # > # All these tests need to be fixed and the entries here removed > >-^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_computer_modify >-^samba4.sam.python\(.*\).__main__.SamTests.test_userAccountControl_user_modify > ^samba4.sam.python\(fl2008r2dc\).__main__.SamTests.test_users_groups\(fl2008r2dc\) > ^samba4.ldap.python\(ad_dc_default\).__main__.BasicTests.test_all\(ad_dc_default\) > ^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_users_groups\(ad_dc_default\) >diff --git a/source4/dsdb/tests/python/sam.py b/source4/dsdb/tests/python/sam.py >index d7f5ed2c7f9..dcb3f4f769d 100755 >--- a/source4/dsdb/tests/python/sam.py >+++ b/source4/dsdb/tests/python/sam.py >@@ -2134,7 +2134,7 @@ class SamTests(samba.tests.TestCase): > self.fail() > except LdbError as e67: > (num, _) = e67.args >- self.assertEqual(num, ERR_UNWILLING_TO_PERFORM) >+ self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > > m = Message() > m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) >@@ -2153,7 +2153,7 @@ class SamTests(samba.tests.TestCase): > self.fail() > except LdbError as e68: > (num, _) = e68.args >- self.assertEqual(num, ERR_UNWILLING_TO_PERFORM) >+ self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > > res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn, > scope=SCOPE_BASE, attrs=["sAMAccountType"]) >@@ -2501,7 +2501,7 @@ class SamTests(samba.tests.TestCase): > self.fail() > except LdbError as e76: > (num, _) = e76.args >- self.assertEqual(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) >+ self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > > # "primaryGroupID" does not change if account type remains the same > >-- >2.25.1 > > >From 51cb0071913248ae4af884de93ea2c9d5398cfa9 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Thu, 21 Oct 2021 15:42:46 +1300 >Subject: [PATCH 023/190] CVE-2020-25722 selftest: adapt ldap.py/sam.py > test_all tests to new default computer behaviour > >Objects of objectclass computer are computers by default now and this changes >the sAMAccountType and primaryGroupID as well as userAccountControl > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/uac_objectclass_restrict | 3 --- > source4/dsdb/tests/python/ldap.py | 13 +++++++------ > source4/dsdb/tests/python/sam.py | 4 +++- > 3 files changed, 10 insertions(+), 10 deletions(-) > >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index 0971c13c2f0..7328ca17d80 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -3,9 +3,6 @@ > # > # All these tests need to be fixed and the entries here removed > >-^samba4.sam.python\(fl2008r2dc\).__main__.SamTests.test_users_groups\(fl2008r2dc\) >-^samba4.ldap.python\(ad_dc_default\).__main__.BasicTests.test_all\(ad_dc_default\) >-^samba4.sam.python\(ad_dc_default\).__main__.SamTests.test_users_groups\(ad_dc_default\) > ^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-DC_add_CC_WP_user\(ad_dc_default\) > ^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-DC_add_CC_default_user\(ad_dc_default\) > ^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-a2d-user_add_CC_WP_computer\(ad_dc_default\) >diff --git a/source4/dsdb/tests/python/ldap.py b/source4/dsdb/tests/python/ldap.py >index 0c90a0bbd79..ce02d887792 100755 >--- a/source4/dsdb/tests/python/ldap.py >+++ b/source4/dsdb/tests/python/ldap.py >@@ -47,6 +47,7 @@ from samba.dsdb import (UF_NORMAL_ACCOUNT, > ATYPE_WORKSTATION_TRUST, SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE, > SYSTEM_FLAG_CONFIG_ALLOW_RENAME, SYSTEM_FLAG_CONFIG_ALLOW_MOVE, > SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE) >+from samba.dcerpc.security import DOMAIN_RID_DOMAIN_MEMBERS > > from samba.ndr import ndr_pack, ndr_unpack > from samba.dcerpc import security, lsa >@@ -2017,9 +2018,9 @@ delete: description > self.assertTrue("objectGUID" in res[0]) > self.assertTrue("whenCreated" in res[0]) > self.assertEqual(str(res[0]["objectCategory"][0]), ("CN=Computer,%s" % ldb.get_schema_basedn())) >- self.assertEqual(int(res[0]["primaryGroupID"][0]), 513) >- self.assertEqual(int(res[0]["sAMAccountType"][0]), ATYPE_NORMAL_ACCOUNT) >- self.assertEqual(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE) >+ self.assertEqual(int(res[0]["primaryGroupID"][0]), DOMAIN_RID_DOMAIN_MEMBERS) >+ self.assertEqual(int(res[0]["sAMAccountType"][0]), ATYPE_WORKSTATION_TRUST) >+ self.assertEqual(int(res[0]["userAccountControl"][0]), UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE) > > delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) > >@@ -2498,9 +2499,9 @@ member: cn=ldaptestuser2,cn=users,""" + self.base_dn + """ > self.assertTrue("objectGUID" in res[0]) > self.assertTrue("whenCreated" in res[0]) > self.assertEqual(str(res[0]["objectCategory"]), ("CN=Computer,%s" % ldb.get_schema_basedn())) >- self.assertEqual(int(res[0]["primaryGroupID"][0]), 513) >- self.assertEqual(int(res[0]["sAMAccountType"][0]), ATYPE_NORMAL_ACCOUNT) >- self.assertEqual(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE) >+ self.assertEqual(int(res[0]["primaryGroupID"][0]), DOMAIN_RID_DOMAIN_MEMBERS) >+ self.assertEqual(int(res[0]["sAMAccountType"][0]), ATYPE_WORKSTATION_TRUST) >+ self.assertEqual(int(res[0]["userAccountControl"][0]), UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE) > self.assertEqual(str(res[0]["memberOf"][0]).upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper()) > self.assertEqual(len(res[0]["memberOf"]), 1) > >diff --git a/source4/dsdb/tests/python/sam.py b/source4/dsdb/tests/python/sam.py >index dcb3f4f769d..ca01e2c87cf 100755 >--- a/source4/dsdb/tests/python/sam.py >+++ b/source4/dsdb/tests/python/sam.py >@@ -290,7 +290,9 @@ class SamTests(samba.tests.TestCase): > > ldb.add({ > "dn": "cn=ldaptestuser,cn=users," + self.base_dn, >- "objectclass": "computer"}) >+ "objectclass": "computer", >+ "userAccountControl": str(UF_NORMAL_ACCOUNT | >+ UF_PASSWD_NOTREQD)}) > > res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn, > scope=SCOPE_BASE, attrs=["primaryGroupID"]) >-- >2.25.1 > > >From cc67b09227130acc1429ee6f8c842f6060c5554e Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Fri, 22 Oct 2021 22:40:06 +1300 >Subject: [PATCH 024/190] CVE-2020-25722 selftest: Allow > self.assertRaisesLdbError() to take a list of errors to match with > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > python/samba/tests/__init__.py | 30 ++++++++++++------- > selftest/knownfail.d/uac_objectclass_restrict | 2 -- > .../dsdb/tests/python/user_account_control.py | 5 ++++ > 3 files changed, 25 insertions(+), 12 deletions(-) > >diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py >index 1b1fd984251..9ed8db7d337 100644 >--- a/python/samba/tests/__init__.py >+++ b/python/samba/tests/__init__.py >@@ -20,6 +20,7 @@ > import os > import tempfile > import warnings >+import collections > import ldb > import samba > from samba import param >@@ -195,23 +196,32 @@ class TestCase(unittest.TestCase): > f(*args, **kwargs) > except ldb.LdbError as e: > (num, msg) = e.args >- if num != errcode: >+ if isinstance(errcode, collections.abc.Container): >+ found = num in errcode >+ else: >+ found = num == errcode >+ if not found: > lut = {v: k for k, v in vars(ldb).items() > if k.startswith('ERR_') and isinstance(v, int)} >- self.fail("%s, expected " >- "LdbError %s, (%d) " >- "got %s (%d) " >- "%s" % (message, >- lut.get(errcode), errcode, >- lut.get(num), num, >- msg)) >+ if isinstance(errcode, collections.abc.Container): >+ errcode_name = ' '.join(lut.get(x) for x in errcode) >+ else: >+ errcode_name = lut.get(errcode) >+ self.fail(f"{message}, expected " >+ f"LdbError {errcode_name}, {errcode} " >+ f"got {lut.get(num)} ({num}) " >+ f"{msg}") > else: > lut = {v: k for k, v in vars(ldb).items() > if k.startswith('ERR_') and isinstance(v, int)} >+ if isinstance(errcode, collections.abc.Container): >+ errcode_name = ' '.join(lut.get(x) for x in errcode) >+ else: >+ errcode_name = lut.get(errcode) > self.fail("%s, expected " >- "LdbError %s, (%d) " >+ "LdbError %s, (%s) " > "but we got success" % (message, >- lut.get(errcode), >+ errcode_name, > errcode)) > > >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index 7328ca17d80..ac6f4857bf4 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -20,8 +20,6 @@ > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_user_replace\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_SERVER_TRUST_ACCOUNT_computer_replace\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_replace\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_SERVER_TRUST_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_set_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_NORMAL_ACCOUNT\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_SERVER_TRUST_ACCOUNT_deladd_priv\(ad_dc_default\) >diff --git a/source4/dsdb/tests/python/user_account_control.py b/source4/dsdb/tests/python/user_account_control.py >index bda005e929b..6eb864bb21f 100755 >--- a/source4/dsdb/tests/python/user_account_control.py >+++ b/source4/dsdb/tests/python/user_account_control.py >@@ -553,6 +553,9 @@ class UserAccountControlTests(samba.tests.TestCase): > if (bit in priv_bits): > self.fail("Unexpectedly able to set userAccountControl bit 0x%08X (%s), on %s" > % (bit, bit_str, m.dn)) >+ if (bit in account_types and bit != UF_NORMAL_ACCOUNT): >+ self.fail("Unexpectedly able to set userAccountControl bit 0x%08X (%s), on %s" >+ % (bit, bit_str, m.dn)) > except LdbError as e: > (enum, estr) = e.args > if bit in invalid_bits: >@@ -560,6 +563,8 @@ class UserAccountControlTests(samba.tests.TestCase): > ldb.ERR_OTHER, > "was not able to set 0x%08X (%s) on %s" > % (bit, bit_str, m.dn)) >+ elif (bit in account_types): >+ self.assertIn(enum, [ldb.ERR_OBJECT_CLASS_VIOLATION, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS]) > elif (bit in priv_bits): > self.assertEqual(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, enum) > else: >-- >2.25.1 > > >From 57f8e7967d06918ec3add8650fc767a35eacc47f Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Fri, 22 Oct 2021 22:54:52 +1300 >Subject: [PATCH 025/190] CVE-2020-25722 selftest/user_account_control: Allow a > broader set of possible errors > >This favors a test that confirms we got an error over getting exactly >the right error, at least for now. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/uac_objectclass_restrict | 4 ---- > selftest/knownfail.d/user_account_control | 1 - > source4/dsdb/tests/python/user_account_control.py | 12 ++++++++---- > 3 files changed, 8 insertions(+), 9 deletions(-) > delete mode 100644 selftest/knownfail.d/user_account_control > >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index ac6f4857bf4..1d72442f8a8 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -22,10 +22,6 @@ > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_replace\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_NORMAL_ACCOUNT\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_SERVER_TRUST_ACCOUNT_deladd_priv\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_SERVER_TRUST_ACCOUNT_deladd_wp\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_SERVER_TRUST_ACCOUNT_replace_priv\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_uac_mod_lock_UF_NORMAL_ACCOUNT_UF_SERVER_TRUST_ACCOUNT_replace_wp\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_INTERDOMAIN_TRUST_ACCOUNT\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_NORMAL_ACCOUNT\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_NORMAL_ACCOUNT_UF_PASSWD_NOTREQD\(ad_dc_default\) >diff --git a/selftest/knownfail.d/user_account_control b/selftest/knownfail.d/user_account_control >deleted file mode 100644 >index ad3af678708..00000000000 >--- a/selftest/knownfail.d/user_account_control >+++ /dev/null >@@ -1 +0,0 @@ >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_add_computer_cc_normal_bare.ad_dc_default >diff --git a/source4/dsdb/tests/python/user_account_control.py b/source4/dsdb/tests/python/user_account_control.py >index 6eb864bb21f..4da7c160957 100755 >--- a/source4/dsdb/tests/python/user_account_control.py >+++ b/source4/dsdb/tests/python/user_account_control.py >@@ -443,7 +443,8 @@ class UserAccountControlTests(samba.tests.TestCase): > m.dn = res[0].dn > m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_NORMAL_ACCOUNT), > ldb.FLAG_MOD_REPLACE, "userAccountControl") >- self.assertRaisesLdbError(ldb.ERR_UNWILLING_TO_PERFORM, >+ self.assertRaisesLdbError([ldb.ERR_OBJECT_CLASS_VIOLATION, >+ ldb.ERR_UNWILLING_TO_PERFORM], > f"Unexpectedly able to set userAccountControl to be an Normal " > "account without |UF_PASSWD_NOTREQD Unexpectedly able to " > "set userAccountControl to be a workstation on {m.dn}", >@@ -1053,12 +1054,14 @@ class UserAccountControlTests(samba.tests.TestCase): > samdb.modify(m) > elif (account_type == UF_NORMAL_ACCOUNT) and \ > (account_type2 == UF_SERVER_TRUST_ACCOUNT) and not priv: >- self.assertRaisesLdbError(ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, >+ self.assertRaisesLdbError([ldb.ERR_OBJECT_CLASS_VIOLATION, >+ ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS], > f"Should have been unable to change {account_type_str} to {account_type2_str}", > samdb.modify, m) > elif (account_type == UF_NORMAL_ACCOUNT) and \ > (account_type2 == UF_SERVER_TRUST_ACCOUNT) and priv: >- self.assertRaisesLdbError(ldb.ERR_UNWILLING_TO_PERFORM, >+ self.assertRaisesLdbError([ldb.ERR_OBJECT_CLASS_VIOLATION, >+ ldb.ERR_UNWILLING_TO_PERFORM], > f"Should have been unable to change {account_type_str} to {account_type2_str}", > samdb.modify, m) > elif (account_type == UF_WORKSTATION_TRUST_ACCOUNT) and \ >@@ -1131,7 +1134,8 @@ class UserAccountControlTests(samba.tests.TestCase): > m["1objectclass"] = ldb.MessageElement(new_objectclass, > ldb.FLAG_MOD_ADD, "objectclass") > >- self.assertRaisesLdbError(ldb.ERR_UNWILLING_TO_PERFORM, >+ self.assertRaisesLdbError([ldb.ERR_OBJECT_CLASS_VIOLATION, >+ ldb.ERR_UNWILLING_TO_PERFORM], > "Should have been unable Able to change objectclass of a {objectclass}", > self.admin_samdb.modify, m) > >-- >2.25.1 > > >From 8e58fe7db94bb0309eedfc2b70e7aa3048235191 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Fri, 22 Oct 2021 23:41:23 +1300 >Subject: [PATCH 026/190] CVE-2020-25722 selftest/user_account_control: more > work to cope with UAC/objectclass defaults and lock > >This new restriction breaks a large number of assumptions in the tests, like >that you can remove some UF_ flags, because it turns out doing so will >make the 'computer' a 'user' again, and this will fail. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail.d/uac_objectclass_restrict | 6 --- > .../dsdb/tests/python/user_account_control.py | 46 ++++++++++++------- > 2 files changed, 29 insertions(+), 23 deletions(-) > >diff --git a/selftest/knownfail.d/uac_objectclass_restrict b/selftest/knownfail.d/uac_objectclass_restrict >index 1d72442f8a8..c4d4507c833 100644 >--- a/selftest/knownfail.d/uac_objectclass_restrict >+++ b/selftest/knownfail.d/uac_objectclass_restrict >@@ -14,14 +14,8 @@ > ^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-t4d-user_mod-del-add_CC_default_computer\(ad_dc_default\) > ^samba4.priv_attrs.strict.python\(ad_dc_default\).__main__.PrivAttrsTests.test_priv_attr_userAccountControl-t4d-user_mod-replace_CC_default_computer\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_add_computer_sd_cc\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_admin_mod_uac\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_mod_computer_cc\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_computer_replace\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_NORMAL_ACCOUNT_user_replace\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_SERVER_TRUST_ACCOUNT_computer_replace\(ad_dc_default\) > ^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_objectclass_mod_lock_UF_WORKSTATION_TRUST_ACCOUNT_computer_replace\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_NORMAL_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_unrelated_modify_UF_WORKSTATION_TRUST_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_INTERDOMAIN_TRUST_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_NORMAL_ACCOUNT\(ad_dc_default\) >-^samba4.user_account_control.python\(ad_dc_default\).__main__.UserAccountControlTests.test_uac_bits_add_UF_NORMAL_ACCOUNT_UF_PASSWD_NOTREQD\(ad_dc_default\) >diff --git a/source4/dsdb/tests/python/user_account_control.py b/source4/dsdb/tests/python/user_account_control.py >index 4da7c160957..02ade60b5f8 100755 >--- a/source4/dsdb/tests/python/user_account_control.py >+++ b/source4/dsdb/tests/python/user_account_control.py >@@ -392,11 +392,10 @@ class UserAccountControlTests(samba.tests.TestCase): > m.dn = res[0].dn > m["userAccountControl"] = ldb.MessageElement(str(samba.dsdb.UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD), > ldb.FLAG_MOD_REPLACE, "userAccountControl") >- try: >- self.samdb.modify(m) >- except LdbError as e: >- (enum, estr) = e.args >- self.fail(f"got {estr} setting userAccountControl to UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD") >+ >+ self.assertRaisesLdbError(ldb.ERR_OBJECT_CLASS_VIOLATION, >+ f"Unexpectedly able to set userAccountControl as to UF_NORMAL_ACCOUNT|UF_PASSWD_NOTREQD on {m.dn}", >+ self.samdb.modify, m) > > m = ldb.Message() > m.dn = res[0].dn >@@ -460,7 +459,7 @@ class UserAccountControlTests(samba.tests.TestCase): > scope=SCOPE_SUBTREE, > attrs=["userAccountControl"]) > >- self.assertEqual(int(res[0]["userAccountControl"][0]), (UF_NORMAL_ACCOUNT | >+ self.assertEqual(int(res[0]["userAccountControl"][0]), (UF_WORKSTATION_TRUST_ACCOUNT | > UF_ACCOUNTDISABLE | > UF_PASSWD_NOTREQD)) > >@@ -640,11 +639,9 @@ class UserAccountControlTests(samba.tests.TestCase): > scope=SCOPE_SUBTREE, > attrs=["userAccountControl"]) > >- if account_type == UF_WORKSTATION_TRUST_ACCOUNT: >- self.assertEqual(orig_uac, account_type) >- else: >- self.assertEqual(orig_uac & UF_NORMAL_ACCOUNT, >- account_type) >+ self.assertEqual(len(res), 1) >+ reset_uac = int(res[0]["userAccountControl"][0]) >+ self.assertEqual(orig_uac, reset_uac) > > m = ldb.Message() > m.dn = res[0].dn >@@ -663,14 +660,16 @@ class UserAccountControlTests(samba.tests.TestCase): > # No point going on, try the next bit > continue > elif bit in super_priv_bits: >- self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS) >+ self.assertIn(enum, (ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, >+ ldb.ERR_OBJECT_CLASS_VIOLATION)) > # No point going on, try the next bit > continue > > elif (account_type == UF_NORMAL_ACCOUNT) \ > and (bit in account_types) \ > and (bit != account_type): >- self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM) >+ self.assertIn(enum, (ldb.ERR_UNWILLING_TO_PERFORM, >+ ldb.ERR_OBJECT_CLASS_VIOLATION)) > continue > > elif (account_type == UF_WORKSTATION_TRUST_ACCOUNT) \ >@@ -748,7 +747,10 @@ class UserAccountControlTests(samba.tests.TestCase): > > except LdbError as e3: > (enum, estr) = e3.args >- if bit in priv_to_remove_bits: >+ if account_type == UF_WORKSTATION_TRUST_ACCOUNT: >+ # Because removing any bit would change the account back to a user, which is locked by objectclass >+ self.assertIn(enum, (ldb.ERR_OBJECT_CLASS_VIOLATION, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)) >+ elif bit in priv_to_remove_bits: > self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS) > else: > self.fail("Unexpectedly unable to remove userAccountControl bit 0x%08X on %s: %s" % (bit, m.dn, estr)) >@@ -767,7 +769,7 @@ class UserAccountControlTests(samba.tests.TestCase): > self.assertEqual(int(res[0]["userAccountControl"][0]), > bit | UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD, > "bit 0X%08x should not have been removed" % bit) >- else: >+ elif account_type != UF_WORKSTATION_TRUST_ACCOUNT: > self.assertEqual(int(res[0]["userAccountControl"][0]), > UF_NORMAL_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD, > "bit 0X%08x should have been removed" % bit) >@@ -818,9 +820,19 @@ class UserAccountControlTests(samba.tests.TestCase): > bit_str, > computername)) > elif bit in priv_bits: >- self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS) >+ if bit == UF_INTERDOMAIN_TRUST_ACCOUNT: >+ self.assertIn(enum, (ldb.ERR_OBJECT_CLASS_VIOLATION, >+ ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS)) >+ else: >+ self.assertEqual(enum, ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS) > elif bit in unwilling_bits: >- self.assertEqual(enum, ldb.ERR_UNWILLING_TO_PERFORM) >+ # This can fail early as user in a computer is not permitted as non-admin >+ self.assertIn(enum, (ldb.ERR_UNWILLING_TO_PERFORM, >+ ldb.ERR_OBJECT_CLASS_VIOLATION)) >+ elif bit & UF_NORMAL_ACCOUNT: >+ # This can fail early as user in a computer is not permitted as non-admin >+ self.assertIn(enum, (ldb.ERR_UNWILLING_TO_PERFORM, >+ ldb.ERR_OBJECT_CLASS_VIOLATION)) > else: > self.fail("Unable to set userAccountControl bit 0x%08X (%s) on %s: %s" > % (bit, >-- >2.25.1 > > >From c7fafac40d506e4063e7e464012c35cc41c361e6 Mon Sep 17 00:00:00 2001 >From: Andrew Bartlett <abartlet@samba.org> >Date: Mon, 27 Sep 2021 11:20:19 +1300 >Subject: [PATCH 027/190] CVE-2020-25721 krb5pac: Add new buffers for > samAccountName and objectSID > >These appear when PAC_UPN_DNS_FLAG_HAS_SAM_NAME_AND_SID is set. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14835 > >Signed-off-by: Andrew Bartlett <abartlet@samba.org> >--- > librpc/idl/krb5pac.idl | 18 ++++++++++++++++-- > librpc/ndr/ndr_krb5pac.c | 4 ++-- > 2 files changed, 18 insertions(+), 4 deletions(-) > >diff --git a/librpc/idl/krb5pac.idl b/librpc/idl/krb5pac.idl >index 515150ab9cd..ed488dee425 100644 >--- a/librpc/idl/krb5pac.idl >+++ b/librpc/idl/krb5pac.idl >@@ -86,15 +86,29 @@ interface krb5pac > } PAC_CONSTRAINED_DELEGATION; > > typedef [bitmap32bit] bitmap { >- PAC_UPN_DNS_FLAG_CONSTRUCTED = 0x00000001 >+ PAC_UPN_DNS_FLAG_CONSTRUCTED = 0x00000001, >+ PAC_UPN_DNS_FLAG_HAS_SAM_NAME_AND_SID = 0x00000002 > } PAC_UPN_DNS_FLAGS; > >+ typedef struct { >+ [value(2*strlen_m(samaccountname))] uint16 samaccountname_size; >+ [relative_short,subcontext(0),subcontext_size(samaccountname_size),flag(NDR_ALIGN8|STR_NOTERM|NDR_REMAINING)] string *samaccountname; >+ [value(ndr_size_dom_sid(objectsid, ndr->flags))] uint16 objectsid_size; >+ [relative_short,subcontext(0),subcontext_size(objectsid_size)] dom_sid *objectsid; >+ } PAC_UPN_DNS_INFO_SAM_NAME_AND_SID; >+ >+ typedef [nodiscriminant] union { >+ [case(PAC_UPN_DNS_FLAG_HAS_SAM_NAME_AND_SID)] PAC_UPN_DNS_INFO_SAM_NAME_AND_SID sam_name_and_sid; >+ [default]; >+ } PAC_UPN_DNS_INFO_EX; >+ > typedef struct { > [value(2*strlen_m(upn_name))] uint16 upn_name_size; > [relative_short,subcontext(0),subcontext_size(upn_name_size),flag(NDR_ALIGN8|STR_NOTERM|NDR_REMAINING)] string *upn_name; > [value(2*strlen_m(dns_domain_name))] uint16 dns_domain_name_size; > [relative_short,subcontext(0),subcontext_size(dns_domain_name_size),flag(NDR_ALIGN8|STR_NOTERM|NDR_REMAINING)] string *dns_domain_name; > PAC_UPN_DNS_FLAGS flags; >+ [switch_is(flags & PAC_UPN_DNS_FLAG_HAS_SAM_NAME_AND_SID)] PAC_UPN_DNS_INFO_EX ex; > } PAC_UPN_DNS_INFO; > > typedef [public] struct { >@@ -142,7 +156,7 @@ interface krb5pac > > typedef [public,nopush,nopull] struct { > PAC_TYPE type; >- [value(_ndr_size_PAC_INFO(info, type, 0))] uint32 _ndr_size; >+ [value(_ndr_size_PAC_INFO(info, type, LIBNDR_FLAG_ALIGN8))] uint32 _ndr_size; > /* > * We need to have two subcontexts to get the padding right, > * the outer subcontext uses NDR_ROUND(_ndr_size, 8), while >diff --git a/librpc/ndr/ndr_krb5pac.c b/librpc/ndr/ndr_krb5pac.c >index a9ae2c4a789..57b28df9e52 100644 >--- a/librpc/ndr/ndr_krb5pac.c >+++ b/librpc/ndr/ndr_krb5pac.c >@@ -41,7 +41,7 @@ enum ndr_err_code ndr_push_PAC_BUFFER(struct ndr_push *ndr, int ndr_flags, const > if (ndr_flags & NDR_SCALARS) { > NDR_CHECK(ndr_push_align(ndr, 4)); > NDR_CHECK(ndr_push_PAC_TYPE(ndr, NDR_SCALARS, r->type)); >- NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, _ndr_size_PAC_INFO(r->info,r->type,0))); >+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, _ndr_size_PAC_INFO(r->info,r->type,LIBNDR_FLAG_ALIGN8))); > { > uint32_t _flags_save_PAC_INFO = ndr->flags; > ndr_set_flags(&ndr->flags, LIBNDR_FLAG_ALIGN8); >@@ -59,7 +59,7 @@ enum ndr_err_code ndr_push_PAC_BUFFER(struct ndr_push *ndr, int ndr_flags, const > { > struct ndr_push *_ndr_info_pad; > struct ndr_push *_ndr_info; >- size_t _ndr_size = _ndr_size_PAC_INFO(r->info, r->type, 0); >+ size_t _ndr_size = _ndr_size_PAC_INFO(r->info, r->type, LIBNDR_FLAG_ALIGN8); > NDR_CHECK(ndr_push_subcontext_start(ndr, &_ndr_info_pad, 0, NDR_ROUND(_ndr_size, 8))); > NDR_CHECK(ndr_push_subcontext_start(_ndr_info_pad, &_ndr_info, 0, _ndr_size)); > NDR_CHECK(ndr_push_set_switch_value(_ndr_info, r->info, r->type)); >-- >2.25.1 > > >From 83d4fc9d7b2454da1ac0a8906c034c210d9e91cf Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Wed, 20 Oct 2021 15:48:35 +1300 >Subject: [PATCH 028/190] CVE-2020-25718 tests/krb5: Allow tests accounts to > replicate to RODC > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14558 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/rodc_tests.py | 2 ++ > 1 file changed, 2 insertions(+) > >diff --git a/python/samba/tests/krb5/rodc_tests.py b/python/samba/tests/krb5/rodc_tests.py >index 302ae865cf1..0e252d90262 100755 >--- a/python/samba/tests/krb5/rodc_tests.py >+++ b/python/samba/tests/krb5/rodc_tests.py >@@ -41,11 +41,13 @@ class RodcKerberosTests(KDCBaseTest): > user_creds = self.get_cached_creds( > account_type=self.AccountType.USER, > opts={ >+ 'allowed_replication': True, > 'revealed_to_rodc': True > }) > target_creds = self.get_cached_creds( > account_type=self.AccountType.COMPUTER, > opts={ >+ 'allowed_replication': True, > 'revealed_to_rodc': True > }) > >-- >2.25.1 > > >From 392c39c202985aaae2b89256af9227c98d0c6257 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Mon, 18 Oct 2021 14:59:01 +1300 >Subject: [PATCH 029/190] CVE-2020-25719 CVE-2020-25717 tests/krb5: Modify > get_service_ticket() to use _generic_kdc_exchange() > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14799 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14561 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 54 ++++++++++++------------ > 1 file changed, 27 insertions(+), 27 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index 8ae9c24b0fc..c129883e7cd 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -1275,7 +1275,7 @@ class KDCBaseTest(RawKerberosTest): > expected_flags=None, unexpected_flags=None, > fresh=False): > user_name = tgt.cname['name-string'][0] >- target_name = target_creds.get_username() >+ target_name = target_creds.get_username()[:-1] > cache_key = (user_name, target_name, service, to_rodc, kdc_options) > > if not fresh: >@@ -1288,40 +1288,40 @@ class KDCBaseTest(RawKerberosTest): > > if kdc_options is None: > kdc_options = '0' >- kdc_options = krb5_asn1.KDCOptions(kdc_options) >- >- key = tgt.session_key >- ticket = tgt.ticket >+ kdc_options = str(krb5_asn1.KDCOptions(kdc_options)) > >- cname = tgt.cname >- realm = tgt.crealm >- >- target_name = target_creds.get_username()[:-1] > sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, > names=[service, target_name]) >+ srealm = target_creds.get_realm() > >- rep, enc_part = self.tgs_req(cname, sname, realm, ticket, key, etype, >- to_rodc=to_rodc, >- service_creds=target_creds, >- kdc_options=kdc_options, >- expected_flags=expected_flags, >- unexpected_flags=unexpected_flags) >+ authenticator_subkey = self.RandomKey(kcrypto.Enctype.AES256) > >- service_ticket = rep['ticket'] >+ decryption_key = self.TicketDecryptionKey_from_creds(target_creds) > >- ticket_etype = service_ticket['enc-part']['etype'] >- target_key = self.TicketDecryptionKey_from_creds(target_creds, >- etype=ticket_etype) >+ kdc_exchange_dict = self.tgs_exchange_dict( >+ expected_crealm=tgt.crealm, >+ expected_cname=tgt.cname, >+ expected_srealm=srealm, >+ expected_sname=sname, >+ expected_supported_etypes=target_creds.tgs_supported_enctypes, >+ expected_flags=expected_flags, >+ unexpected_flags=unexpected_flags, >+ ticket_decryption_key=decryption_key, >+ check_rep_fn=self.generic_check_kdc_rep, >+ check_kdc_private_fn=self.generic_check_kdc_private, >+ tgt=tgt, >+ authenticator_subkey=authenticator_subkey, >+ kdc_options=kdc_options, >+ to_rodc=to_rodc) > >- session_key = self.EncryptionKey_import(enc_part['key']) >+ rep = self._generic_kdc_exchange(kdc_exchange_dict, >+ cname=None, >+ realm=srealm, >+ sname=sname, >+ etypes=etype) >+ self.check_tgs_reply(rep) > >- service_ticket_creds = KerberosTicketCreds(service_ticket, >- session_key, >- crealm=realm, >- cname=cname, >- srealm=realm, >- sname=sname, >- decryption_key=target_key) >+ service_ticket_creds = kdc_exchange_dict['rep_ticket_creds'] > > if to_rodc: > krbtgt_creds = self.get_rodc_krbtgt_creds() >-- >2.25.1 > > >From cda8221bbe1eb94b90c0fc69d6cf7e9bac2257e4 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Mon, 18 Oct 2021 15:00:38 +1300 >Subject: [PATCH 030/190] CVE-2020-25719 CVE-2020-25717 tests/krb5: Add > pac_request parameter to get_service_ticket() > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14799 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14561 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 8 ++++++-- > 1 file changed, 6 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index c129883e7cd..813af767dbd 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -1273,10 +1273,11 @@ class KDCBaseTest(RawKerberosTest): > def get_service_ticket(self, tgt, target_creds, service='host', > to_rodc=False, kdc_options=None, > expected_flags=None, unexpected_flags=None, >- fresh=False): >+ pac_request=True, expect_pac=True, fresh=False): > user_name = tgt.cname['name-string'][0] > target_name = target_creds.get_username()[:-1] >- cache_key = (user_name, target_name, service, to_rodc, kdc_options) >+ cache_key = (user_name, target_name, service, to_rodc, kdc_options, >+ pac_request) > > if not fresh: > ticket = self.tkt_cache.get(cache_key) >@@ -1312,6 +1313,8 @@ class KDCBaseTest(RawKerberosTest): > tgt=tgt, > authenticator_subkey=authenticator_subkey, > kdc_options=kdc_options, >+ pac_request=pac_request, >+ expect_pac=expect_pac, > to_rodc=to_rodc) > > rep = self._generic_kdc_exchange(kdc_exchange_dict, >@@ -1329,6 +1332,7 @@ class KDCBaseTest(RawKerberosTest): > krbtgt_creds = self.get_krbtgt_creds() > krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) > self.verify_ticket(service_ticket_creds, krbtgt_key, >+ expect_pac=expect_pac, > expect_ticket_checksum=self.tkt_sig_support) > > self.tkt_cache[cache_key] = service_ticket_creds >-- >2.25.1 > > >From 6b677a8393ce7cfe2381ca816ebd7913be351985 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 8 Oct 2021 15:40:09 +1300 >Subject: [PATCH 031/190] CVE-2020-25722 tests/krb5: Allow creating server > accounts > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14776 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 7 +++++++ > 1 file changed, 7 insertions(+) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index 813af767dbd..a0da89041c4 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -38,12 +38,14 @@ from samba.dsdb import ( > DS_DOMAIN_FUNCTION_2000, > DS_DOMAIN_FUNCTION_2008, > DS_GUID_COMPUTERS_CONTAINER, >+ DS_GUID_DOMAIN_CONTROLLERS_CONTAINER, > DS_GUID_USERS_CONTAINER, > UF_WORKSTATION_TRUST_ACCOUNT, > UF_NO_AUTH_DATA_REQUIRED, > UF_NORMAL_ACCOUNT, > UF_NOT_DELEGATED, > UF_PARTIAL_SECRETS_ACCOUNT, >+ UF_SERVER_TRUST_ACCOUNT, > UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION > ) > from samba.join import DCJoinContext >@@ -94,6 +96,7 @@ class KDCBaseTest(RawKerberosTest): > class AccountType(Enum): > USER = auto() > COMPUTER = auto() >+ SERVER = auto() > > @classmethod > def setUpClass(cls): >@@ -245,6 +248,8 @@ class KDCBaseTest(RawKerberosTest): > if ou is None: > if account_type is account_type.COMPUTER: > guid = DS_GUID_COMPUTERS_CONTAINER >+ elif account_type is account_type.SERVER: >+ guid = DS_GUID_DOMAIN_CONTROLLERS_CONTAINER > else: > guid = DS_GUID_USERS_CONTAINER > >@@ -265,6 +270,8 @@ class KDCBaseTest(RawKerberosTest): > account_name += '$' > if account_type is self.AccountType.COMPUTER: > account_control |= UF_WORKSTATION_TRUST_ACCOUNT >+ elif account_type is self.AccountType.SERVER: >+ account_control |= UF_SERVER_TRUST_ACCOUNT > else: > self.fail() > >-- >2.25.1 > > >From 25f2815cd3508867509a8b5a9e5b8d4d7a998b1e Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Thu, 30 Sep 2021 16:53:22 +1300 >Subject: [PATCH 032/190] CVE-2020-25719 tests/krb5: Add is_tgt() helper method > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14686 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/raw_testcase.py | 7 +++++-- > 1 file changed, 5 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py >index fdf078ea788..055209fd09d 100644 >--- a/python/samba/tests/krb5/raw_testcase.py >+++ b/python/samba/tests/krb5/raw_testcase.py >@@ -3086,8 +3086,7 @@ class RawKerberosTest(TestCaseInTempDir): > def verify_ticket(self, ticket, krbtgt_key, expect_pac=True, > expect_ticket_checksum=True): > # Check if the ticket is a TGT. >- sname = ticket.ticket['sname'] >- is_tgt = self.is_tgs(sname) >+ is_tgt = self.is_tgt(ticket) > > # Decrypt the ticket. > >@@ -3506,6 +3505,10 @@ class RawKerberosTest(TestCaseInTempDir): > name = principal['name-string'][0] > return name in ('krbtgt', b'krbtgt') > >+ def is_tgt(self, ticket): >+ sname = ticket.ticket['sname'] >+ return self.is_tgs(sname) >+ > def get_empty_pac(self): > return self.AuthorizationData_create(AD_WIN2K_PAC, bytes(1)) > >-- >2.25.1 > > >From 3301fd2b2dcc52d179b8b4318722b52f591049aa Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 19 Oct 2021 15:02:10 +1300 >Subject: [PATCH 033/190] CVE-2020-25719 tests/krb5: Add method to get unique > username for test accounts > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14686 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 9 +++++++-- > 1 file changed, 7 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index a0da89041c4..e85574c51cb 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -711,8 +711,7 @@ class KDCBaseTest(RawKerberosTest): > > rodc_dn = self.get_server_dn(rodc_samdb) > >- user_name = self.account_base + str(self.account_id) >- type(self).account_id += 1 >+ user_name = self.get_new_username() > if name_prefix is not None: > user_name = name_prefix + user_name > if name_suffix is not None: >@@ -821,6 +820,12 @@ class KDCBaseTest(RawKerberosTest): > > return creds > >+ def get_new_username(self): >+ user_name = self.account_base + str(self.account_id) >+ type(self).account_id += 1 >+ >+ return user_name >+ > def get_client_creds(self, > allow_missing_password=False, > allow_missing_keys=True): >-- >2.25.1 > > >From 42486d5fbad36376b712f121c37589c96be4a9a7 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Wed, 20 Oct 2021 15:48:20 +1300 >Subject: [PATCH 034/190] MS CVE-2020-17049 tests/krb5: Allow tests to pass if > ticket signature checksum type is wrong > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14642 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/raw_testcase.py | 21 +++++++++++++++++++-- > 1 file changed, 19 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py >index 055209fd09d..62e1b9867dd 100644 >--- a/python/samba/tests/krb5/raw_testcase.py >+++ b/python/samba/tests/krb5/raw_testcase.py >@@ -2375,6 +2375,13 @@ class RawKerberosTest(TestCaseInTempDir): > krbtgt_creds = self.get_krbtgt_creds() > krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) > >+ krbtgt_keys = [krbtgt_key] >+ if not self.strict_checking: >+ krbtgt_key_rc4 = self.TicketDecryptionKey_from_creds( >+ krbtgt_creds, >+ etype=kcrypto.Enctype.RC4) >+ krbtgt_keys.append(krbtgt_key_rc4) >+ > expect_pac = kdc_exchange_dict['expect_pac'] > > ticket_session_key = None >@@ -2522,7 +2529,7 @@ class RawKerberosTest(TestCaseInTempDir): > self.assertIsNotNone(ticket_decryption_key) > > if ticket_decryption_key is not None: >- self.verify_ticket(ticket_creds, krbtgt_key, expect_pac=expect_pac, >+ self.verify_ticket(ticket_creds, krbtgt_keys, expect_pac=expect_pac, > expect_ticket_checksum=expect_ticket_checksum > or self.tkt_sig_support) > >@@ -3083,7 +3090,7 @@ class RawKerberosTest(TestCaseInTempDir): > ticket_blob) > self.assertEqual(expected_checksum, checksum) > >- def verify_ticket(self, ticket, krbtgt_key, expect_pac=True, >+ def verify_ticket(self, ticket, krbtgt_keys, expect_pac=True, > expect_ticket_checksum=True): > # Check if the ticket is a TGT. > is_tgt = self.is_tgt(ticket) >@@ -3171,6 +3178,16 @@ class RawKerberosTest(TestCaseInTempDir): > > kdc_checksum, kdc_ctype = checksums[ > krb5pac.PAC_TYPE_KDC_CHECKSUM] >+ >+ if isinstance(krbtgt_keys, collections.abc.Container): >+ if self.strict_checking: >+ krbtgt_key = krbtgt_keys[0] >+ else: >+ krbtgt_key = next(key for key in krbtgt_keys >+ if key.ctype == kdc_ctype) >+ else: >+ krbtgt_key = krbtgt_keys >+ > krbtgt_key.verify_rodc_checksum(KU_NON_KERB_CKSUM_SALT, > server_checksum, > kdc_ctype, >-- >2.25.1 > > >From 8bf67d1273f81c7d6a27f9629f2b8c4f2d846e2e Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Thu, 21 Oct 2021 16:46:23 +1300 >Subject: [PATCH 035/190] CVE-2020-25721 tests/krb5: Check PAC buffer types > when STRICT_CHECKING=0 > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14835 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/raw_testcase.py | 23 ++++++++++++++--------- > 1 file changed, 14 insertions(+), 9 deletions(-) > >diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py >index 62e1b9867dd..8e55790272a 100644 >--- a/python/samba/tests/krb5/raw_testcase.py >+++ b/python/samba/tests/krb5/raw_testcase.py >@@ -1102,13 +1102,14 @@ class RawKerberosTest(TestCaseInTempDir): > f"unexpected in {v}") > > def assertSequenceElementsEqual(self, expected, got, *, >- require_strict=None): >- if self.strict_checking: >+ require_strict=None, >+ require_ordered=True): >+ if self.strict_checking and require_ordered: > self.assertEqual(expected, got) > else: > fail_msg = f'expected: {expected} got: {got}' > >- if require_strict is not None: >+ if not self.strict_checking and require_strict is not None: > fail_msg += f' (ignoring: {require_strict})' > expected = (x for x in expected if x not in require_strict) > got = (x for x in got if x not in require_strict) >@@ -2569,12 +2570,16 @@ class RawKerberosTest(TestCaseInTempDir): > if not self.is_tgs(expected_sname): > expected_types.append(krb5pac.PAC_TYPE_TICKET_CHECKSUM) > >- if self.strict_checking: >- buffer_types = [pac_buffer.type >- for pac_buffer in pac.buffers] >- self.assertCountEqual(expected_types, buffer_types, >- f'expected: {expected_types} ' >- f'got: {buffer_types}') >+ require_strict = {krb5pac.PAC_TYPE_CLIENT_CLAIMS_INFO} >+ if not self.tkt_sig_support: >+ require_strict.add(krb5pac.PAC_TYPE_TICKET_CHECKSUM) >+ >+ buffer_types = [pac_buffer.type >+ for pac_buffer in pac.buffers] >+ self.assertSequenceElementsEqual( >+ expected_types, buffer_types, >+ require_ordered=False, >+ require_strict=require_strict) > > expected_account_name = kdc_exchange_dict['expected_account_name'] > expected_sid = kdc_exchange_dict['expected_sid'] >-- >2.25.1 > > >From ebb068c264a2886a102decc019730abdc544b6a1 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 22 Oct 2021 11:37:31 +1300 >Subject: [PATCH 036/190] CVE-2020-25719 CVE-2020-25717 tests/krb5: Refactor > create_ccache_with_user() to take credentials of target service > >This allows us to use get_tgt() and get_service_ticket() to obtain >tickets, which simplifies the logic. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14799 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14561 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 43 ++++++------------------ > python/samba/tests/krb5/test_ccache.py | 2 +- > python/samba/tests/krb5/test_ldap.py | 7 ++-- > python/samba/tests/krb5/test_rpc.py | 7 ++-- > python/samba/tests/krb5/test_smb.py | 7 ++-- > 5 files changed, 27 insertions(+), 39 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index e85574c51cb..e77a940f411 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -1283,11 +1283,13 @@ class KDCBaseTest(RawKerberosTest): > return rep, enc_part > > def get_service_ticket(self, tgt, target_creds, service='host', >+ target_name=None, > to_rodc=False, kdc_options=None, > expected_flags=None, unexpected_flags=None, > pac_request=True, expect_pac=True, fresh=False): > user_name = tgt.cname['name-string'][0] >- target_name = target_creds.get_username()[:-1] >+ if target_name is None: >+ target_name = target_creds.get_username()[:-1] > cache_key = (user_name, target_name, service, to_rodc, kdc_options, > pac_request) > >@@ -1669,51 +1671,28 @@ class KDCBaseTest(RawKerberosTest): > > return cachefile > >- def create_ccache_with_user(self, user_credentials, mach_name, >- service="host"): >+ def create_ccache_with_user(self, user_credentials, mach_credentials, >+ service="host", target_name=None): > # Obtain a service ticket authorising the user and place it into a > # newly created credentials cache file. > > user_name = user_credentials.get_username() > realm = user_credentials.get_realm() > >- # Do the initial AS-REQ, should get a pre-authentication required >- # response >- etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) > cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, > names=[user_name]) >- sname = self.PrincipalName_create(name_type=NT_SRV_HST, >- names=["krbtgt", realm]) >- >- rep = self.as_req(cname, sname, realm, etype) >- self.check_pre_authentication(rep) > >- # Do the next AS-REQ >- padata = self.get_enc_timestamp_pa_data(user_credentials, rep) >- key = self.get_as_rep_key(user_credentials, rep) >- rep = self.as_req(cname, sname, realm, etype, padata=[padata]) >- self.check_as_reply(rep) >+ tgt = self.get_tgt(user_credentials) > > # Request a ticket to the host service on the machine account >- ticket = rep['ticket'] >- enc_part = self.get_as_rep_enc_data(key, rep) >- key = self.EncryptionKey_import(enc_part['key']) >- cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >- names=[user_name]) >- sname = self.PrincipalName_create(name_type=NT_SRV_HST, >- names=[service, mach_name]) >- >- (rep, enc_part) = self.tgs_req( >- cname, sname, realm, ticket, key, etype) >- self.check_tgs_reply(rep) >- key = self.EncryptionKey_import(enc_part['key']) >- >- # Check the contents of the pac, and the ticket >- ticket = rep['ticket'] >+ ticket = self.get_service_ticket(tgt, mach_credentials, >+ service=service, >+ target_name=target_name) > > # Write the ticket into a credentials cache file that can be ingested > # by the main credentials code. >- cachefile = self.create_ccache(cname, ticket, enc_part) >+ cachefile = self.create_ccache(cname, ticket.ticket, >+ ticket.encpart_private) > > # Create a credentials object to reference the credentials cache. > creds = Credentials() >diff --git a/python/samba/tests/krb5/test_ccache.py b/python/samba/tests/krb5/test_ccache.py >index 6a2b78398ac..040ae5cc9a1 100755 >--- a/python/samba/tests/krb5/test_ccache.py >+++ b/python/samba/tests/krb5/test_ccache.py >@@ -67,7 +67,7 @@ class CcacheTests(KDCBaseTest): > # ticket, to ensure that the krbtgt ticket doesn't also need to be > # stored. > (creds, cachefile) = self.create_ccache_with_user(user_credentials, >- mach_name) >+ mach_credentials) > > # Authenticate in-process to the machine account using the user's > # cached credentials. >diff --git a/python/samba/tests/krb5/test_ldap.py b/python/samba/tests/krb5/test_ldap.py >index 95b2d24221a..7d9ffebe298 100755 >--- a/python/samba/tests/krb5/test_ldap.py >+++ b/python/samba/tests/krb5/test_ldap.py >@@ -53,13 +53,16 @@ class LdapTests(KDCBaseTest): > # Create the user account. > (user_credentials, _) = self.create_account(samdb, user_name) > >+ mach_credentials = self.get_dc_creds() >+ > # Talk to the KDC to obtain the service ticket, which gets placed into > # the cache. The machine account name has to match the name in the > # ticket, to ensure that the krbtgt ticket doesn't also need to be > # stored. > (creds, cachefile) = self.create_ccache_with_user(user_credentials, >- mach_name, >- service) >+ mach_credentials, >+ service, >+ mach_name) > > # Authenticate in-process to the machine account using the user's > # cached credentials. >diff --git a/python/samba/tests/krb5/test_rpc.py b/python/samba/tests/krb5/test_rpc.py >index 40ac6df7a35..ef8dd4dcbf5 100755 >--- a/python/samba/tests/krb5/test_rpc.py >+++ b/python/samba/tests/krb5/test_rpc.py >@@ -50,13 +50,16 @@ class RpcTests(KDCBaseTest): > # Create the user account. > (user_credentials, _) = self.create_account(samdb, user_name) > >+ mach_credentials = self.get_dc_creds() >+ > # Talk to the KDC to obtain the service ticket, which gets placed into > # the cache. The machine account name has to match the name in the > # ticket, to ensure that the krbtgt ticket doesn't also need to be > # stored. > (creds, cachefile) = self.create_ccache_with_user(user_credentials, >- mach_name, >- service) >+ mach_credentials, >+ service, >+ mach_name) > > # Authenticate in-process to the machine account using the user's > # cached credentials. >diff --git a/python/samba/tests/krb5/test_smb.py b/python/samba/tests/krb5/test_smb.py >index eebc9a9d4fe..1e70ed322bf 100755 >--- a/python/samba/tests/krb5/test_smb.py >+++ b/python/samba/tests/krb5/test_smb.py >@@ -55,13 +55,16 @@ class SmbTests(KDCBaseTest): > # Create the user account. > (user_credentials, _) = self.create_account(samdb, user_name) > >+ mach_credentials = self.get_dc_creds() >+ > # Talk to the KDC to obtain the service ticket, which gets placed into > # the cache. The machine account name has to match the name in the > # ticket, to ensure that the krbtgt ticket doesn't also need to be > # stored. > (creds, cachefile) = self.create_ccache_with_user(user_credentials, >- mach_name, >- service) >+ mach_credentials, >+ service, >+ mach_name) > > # Set the Kerberos 5 credentials cache environment variable. This is > # required because the codepath that gets run (gse_krb5) looks for it >-- >2.25.1 > > >From 9f7155b3d89aacf732d09cb3fe6dfa6e76d80d05 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 22 Oct 2021 11:37:37 +1300 >Subject: [PATCH 037/190] CVE-2020-25719 CVE-2020-25717 tests/krb5: Allow > create_ccache_with_user() to return a ticket without a PAC > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14799 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14561 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 5 ++++- > 1 file changed, 4 insertions(+), 1 deletion(-) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index e77a940f411..aed4c427ab0 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -1672,7 +1672,7 @@ class KDCBaseTest(RawKerberosTest): > return cachefile > > def create_ccache_with_user(self, user_credentials, mach_credentials, >- service="host", target_name=None): >+ service="host", target_name=None, pac=True): > # Obtain a service ticket authorising the user and place it into a > # newly created credentials cache file. > >@@ -1689,6 +1689,9 @@ class KDCBaseTest(RawKerberosTest): > service=service, > target_name=target_name) > >+ if not pac: >+ ticket = self.modified_ticket(ticket, exclude_pac=True) >+ > # Write the ticket into a credentials cache file that can be ingested > # by the main credentials code. > cachefile = self.create_ccache(cname, ticket.ticket, >-- >2.25.1 > > >From a1e90086170525e7e04dc47209fc2e8b6d0714f8 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Thu, 21 Oct 2021 15:45:00 +1300 >Subject: [PATCH 038/190] CVE-2020-25722 tests/krb5: Add KDC tests for 3-part > SPNs > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14776 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 1 + > python/samba/tests/krb5/spn_tests.py | 212 +++++++++++++++++++++++ > python/samba/tests/usage.py | 1 + > selftest/knownfail_heimdal_kdc | 6 + > selftest/knownfail_mit_kdc | 6 + > source4/selftest/tests.py | 10 ++ > 6 files changed, 236 insertions(+) > create mode 100755 python/samba/tests/krb5/spn_tests.py > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index aed4c427ab0..cc23484ba2c 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -97,6 +97,7 @@ class KDCBaseTest(RawKerberosTest): > USER = auto() > COMPUTER = auto() > SERVER = auto() >+ RODC = auto() > > @classmethod > def setUpClass(cls): >diff --git a/python/samba/tests/krb5/spn_tests.py b/python/samba/tests/krb5/spn_tests.py >new file mode 100755 >index 00000000000..62d2ea081bc >--- /dev/null >+++ b/python/samba/tests/krb5/spn_tests.py >@@ -0,0 +1,212 @@ >+#!/usr/bin/env python3 >+# Unix SMB/CIFS implementation. >+# Copyright (C) Stefan Metzmacher 2020 >+# Copyright (C) 2020 Catalyst.Net Ltd >+# >+# 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 os >+import sys >+ >+from samba.tests import DynamicTestCase >+ >+import ldb >+ >+from samba.tests.krb5.kdc_base_test import KDCBaseTest >+from samba.tests.krb5.raw_testcase import KerberosCredentials >+from samba.tests.krb5.rfc4120_constants import ( >+ AES256_CTS_HMAC_SHA1_96, >+ ARCFOUR_HMAC_MD5, >+ KDC_ERR_S_PRINCIPAL_UNKNOWN, >+ NT_PRINCIPAL, >+) >+ >+sys.path.insert(0, "bin/python") >+os.environ["PYTHONUNBUFFERED"] = "1" >+ >+global_asn1_print = False >+global_hexdump = False >+ >+ >+@DynamicTestCase >+class SpnTests(KDCBaseTest): >+ test_account_types = { >+ 'computer': KDCBaseTest.AccountType.COMPUTER, >+ 'server': KDCBaseTest.AccountType.SERVER, >+ 'rodc': KDCBaseTest.AccountType.RODC >+ } >+ test_spns = { >+ '2_part': 'ldap/{{account}}', >+ '3_part_our_domain': 'ldap/{{account}}/{netbios_domain_name}', >+ '3_part_our_realm': 'ldap/{{account}}/{dns_domain_name}', >+ '3_part_not_our_realm': 'ldap/{{account}}/test', >+ '3_part_instance': 'ldap/{{account}}:test/{dns_domain_name}' >+ } >+ >+ @classmethod >+ def setUpClass(cls): >+ super().setUpClass() >+ >+ cls._mock_rodc_creds = None >+ >+ @classmethod >+ def setUpDynamicTestCases(cls): >+ for account_type_name, account_type in cls.test_account_types.items(): >+ for spn_name, spn in cls.test_spns.items(): >+ tname = f'{spn_name}_spn_{account_type_name}' >+ targs = (account_type, spn) >+ cls.generate_dynamic_test('test_spn', tname, *targs) >+ >+ def _test_spn_with_args(self, account_type, spn): >+ target_creds = self._get_creds(account_type) >+ spn = self._format_spn(spn, target_creds) >+ >+ sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=spn.split('/')) >+ >+ client_creds = self.get_client_creds() >+ tgt = self.get_tgt(client_creds) >+ >+ samdb = self.get_samdb() >+ netbios_domain_name = samdb.domain_netbios_name() >+ dns_domain_name = samdb.domain_dns_name() >+ >+ subkey = self.RandomKey(tgt.session_key.etype) >+ >+ etypes = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5,) >+ >+ if account_type is self.AccountType.SERVER: >+ ticket_etype = AES256_CTS_HMAC_SHA1_96 >+ else: >+ ticket_etype = None >+ decryption_key = self.TicketDecryptionKey_from_creds( >+ target_creds, etype=ticket_etype) >+ >+ if (spn.count('/') > 1 >+ and (spn.endswith(netbios_domain_name) >+ or spn.endswith(dns_domain_name)) >+ and account_type is not self.AccountType.SERVER >+ and account_type is not self.AccountType.RODC): >+ expected_error_mode = KDC_ERR_S_PRINCIPAL_UNKNOWN >+ check_error_fn = self.generic_check_kdc_error >+ check_rep_fn = None >+ else: >+ expected_error_mode = 0 >+ check_error_fn = None >+ check_rep_fn = self.generic_check_kdc_rep >+ >+ kdc_exchange_dict = self.tgs_exchange_dict( >+ expected_crealm=tgt.crealm, >+ expected_cname=tgt.cname, >+ expected_srealm=tgt.srealm, >+ expected_sname=sname, >+ ticket_decryption_key=decryption_key, >+ check_rep_fn=check_rep_fn, >+ check_error_fn=check_error_fn, >+ check_kdc_private_fn=self.generic_check_kdc_private, >+ expected_error_mode=expected_error_mode, >+ tgt=tgt, >+ authenticator_subkey=subkey, >+ kdc_options='0', >+ expect_edata=False) >+ >+ self._generic_kdc_exchange(kdc_exchange_dict, >+ cname=None, >+ realm=tgt.srealm, >+ sname=sname, >+ etypes=etypes) >+ >+ def setUp(self): >+ super().setUp() >+ self.do_asn1_print = global_asn1_print >+ self.do_hexdump = global_hexdump >+ >+ def _format_spns(self, spns, creds=None): >+ return map(lambda spn: self._format_spn(spn, creds), spns) >+ >+ def _format_spn(self, spn, creds=None): >+ samdb = self.get_samdb() >+ >+ spn = spn.format(netbios_domain_name=samdb.domain_netbios_name(), >+ dns_domain_name=samdb.domain_dns_name()) >+ >+ if creds is not None: >+ account_name = creds.get_username() >+ spn = spn.format(account=account_name) >+ >+ return spn >+ >+ def _get_creds(self, account_type): >+ spns = self._format_spns(self.test_spns.values()) >+ >+ if account_type is self.AccountType.RODC: >+ creds = self._mock_rodc_creds >+ if creds is None: >+ creds = self._get_mock_rodc_creds(spns) >+ type(self)._mock_rodc_creds = creds >+ else: >+ creds = self.get_cached_creds( >+ account_type=account_type, >+ opts={ >+ 'spn': spns >+ }) >+ >+ return creds >+ >+ def _get_mock_rodc_creds(self, spns): >+ rodc_ctx = self.get_mock_rodc_ctx() >+ >+ for spn in spns: >+ spn = spn.format(account=rodc_ctx.myname) >+ if spn not in rodc_ctx.SPNs: >+ rodc_ctx.SPNs.append(spn) >+ >+ samdb = self.get_samdb() >+ rodc_dn = ldb.Dn(samdb, rodc_ctx.acct_dn) >+ >+ msg = ldb.Message(rodc_dn) >+ msg['servicePrincipalName'] = ldb.MessageElement( >+ rodc_ctx.SPNs, >+ ldb.FLAG_MOD_REPLACE, >+ 'servicePrincipalName') >+ samdb.modify(msg) >+ >+ creds = KerberosCredentials() >+ creds.guess(self.get_lp()) >+ creds.set_realm(rodc_ctx.realm.upper()) >+ creds.set_domain(rodc_ctx.domain_name) >+ creds.set_password(rodc_ctx.acct_pass) >+ creds.set_username(rodc_ctx.myname) >+ creds.set_workstation(rodc_ctx.samname) >+ creds.set_dn(rodc_dn) >+ creds.set_spn(rodc_ctx.SPNs) >+ >+ res = samdb.search(base=rodc_dn, >+ scope=ldb.SCOPE_BASE, >+ attrs=['msDS-KeyVersionNumber']) >+ kvno = int(res[0].get('msDS-KeyVersionNumber', idx=0)) >+ creds.set_kvno(kvno) >+ >+ keys = self.get_keys(samdb, rodc_dn) >+ self.creds_set_keys(creds, keys) >+ >+ return creds >+ >+ >+if __name__ == "__main__": >+ global_asn1_print = False >+ global_hexdump = False >+ import unittest >+ unittest.main() >diff --git a/python/samba/tests/usage.py b/python/samba/tests/usage.py >index 570391d67ac..a7fb4631d0b 100644 >--- a/python/samba/tests/usage.py >+++ b/python/samba/tests/usage.py >@@ -105,6 +105,7 @@ EXCLUDE_USAGE = { > 'python/samba/tests/krb5/fast_tests.py', > 'python/samba/tests/krb5/rodc_tests.py', > 'python/samba/tests/krb5/salt_tests.py', >+ 'python/samba/tests/krb5/spn_tests.py', > } > > EXCLUDE_HELP = { >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 5e0ee77ff6a..3cd8cfde948 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -93,3 +93,9 @@ > ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_auth_data_required > ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_no_auth_data_required_a > ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_no_auth_data_required_b >+# >+# SPN tests >+# >+^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_instance_spn_computer >+^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_our_domain_spn_computer >+^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_our_realm_spn_computer >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index 7aa95cbb1c7..1576e7244ac 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -352,3 +352,9 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2003dc > ^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2008dc > ^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2008r2dc >+# >+# SPN tests >+# >+^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_instance_spn_computer >+^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_our_domain_spn_computer >+^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_our_realm_spn_computer >diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py >index 21894812422..f74e87345ee 100755 >--- a/source4/selftest/tests.py >+++ b/source4/selftest/tests.py >@@ -1620,6 +1620,16 @@ planpythontestsuite( > 'FAST_SUPPORT': have_fast_support, > 'TKT_SIG_SUPPORT': tkt_sig_support > }) >+planpythontestsuite( >+ "ad_dc", >+ "samba.tests.krb5.spn_tests", >+ environ={ >+ 'ADMIN_USERNAME': '$USERNAME', >+ 'ADMIN_PASSWORD': '$PASSWORD', >+ 'STRICT_CHECKING': '0', >+ 'FAST_SUPPORT': have_fast_support, >+ 'TKT_SIG_SUPPORT': tkt_sig_support >+ }) > > for env in [ > 'vampire_dc', >-- >2.25.1 > > >From 8970b346a0515d67bb00b4f89235d4a1b7ec45b1 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Wed, 13 Oct 2021 16:07:09 +1300 >Subject: [PATCH 039/190] CVE-2020-25721 ndrdump: Add tests for PAC with > UPN_DNS_INFO > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14835 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/blackbox/ndrdump.py | 35 +++ > .../tests/krb5pac_upn_dns_info_ex.b64.txt | 1 + > .../librpc/tests/krb5pac_upn_dns_info_ex.txt | 220 ++++++++++++++++++ > ...5pac_upn_dns_info_ex_not_supported.b64.txt | 1 + > .../krb5pac_upn_dns_info_ex_not_supported.txt | 213 +++++++++++++++++ > 5 files changed, 470 insertions(+) > create mode 100644 source4/librpc/tests/krb5pac_upn_dns_info_ex.b64.txt > create mode 100644 source4/librpc/tests/krb5pac_upn_dns_info_ex.txt > create mode 100644 source4/librpc/tests/krb5pac_upn_dns_info_ex_not_supported.b64.txt > create mode 100644 source4/librpc/tests/krb5pac_upn_dns_info_ex_not_supported.txt > >diff --git a/python/samba/tests/blackbox/ndrdump.py b/python/samba/tests/blackbox/ndrdump.py >index b6c61d7e7a4..ace56d3edf6 100644 >--- a/python/samba/tests/blackbox/ndrdump.py >+++ b/python/samba/tests/blackbox/ndrdump.py >@@ -89,6 +89,41 @@ class NdrDumpTests(BlackboxTestCase): > expected.encode('utf-8')) > self.assertTrue(actual.endswith(b"dump OK\n")) > >+ def test_ndrdump_upn_dns_info_ex(self): >+ with open(self.data_path( >+ 'krb5pac_upn_dns_info_ex.txt')) as f: >+ expected = f.read() >+ data_path = self.data_path( >+ 'krb5pac_upn_dns_info_ex.b64.txt') >+ >+ try: >+ actual = self.check_output( >+ 'ndrdump --debug-stdout -d0 krb5pac PAC_DATA struct ' >+ '--validate --base64-input ' + data_path) >+ except BlackboxProcessError as e: >+ self.fail(e) >+ >+ self.assertEqual(actual, expected.encode('utf-8')) >+ >+ def test_ndrdump_upn_dns_info_ex_not_supported(self): >+ with open(self.data_path( >+ 'krb5pac_upn_dns_info_ex_not_supported.txt')) as f: >+ expected = f.read() >+ data_path = self.data_path( >+ 'krb5pac_upn_dns_info_ex_not_supported.b64.txt') >+ >+ try: >+ # This PAC has been edited to remove the >+ # PAC_UPN_DNS_FLAG_HAS_SAM_NAME_AND_SID bit, so that we can >+ # simulate older versions of Samba parsing this structure. >+ actual = self.check_output( >+ 'ndrdump --debug-stdout -d0 krb5pac PAC_DATA struct ' >+ '--validate --base64-input ' + data_path) >+ except BlackboxProcessError as e: >+ self.fail(e) >+ >+ self.assertEqual(actual, expected.encode('utf-8')) >+ > def test_ndrdump_with_binary_struct_number(self): > expected = '''pull returned Success > GUID : 33323130-3534-3736-3839-616263646566 >diff --git a/source4/librpc/tests/krb5pac_upn_dns_info_ex.b64.txt b/source4/librpc/tests/krb5pac_upn_dns_info_ex.b64.txt >new file mode 100644 >index 00000000000..02b570624bc >--- /dev/null >+++ b/source4/librpc/tests/krb5pac_upn_dns_info_ex.b64.txt >@@ -0,0 +1 @@ >+BgAAAAAAAAABAAAA0AEAAGgAAAAAAAAACgAAABwAAAA4AgAAAAAAAAwAAACoAAAAWAIAAAAAAAAGAAAAFAAAAAADAAAAAAAABwAAABAAAAAYAwAAAAAAABAAAAAQAAAAKAMAAAAAAAABEAgAzMzMzMABAAAAAAAAAAACAAAAAAAAAAAA/////////3//////////f7pMcCzXv9cBugzaVqDA1wG6zMkh2ODXARIAEgAEAAIAAAAAAAgAAgAAAAAADAACAAAAAAAQAAIAAAAAABQAAgAAAAAAGAACAAAAAACOBAAAAQIAAAEAAAAcAAIAIAAAAAAAAAAAAAAAAAAAAAAAAAAOABAAIAACABYAGAAkAAIAKAACAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAALAACAAAAAAAAAAAAAAAAAAkAAAAAAAAACQAAAHQAcwB0AHQAawB0AHUAcwByAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAECAAAHAAAACAAAAAAAAAAHAAAATABPAEMAQQBMAEQAQwAAAAwAAAAAAAAACwAAAFMAQQBNAEIAQQBEAE8ATQBBAEkATgAAAAQAAAABBAAAAAAABRUAAAC2fvX0wDGiOufKt1QBAAAAMAACAAcAAAABAAAAAQEAAAAAABIBAAAAAAAAAIC3ISzXv9cBEgB0AHMAdAB0AGsAdAB1AHMAcgAAAAAANgAYACIAUAADAAAAEgB4ABwAigAAAAAAdABzAHQAdABrAHQAdQBzAHIAQABzAGEAbQBiAGEALgBlAHgAYQBtAHAAbABlAC4AYwBvAG0AAABTAEEATQBCAEEALgBFAFgAQQBNAFAATABFAC4AQwBPAE0AAAAAAAAAdABzAHQAdABrAHQAdQBzAHIAAQUAAAAAAAUVAAAAtn719MAxojrnyrdUjgQAAAAAdv///ys5aox2KdqNY8CVVxkQbs4AAAAAEAAAAFrUeP0b8Pbct0VlVhAAAAB4SC+IGKoLP+0030o= >diff --git a/source4/librpc/tests/krb5pac_upn_dns_info_ex.txt b/source4/librpc/tests/krb5pac_upn_dns_info_ex.txt >new file mode 100644 >index 00000000000..9747d1b6d3a >--- /dev/null >+++ b/source4/librpc/tests/krb5pac_upn_dns_info_ex.txt >@@ -0,0 +1,220 @@ >+pull returned Success >+ PAC_DATA: struct PAC_DATA >+ num_buffers : 0x00000006 (6) >+ version : 0x00000000 (0) >+ buffers: ARRAY(6) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_LOGON_INFO (1) >+ _ndr_size : 0x000001d0 (464) >+ info : * >+ info : union PAC_INFO(case 1) >+ logon_info: struct PAC_LOGON_INFO_CTR >+ info : * >+ info: struct PAC_LOGON_INFO >+ info3: struct netr_SamInfo3 >+ base: struct netr_SamBaseInfo >+ logon_time : NTTIME(0) >+ logoff_time : Thu Sep 14 02:48:05 AM 30828 UTC >+ kickoff_time : Thu Sep 14 02:48:05 AM 30828 UTC >+ last_password_change : Wed Oct 13 02:08:12 AM 2021 UTC >+ allow_password_change : Thu Oct 14 02:08:12 AM 2021 UTC >+ force_password_change : Wed Nov 24 02:08:12 AM 2021 UTC >+ account_name: struct lsa_String >+ length : 0x0012 (18) >+ size : 0x0012 (18) >+ string : * >+ string : 'tsttktusr' >+ full_name: struct lsa_String >+ length : 0x0000 (0) >+ size : 0x0000 (0) >+ string : * >+ string : '' >+ logon_script: struct lsa_String >+ length : 0x0000 (0) >+ size : 0x0000 (0) >+ string : * >+ string : '' >+ profile_path: struct lsa_String >+ length : 0x0000 (0) >+ size : 0x0000 (0) >+ string : * >+ string : '' >+ home_directory: struct lsa_String >+ length : 0x0000 (0) >+ size : 0x0000 (0) >+ string : * >+ string : '' >+ home_drive: struct lsa_String >+ length : 0x0000 (0) >+ size : 0x0000 (0) >+ string : * >+ string : '' >+ logon_count : 0x0000 (0) >+ bad_password_count : 0x0000 (0) >+ rid : 0x0000048e (1166) >+ primary_gid : 0x00000201 (513) >+ groups: struct samr_RidWithAttributeArray >+ count : 0x00000001 (1) >+ rids : * >+ rids: ARRAY(1) >+ rids: struct samr_RidWithAttribute >+ rid : 0x00000201 (513) >+ attributes : 0x00000007 (7) >+ 1: SE_GROUP_MANDATORY >+ 1: SE_GROUP_ENABLED_BY_DEFAULT >+ 1: SE_GROUP_ENABLED >+ 0: SE_GROUP_OWNER >+ 0: SE_GROUP_USE_FOR_DENY_ONLY >+ 0: SE_GROUP_INTEGRITY >+ 0: SE_GROUP_INTEGRITY_ENABLED >+ 0: SE_GROUP_RESOURCE >+ 0x00: SE_GROUP_LOGON_ID (0) >+ user_flags : 0x00000020 (32) >+ 0: NETLOGON_GUEST >+ 0: NETLOGON_NOENCRYPTION >+ 0: NETLOGON_CACHED_ACCOUNT >+ 0: NETLOGON_USED_LM_PASSWORD >+ 1: NETLOGON_EXTRA_SIDS >+ 0: NETLOGON_SUBAUTH_SESSION_KEY >+ 0: NETLOGON_SERVER_TRUST_ACCOUNT >+ 0: NETLOGON_NTLMV2_ENABLED >+ 0: NETLOGON_RESOURCE_GROUPS >+ 0: NETLOGON_PROFILE_PATH_RETURNED >+ 0: NETLOGON_GRACE_LOGON >+ key: struct netr_UserSessionKey >+ key: ARRAY(16): <REDACTED SECRET VALUES> >+ logon_server: struct lsa_StringLarge >+ length : 0x000e (14) >+ size : 0x0010 (16) >+ string : * >+ string : 'LOCALDC' >+ logon_domain: struct lsa_StringLarge >+ length : 0x0016 (22) >+ size : 0x0018 (24) >+ string : * >+ string : 'SAMBADOMAIN' >+ domain_sid : * >+ domain_sid : S-1-5-21-4109729462-983708096-1421331175 >+ LMSessKey: struct netr_LMSessionKey >+ key: ARRAY(8): <REDACTED SECRET VALUES> >+ acct_flags : 0x00000010 (16) >+ 0: ACB_DISABLED >+ 0: ACB_HOMDIRREQ >+ 0: ACB_PWNOTREQ >+ 0: ACB_TEMPDUP >+ 1: ACB_NORMAL >+ 0: ACB_MNS >+ 0: ACB_DOMTRUST >+ 0: ACB_WSTRUST >+ 0: ACB_SVRTRUST >+ 0: ACB_PWNOEXP >+ 0: ACB_AUTOLOCK >+ 0: ACB_ENC_TXT_PWD_ALLOWED >+ 0: ACB_SMARTCARD_REQUIRED >+ 0: ACB_TRUSTED_FOR_DELEGATION >+ 0: ACB_NOT_DELEGATED >+ 0: ACB_USE_DES_KEY_ONLY >+ 0: ACB_DONT_REQUIRE_PREAUTH >+ 0: ACB_PW_EXPIRED >+ 0: ACB_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION >+ 0: ACB_NO_AUTH_DATA_REQD >+ 0: ACB_PARTIAL_SECRETS_ACCOUNT >+ 0: ACB_USE_AES_KEYS >+ sub_auth_status : 0x00000000 (0) >+ last_successful_logon : NTTIME(0) >+ last_failed_logon : NTTIME(0) >+ failed_logon_count : 0x00000000 (0) >+ reserved : 0x00000000 (0) >+ sidcount : 0x00000001 (1) >+ sids : * >+ sids: ARRAY(1) >+ sids: struct netr_SidAttr >+ sid : * >+ sid : S-1-18-1 >+ attributes : 0x00000007 (7) >+ 1: SE_GROUP_MANDATORY >+ 1: SE_GROUP_ENABLED_BY_DEFAULT >+ 1: SE_GROUP_ENABLED >+ 0: SE_GROUP_OWNER >+ 0: SE_GROUP_USE_FOR_DENY_ONLY >+ 0: SE_GROUP_INTEGRITY >+ 0: SE_GROUP_INTEGRITY_ENABLED >+ 0: SE_GROUP_RESOURCE >+ 0x00: SE_GROUP_LOGON_ID (0) >+ resource_groups: struct PAC_DOMAIN_GROUP_MEMBERSHIP >+ domain_sid : NULL >+ groups: struct samr_RidWithAttributeArray >+ count : 0x00000000 (0) >+ rids : NULL >+ _pad : 0x00000000 (0) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_LOGON_NAME (10) >+ _ndr_size : 0x0000001c (28) >+ info : * >+ info : union PAC_INFO(case 10) >+ logon_name: struct PAC_LOGON_NAME >+ logon_time : Wed Oct 13 02:08:11 AM 2021 UTC >+ size : 0x0012 (18) >+ account_name : 'tsttktusr' >+ _pad : 0x00000000 (0) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_UPN_DNS_INFO (12) >+ _ndr_size : 0x000000a8 (168) >+ info : * >+ info : union PAC_INFO(case 12) >+ upn_dns_info: struct PAC_UPN_DNS_INFO >+ upn_name_size : 0x0036 (54) >+ upn_name : * >+ upn_name : 'tsttktusr@samba.example.com' >+ dns_domain_name_size : 0x0022 (34) >+ dns_domain_name : * >+ dns_domain_name : 'SAMBA.EXAMPLE.COM' >+ flags : 0x00000003 (3) >+ 1: PAC_UPN_DNS_FLAG_CONSTRUCTED >+ 1: PAC_UPN_DNS_FLAG_HAS_SAM_NAME_AND_SID >+ ex : union PAC_UPN_DNS_INFO_EX(case 2) >+ sam_name_and_sid: struct PAC_UPN_DNS_INFO_SAM_NAME_AND_SID >+ samaccountname_size : 0x0012 (18) >+ samaccountname : * >+ samaccountname : 'tsttktusr' >+ objectsid_size : 0x001c (28) >+ objectsid : * >+ objectsid : S-1-5-21-4109729462-983708096-1421331175-1166 >+ _pad : 0x00000000 (0) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_SRV_CHECKSUM (6) >+ _ndr_size : 0x00000014 (20) >+ info : * >+ info : union PAC_INFO(case 6) >+ srv_cksum: struct PAC_SIGNATURE_DATA >+ type : 0xffffff76 (4294967158) >+ signature : DATA_BLOB length=16 >+[0000] 2B 39 6A 8C 76 29 DA 8D 63 C0 95 57 19 10 6E CE +9j.v).. c..W..n. >+ _pad : 0x00000000 (0) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_KDC_CHECKSUM (7) >+ _ndr_size : 0x00000010 (16) >+ info : * >+ info : union PAC_INFO(case 7) >+ kdc_cksum: struct PAC_SIGNATURE_DATA >+ type : 0x00000010 (16) >+ signature : DATA_BLOB length=12 >+[0000] 5A D4 78 FD 1B F0 F6 DC B7 45 65 56 Z.x..... .EeV >+ _pad : 0x00000000 (0) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_TICKET_CHECKSUM (16) >+ _ndr_size : 0x00000010 (16) >+ info : * >+ info : union PAC_INFO(case 16) >+ ticket_checksum: struct PAC_SIGNATURE_DATA >+ type : 0x00000010 (16) >+ signature : DATA_BLOB length=12 >+[0000] 78 48 2F 88 18 AA 0B 3F ED 34 DF 4A xH/....? .4.J >+ _pad : 0x00000000 (0) >+push returned Success >+pull returned Success >+WARNING! orig bytes:824 validated pushed bytes:832 >+WARNING! orig pulled bytes:824 validated pulled bytes:832 >+WARNING! orig and validated differ at byte 0x2C (44) >+WARNING! orig byte[0x2C] = 0xA8 validated byte[0x2C] = 0xB0 >+dump OK >diff --git a/source4/librpc/tests/krb5pac_upn_dns_info_ex_not_supported.b64.txt b/source4/librpc/tests/krb5pac_upn_dns_info_ex_not_supported.b64.txt >new file mode 100644 >index 00000000000..cd99b9d0b0a >--- /dev/null >+++ b/source4/librpc/tests/krb5pac_upn_dns_info_ex_not_supported.b64.txt >@@ -0,0 +1 @@ >+BgAAAAAAAAABAAAA0AEAAGgAAAAAAAAACgAAABwAAAA4AgAAAAAAAAwAAACoAAAAWAIAAAAAAAAGAAAAFAAAAAADAAAAAAAABwAAABAAAAAYAwAAAAAAABAAAAAQAAAAKAMAAAAAAAABEAgAzMzMzMABAAAAAAAAAAACAAAAAAAAAAAA/////////3//////////f7pMcCzXv9cBugzaVqDA1wG6zMkh2ODXARIAEgAEAAIAAAAAAAgAAgAAAAAADAACAAAAAAAQAAIAAAAAABQAAgAAAAAAGAACAAAAAACOBAAAAQIAAAEAAAAcAAIAIAAAAAAAAAAAAAAAAAAAAAAAAAAOABAAIAACABYAGAAkAAIAKAACAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAALAACAAAAAAAAAAAAAAAAAAkAAAAAAAAACQAAAHQAcwB0AHQAawB0AHUAcwByAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAECAAAHAAAACAAAAAAAAAAHAAAATABPAEMAQQBMAEQAQwAAAAwAAAAAAAAACwAAAFMAQQBNAEIAQQBEAE8ATQBBAEkATgAAAAQAAAABBAAAAAAABRUAAAC2fvX0wDGiOufKt1QBAAAAMAACAAcAAAABAAAAAQEAAAAAABIBAAAAAAAAAIC3ISzXv9cBEgB0AHMAdAB0AGsAdAB1AHMAcgAAAAAANgAYACIAUAABAAAAEgB4ABwAigAAAAAAdABzAHQAdABrAHQAdQBzAHIAQABzAGEAbQBiAGEALgBlAHgAYQBtAHAAbABlAC4AYwBvAG0AAABTAEEATQBCAEEALgBFAFgAQQBNAFAATABFAC4AQwBPAE0AAAAAAAAAdABzAHQAdABrAHQAdQBzAHIAAQUAAAAAAAUVAAAAtn719MAxojrnyrdUjgQAAAAAdv///ys5aox2KdqNY8CVVxkQbs4AAAAAEAAAAFrUeP0b8Pbct0VlVhAAAAB4SC+IGKoLP+0030o= >diff --git a/source4/librpc/tests/krb5pac_upn_dns_info_ex_not_supported.txt b/source4/librpc/tests/krb5pac_upn_dns_info_ex_not_supported.txt >new file mode 100644 >index 00000000000..d29832ede49 >--- /dev/null >+++ b/source4/librpc/tests/krb5pac_upn_dns_info_ex_not_supported.txt >@@ -0,0 +1,213 @@ >+pull returned Success >+ PAC_DATA: struct PAC_DATA >+ num_buffers : 0x00000006 (6) >+ version : 0x00000000 (0) >+ buffers: ARRAY(6) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_LOGON_INFO (1) >+ _ndr_size : 0x000001d0 (464) >+ info : * >+ info : union PAC_INFO(case 1) >+ logon_info: struct PAC_LOGON_INFO_CTR >+ info : * >+ info: struct PAC_LOGON_INFO >+ info3: struct netr_SamInfo3 >+ base: struct netr_SamBaseInfo >+ logon_time : NTTIME(0) >+ logoff_time : Thu Sep 14 02:48:05 AM 30828 UTC >+ kickoff_time : Thu Sep 14 02:48:05 AM 30828 UTC >+ last_password_change : Wed Oct 13 02:08:12 AM 2021 UTC >+ allow_password_change : Thu Oct 14 02:08:12 AM 2021 UTC >+ force_password_change : Wed Nov 24 02:08:12 AM 2021 UTC >+ account_name: struct lsa_String >+ length : 0x0012 (18) >+ size : 0x0012 (18) >+ string : * >+ string : 'tsttktusr' >+ full_name: struct lsa_String >+ length : 0x0000 (0) >+ size : 0x0000 (0) >+ string : * >+ string : '' >+ logon_script: struct lsa_String >+ length : 0x0000 (0) >+ size : 0x0000 (0) >+ string : * >+ string : '' >+ profile_path: struct lsa_String >+ length : 0x0000 (0) >+ size : 0x0000 (0) >+ string : * >+ string : '' >+ home_directory: struct lsa_String >+ length : 0x0000 (0) >+ size : 0x0000 (0) >+ string : * >+ string : '' >+ home_drive: struct lsa_String >+ length : 0x0000 (0) >+ size : 0x0000 (0) >+ string : * >+ string : '' >+ logon_count : 0x0000 (0) >+ bad_password_count : 0x0000 (0) >+ rid : 0x0000048e (1166) >+ primary_gid : 0x00000201 (513) >+ groups: struct samr_RidWithAttributeArray >+ count : 0x00000001 (1) >+ rids : * >+ rids: ARRAY(1) >+ rids: struct samr_RidWithAttribute >+ rid : 0x00000201 (513) >+ attributes : 0x00000007 (7) >+ 1: SE_GROUP_MANDATORY >+ 1: SE_GROUP_ENABLED_BY_DEFAULT >+ 1: SE_GROUP_ENABLED >+ 0: SE_GROUP_OWNER >+ 0: SE_GROUP_USE_FOR_DENY_ONLY >+ 0: SE_GROUP_INTEGRITY >+ 0: SE_GROUP_INTEGRITY_ENABLED >+ 0: SE_GROUP_RESOURCE >+ 0x00: SE_GROUP_LOGON_ID (0) >+ user_flags : 0x00000020 (32) >+ 0: NETLOGON_GUEST >+ 0: NETLOGON_NOENCRYPTION >+ 0: NETLOGON_CACHED_ACCOUNT >+ 0: NETLOGON_USED_LM_PASSWORD >+ 1: NETLOGON_EXTRA_SIDS >+ 0: NETLOGON_SUBAUTH_SESSION_KEY >+ 0: NETLOGON_SERVER_TRUST_ACCOUNT >+ 0: NETLOGON_NTLMV2_ENABLED >+ 0: NETLOGON_RESOURCE_GROUPS >+ 0: NETLOGON_PROFILE_PATH_RETURNED >+ 0: NETLOGON_GRACE_LOGON >+ key: struct netr_UserSessionKey >+ key: ARRAY(16): <REDACTED SECRET VALUES> >+ logon_server: struct lsa_StringLarge >+ length : 0x000e (14) >+ size : 0x0010 (16) >+ string : * >+ string : 'LOCALDC' >+ logon_domain: struct lsa_StringLarge >+ length : 0x0016 (22) >+ size : 0x0018 (24) >+ string : * >+ string : 'SAMBADOMAIN' >+ domain_sid : * >+ domain_sid : S-1-5-21-4109729462-983708096-1421331175 >+ LMSessKey: struct netr_LMSessionKey >+ key: ARRAY(8): <REDACTED SECRET VALUES> >+ acct_flags : 0x00000010 (16) >+ 0: ACB_DISABLED >+ 0: ACB_HOMDIRREQ >+ 0: ACB_PWNOTREQ >+ 0: ACB_TEMPDUP >+ 1: ACB_NORMAL >+ 0: ACB_MNS >+ 0: ACB_DOMTRUST >+ 0: ACB_WSTRUST >+ 0: ACB_SVRTRUST >+ 0: ACB_PWNOEXP >+ 0: ACB_AUTOLOCK >+ 0: ACB_ENC_TXT_PWD_ALLOWED >+ 0: ACB_SMARTCARD_REQUIRED >+ 0: ACB_TRUSTED_FOR_DELEGATION >+ 0: ACB_NOT_DELEGATED >+ 0: ACB_USE_DES_KEY_ONLY >+ 0: ACB_DONT_REQUIRE_PREAUTH >+ 0: ACB_PW_EXPIRED >+ 0: ACB_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION >+ 0: ACB_NO_AUTH_DATA_REQD >+ 0: ACB_PARTIAL_SECRETS_ACCOUNT >+ 0: ACB_USE_AES_KEYS >+ sub_auth_status : 0x00000000 (0) >+ last_successful_logon : NTTIME(0) >+ last_failed_logon : NTTIME(0) >+ failed_logon_count : 0x00000000 (0) >+ reserved : 0x00000000 (0) >+ sidcount : 0x00000001 (1) >+ sids : * >+ sids: ARRAY(1) >+ sids: struct netr_SidAttr >+ sid : * >+ sid : S-1-18-1 >+ attributes : 0x00000007 (7) >+ 1: SE_GROUP_MANDATORY >+ 1: SE_GROUP_ENABLED_BY_DEFAULT >+ 1: SE_GROUP_ENABLED >+ 0: SE_GROUP_OWNER >+ 0: SE_GROUP_USE_FOR_DENY_ONLY >+ 0: SE_GROUP_INTEGRITY >+ 0: SE_GROUP_INTEGRITY_ENABLED >+ 0: SE_GROUP_RESOURCE >+ 0x00: SE_GROUP_LOGON_ID (0) >+ resource_groups: struct PAC_DOMAIN_GROUP_MEMBERSHIP >+ domain_sid : NULL >+ groups: struct samr_RidWithAttributeArray >+ count : 0x00000000 (0) >+ rids : NULL >+ _pad : 0x00000000 (0) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_LOGON_NAME (10) >+ _ndr_size : 0x0000001c (28) >+ info : * >+ info : union PAC_INFO(case 10) >+ logon_name: struct PAC_LOGON_NAME >+ logon_time : Wed Oct 13 02:08:11 AM 2021 UTC >+ size : 0x0012 (18) >+ account_name : 'tsttktusr' >+ _pad : 0x00000000 (0) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_UPN_DNS_INFO (12) >+ _ndr_size : 0x000000a8 (168) >+ info : * >+ info : union PAC_INFO(case 12) >+ upn_dns_info: struct PAC_UPN_DNS_INFO >+ upn_name_size : 0x0036 (54) >+ upn_name : * >+ upn_name : 'tsttktusr@samba.example.com' >+ dns_domain_name_size : 0x0022 (34) >+ dns_domain_name : * >+ dns_domain_name : 'SAMBA.EXAMPLE.COM' >+ flags : 0x00000001 (1) >+ 1: PAC_UPN_DNS_FLAG_CONSTRUCTED >+ 0: PAC_UPN_DNS_FLAG_HAS_SAM_NAME_AND_SID >+ ex : union PAC_UPN_DNS_INFO_EX(case 0) >+ _pad : 0x00000000 (0) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_SRV_CHECKSUM (6) >+ _ndr_size : 0x00000014 (20) >+ info : * >+ info : union PAC_INFO(case 6) >+ srv_cksum: struct PAC_SIGNATURE_DATA >+ type : 0xffffff76 (4294967158) >+ signature : DATA_BLOB length=16 >+[0000] 2B 39 6A 8C 76 29 DA 8D 63 C0 95 57 19 10 6E CE +9j.v).. c..W..n. >+ _pad : 0x00000000 (0) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_KDC_CHECKSUM (7) >+ _ndr_size : 0x00000010 (16) >+ info : * >+ info : union PAC_INFO(case 7) >+ kdc_cksum: struct PAC_SIGNATURE_DATA >+ type : 0x00000010 (16) >+ signature : DATA_BLOB length=12 >+[0000] 5A D4 78 FD 1B F0 F6 DC B7 45 65 56 Z.x..... .EeV >+ _pad : 0x00000000 (0) >+ buffers: struct PAC_BUFFER >+ type : PAC_TYPE_TICKET_CHECKSUM (16) >+ _ndr_size : 0x00000010 (16) >+ info : * >+ info : union PAC_INFO(case 16) >+ ticket_checksum: struct PAC_SIGNATURE_DATA >+ type : 0x00000010 (16) >+ signature : DATA_BLOB length=12 >+[0000] 78 48 2F 88 18 AA 0B 3F ED 34 DF 4A xH/....? .4.J >+ _pad : 0x00000000 (0) >+push returned Success >+pull returned Success >+WARNING! orig bytes:824 validated pushed bytes:768 >+WARNING! orig pulled bytes:824 validated pulled bytes:768 >+WARNING! orig and validated differ at byte 0x2C (44) >+WARNING! orig byte[0x2C] = 0xA8 validated byte[0x2C] = 0x70 >+dump OK >-- >2.25.1 > > >From 344022a9677967310c008dc9a7c939fc8c93f003 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Mon, 18 Oct 2021 15:02:39 +1300 >Subject: [PATCH 040/190] CVE-2020-25719 tests/krb5: Add tests for requiring > and issuing a PAC > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14561 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 124 ++++++++++++++++++++--- > selftest/knownfail_heimdal_kdc | 9 ++ > selftest/knownfail_mit_kdc | 6 ++ > 3 files changed, 123 insertions(+), 16 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index f36704f998c..fbeb5fe61fb 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -31,6 +31,7 @@ from samba.tests.krb5.rfc4120_constants import ( > KRB_ERROR, > KRB_TGS_REP, > KDC_ERR_BADMATCH, >+ KDC_ERR_BADOPTION, > NT_PRINCIPAL, > NT_SRV_INST, > ) >@@ -214,7 +215,8 @@ class KdcTgsTests(KDCBaseTest): > "rep = {%s},%s" % (rep, pac_data)) > > def _make_tgs_request(self, client_creds, service_creds, tgt, >- expect_pac=True): >+ pac_request=None, expect_pac=True, >+ expect_error=False): > client_account = client_creds.get_username() > cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, > names=[client_account]) >@@ -241,6 +243,15 @@ class KdcTgsTests(KDCBaseTest): > > authenticator_subkey = self.RandomKey(kcrypto.Enctype.AES256) > >+ if expect_error: >+ expected_error_mode = KDC_ERR_BADOPTION >+ check_error_fn = self.generic_check_kdc_error >+ check_rep_fn = None >+ else: >+ expected_error_mode = 0 >+ check_error_fn = None >+ check_rep_fn = self.generic_check_kdc_rep >+ > kdc_exchange_dict = self.tgs_exchange_dict( > expected_crealm=expected_crealm, > expected_cname=expected_cname, >@@ -248,12 +259,14 @@ class KdcTgsTests(KDCBaseTest): > expected_sname=expected_sname, > expected_supported_etypes=expected_supported_etypes, > ticket_decryption_key=target_decryption_key, >- check_rep_fn=self.generic_check_kdc_rep, >+ check_error_fn=check_error_fn, >+ check_rep_fn=check_rep_fn, > check_kdc_private_fn=self.generic_check_kdc_private, >- expected_error_mode=0, >+ expected_error_mode=expected_error_mode, > tgt=tgt, > authenticator_subkey=authenticator_subkey, > kdc_options=kdc_options, >+ pac_request=pac_request, > expect_pac=expect_pac) > > rep = self._generic_kdc_exchange(kdc_exchange_dict, >@@ -261,25 +274,43 @@ class KdcTgsTests(KDCBaseTest): > realm=realm, > sname=sname, > etypes=etypes) >- self.check_reply(rep, KRB_TGS_REP) >+ if expect_error: >+ self.check_error_rep(rep, expected_error_mode) >+ >+ return None >+ else: >+ self.check_reply(rep, KRB_TGS_REP) >+ >+ return kdc_exchange_dict['rep_ticket_creds'] >+ >+ def test_request(self): >+ client_creds = self.get_client_creds() >+ service_creds = self.get_service_creds() >+ >+ tgt = self.get_tgt(client_creds) >+ >+ pac = self.get_ticket_pac(tgt) >+ self.assertIsNotNone(pac) >+ >+ ticket = self._make_tgs_request(client_creds, service_creds, tgt) > >- return kdc_exchange_dict['rep_ticket_creds'] >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) > > def test_request_no_pac(self): > client_creds = self.get_client_creds() > service_creds = self.get_service_creds() > >- tgt = self.get_tgt(client_creds, pac_request=False, >- expect_pac=False) >+ tgt = self.get_tgt(client_creds, pac_request=False) > >- pac = self.get_ticket_pac(tgt, expect_pac=False) >- self.assertIsNone(pac) >+ pac = self.get_ticket_pac(tgt) >+ self.assertIsNotNone(pac) > > ticket = self._make_tgs_request(client_creds, service_creds, tgt, >- expect_pac=False) >+ pac_request=False) > >- pac = self.get_ticket_pac(ticket, expect_pac=False) >- self.assertIsNone(pac) >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) > > def test_client_no_auth_data_required(self): > client_creds = self.get_cached_creds( >@@ -297,6 +328,23 @@ class KdcTgsTests(KDCBaseTest): > pac = self.get_ticket_pac(ticket) > self.assertIsNotNone(pac) > >+ def test_no_pac_client_no_auth_data_required(self): >+ client_creds = self.get_cached_creds( >+ account_type=self.AccountType.USER, >+ opts={'no_auth_data_required': True}) >+ service_creds = self.get_service_creds() >+ >+ tgt = self.get_tgt(client_creds, pac_request=False) >+ >+ pac = self.get_ticket_pac(tgt) >+ self.assertIsNotNone(pac) >+ >+ ticket = self._make_tgs_request(client_creds, service_creds, tgt, >+ pac_request=False) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ > def test_service_no_auth_data_required(self): > client_creds = self.get_client_creds() > service_creds = self.get_cached_creds( >@@ -314,8 +362,42 @@ class KdcTgsTests(KDCBaseTest): > pac = self.get_ticket_pac(ticket, expect_pac=False) > self.assertIsNone(pac) > >- def test_remove_pac(self): >+ def test_no_pac_service_no_auth_data_required(self): > client_creds = self.get_client_creds() >+ service_creds = self.get_cached_creds( >+ account_type=self.AccountType.COMPUTER, >+ opts={'no_auth_data_required': True}) >+ >+ tgt = self.get_tgt(client_creds, pac_request=False) >+ >+ pac = self.get_ticket_pac(tgt) >+ self.assertIsNotNone(pac) >+ >+ ticket = self._make_tgs_request(client_creds, service_creds, tgt, >+ pac_request=False, expect_pac=False) >+ >+ pac = self.get_ticket_pac(ticket, expect_pac=False) >+ self.assertIsNone(pac) >+ >+ def test_remove_pac_service_no_auth_data_required(self): >+ client_creds = self.get_client_creds() >+ service_creds = self.get_cached_creds( >+ account_type=self.AccountType.COMPUTER, >+ opts={'no_auth_data_required': True}) >+ >+ tgt = self.modified_ticket(self.get_tgt(client_creds), >+ exclude_pac=True) >+ >+ pac = self.get_ticket_pac(tgt, expect_pac=False) >+ self.assertIsNone(pac) >+ >+ self._make_tgs_request(client_creds, service_creds, tgt, >+ expect_pac=False, expect_error=True) >+ >+ def test_remove_pac_client_no_auth_data_required(self): >+ client_creds = self.get_cached_creds( >+ account_type=self.AccountType.USER, >+ opts={'no_auth_data_required': True}) > service_creds = self.get_service_creds() > > tgt = self.modified_ticket(self.get_tgt(client_creds), >@@ -324,12 +406,22 @@ class KdcTgsTests(KDCBaseTest): > pac = self.get_ticket_pac(tgt, expect_pac=False) > self.assertIsNone(pac) > >- ticket = self._make_tgs_request(client_creds, service_creds, tgt, >- expect_pac=False) >+ self._make_tgs_request(client_creds, service_creds, tgt, >+ expect_pac=False, expect_error=True) > >- pac = self.get_ticket_pac(ticket, expect_pac=False) >+ def test_remove_pac(self): >+ client_creds = self.get_client_creds() >+ service_creds = self.get_service_creds() >+ >+ tgt = self.modified_ticket(self.get_tgt(client_creds), >+ exclude_pac=True) >+ >+ pac = self.get_ticket_pac(tgt, expect_pac=False) > self.assertIsNone(pac) > >+ self._make_tgs_request(client_creds, service_creds, tgt, >+ expect_pac=False, expect_error=True) >+ > > if __name__ == "__main__": > global_asn1_print = False >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 3cd8cfde948..86d1733926f 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -99,3 +99,12 @@ > ^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_instance_spn_computer > ^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_our_domain_spn_computer > ^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_our_realm_spn_computer >+# >+# KDC TGS PAC tests >+# >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_client_no_auth_data_required >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_service_no_auth_data_required >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_client_no_auth_data_required >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_service_no_auth_data_required >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_request_no_pac >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index 1576e7244ac..42a746a5177 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -256,7 +256,13 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_ldap_service_ticket\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_get_ticket_for_host_service_of_machine_account\(ad_dc\) > # >+# KDC TGS PAC tests >+# >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_client_no_auth_data_required\(ad_dc\) >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_service_no_auth_data_required\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac\(ad_dc\) >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_client_no_auth_data_required\(ad_dc\) >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_service_no_auth_data_required\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_request_no_pac\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_service_no_auth_data_required\(ad_dc\) > # >-- >2.25.1 > > >From 2be0e2c8c3d64f161f85f510f3b86e0e71554d29 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Mon, 18 Oct 2021 14:07:41 +1300 >Subject: [PATCH 041/190] CVE-2020-25722 Add test for SPN deletion followed by > addition > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 >--- > selftest/knownfail.d/acl-spn | 1 + > source4/dsdb/tests/python/acl.py | 54 ++++++++++++++++++++++++++++++++ > 2 files changed, 55 insertions(+) > create mode 100644 selftest/knownfail.d/acl-spn > >diff --git a/selftest/knownfail.d/acl-spn b/selftest/knownfail.d/acl-spn >new file mode 100644 >index 00000000000..e68add920a6 >--- /dev/null >+++ b/selftest/knownfail.d/acl-spn >@@ -0,0 +1 @@ >+^samba4.ldap.acl.python.*AclSPNTests.test_delete_add_spn >diff --git a/source4/dsdb/tests/python/acl.py b/source4/dsdb/tests/python/acl.py >index 2cb6303dce3..dcb4017699d 100755 >--- a/source4/dsdb/tests/python/acl.py >+++ b/source4/dsdb/tests/python/acl.py >@@ -2189,6 +2189,60 @@ class AclSPNTests(AclTests): > def test_spn_rodc(self): > self.dc_spn_test(self.rodcctx) > >+ def test_delete_add_spn(self): >+ # Grant Validated-SPN property. >+ mod = f'(OA;;SW;{security.GUID_DRS_VALIDATE_SPN};;{self.user_sid1})' >+ self.sd_utils.dacl_add_ace(self.computerdn, mod) >+ >+ spn_base = f'HOST/{self.computername}' >+ >+ allowed_spn = f'{spn_base}.{self.dcctx.dnsdomain}' >+ not_allowed_spn = f'{spn_base}/{self.dcctx.get_domain_name()}' >+ >+ # Ensure we are able to add an allowed SPN. >+ msg = Message(Dn(self.ldb_user1, self.computerdn)) >+ msg['servicePrincipalName'] = MessageElement(allowed_spn, >+ FLAG_MOD_ADD, >+ 'servicePrincipalName') >+ self.ldb_user1.modify(msg) >+ >+ # Ensure we are not able to add a disallowed SPN. >+ msg = Message(Dn(self.ldb_user1, self.computerdn)) >+ msg['servicePrincipalName'] = MessageElement(not_allowed_spn, >+ FLAG_MOD_ADD, >+ 'servicePrincipalName') >+ self.ldb_user1.transaction_start() >+ try: >+ self.ldb_user1.modify(msg) >+ except LdbError as e: >+ num, _ = e.args >+ self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail(f'able to add disallowed SPN {not_allowed_spn}') >+ finally: >+ self.ldb_user1.transaction_cancel() >+ >+ # Ensure that deleting an existing SPN followed by adding a disallowed >+ # SPN fails. >+ msg = Message(Dn(self.ldb_user1, self.computerdn)) >+ msg['0'] = MessageElement([], >+ FLAG_MOD_DELETE, >+ 'servicePrincipalName') >+ msg['1'] = MessageElement(not_allowed_spn, >+ FLAG_MOD_ADD, >+ 'servicePrincipalName') >+ self.ldb_user1.transaction_start() >+ try: >+ self.ldb_user1.modify(msg) >+ except LdbError as e: >+ num, _ = e.args >+ self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail(f'able to add disallowed SPN {not_allowed_spn}') >+ finally: >+ self.ldb_user1.transaction_cancel() >+ >+ > # tests SEC_ADS_LIST vs. SEC_ADS_LIST_OBJECT > @DynamicTestCase > class AclVisibiltyTests(AclTests): >-- >2.25.1 > > >From 6f313677da5ff914bbd85d47ced80c6b2ffcd376 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 19 Oct 2021 14:39:36 +1300 >Subject: [PATCH 042/190] CVE-2020-25719 tests/krb5: Add a test for making an > S4U2Self request without a PAC > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14686 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/s4u_tests.py | 37 ++++++++++++++++++++++++++-- > selftest/knownfail_heimdal_kdc | 1 + > 2 files changed, 36 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py >index 593ef94c910..a80a7b3427e 100755 >--- a/python/samba/tests/krb5/s4u_tests.py >+++ b/python/samba/tests/krb5/s4u_tests.py >@@ -256,6 +256,17 @@ class S4UKerberosTests(KDCBaseTest): > if unexpected_flags is not None: > unexpected_flags = krb5_asn1.TicketFlags(unexpected_flags) > >+ expected_error_mode = kdc_dict.pop('expected_error_mode', 0) >+ expected_status = kdc_dict.pop('expected_status', None) >+ if expected_error_mode: >+ 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 >+ >+ self.assertIsNone(expected_status) >+ > kdc_options = kdc_dict.pop('kdc_options', '0') > kdc_options = krb5_asn1.KDCOptions(kdc_options) > >@@ -290,9 +301,11 @@ class S4UKerberosTests(KDCBaseTest): > ticket_decryption_key=service_decryption_key, > expect_ticket_checksum=True, > generate_padata_fn=generate_s4u2self_padata, >- check_rep_fn=self.generic_check_kdc_rep, >+ check_error_fn=check_error_fn, >+ check_rep_fn=check_rep_fn, > check_kdc_private_fn=self.generic_check_kdc_private, >- expected_error_mode=0, >+ expected_error_mode=expected_error_mode, >+ expected_status=expected_status, > tgt=service_tgt, > authenticator_subkey=authenticator_subkey, > kdc_options=str(kdc_options), >@@ -321,6 +334,26 @@ class S4UKerberosTests(KDCBaseTest): > 'expected_flags': 'forwardable' > }) > >+ # Test performing an S4U2Self operation with a forwardable ticket that does >+ # not contain a PAC. The request should fail. >+ def test_s4u2self_no_pac(self): >+ def forwardable_no_pac(ticket): >+ ticket = self.set_ticket_forwardable(ticket, flag=True) >+ return self.remove_ticket_pac(ticket) >+ >+ self._run_s4u2self_test( >+ { >+ 'expected_error_mode': (KDC_ERR_GENERIC, >+ KDC_ERR_BADOPTION), >+ 'expected_status': ntstatus.NT_STATUS_INVALID_PARAMETER, >+ 'client_opts': { >+ 'not_delegated': False >+ }, >+ 'kdc_options': 'forwardable', >+ 'modify_service_tgt_fn': forwardable_no_pac, >+ 'expected_flags': 'forwardable' >+ }) >+ > # Test performing an S4U2Self operation without requesting a forwardable > # ticket. The resulting ticket should not have the 'forwardable' flag set. > def test_s4u2self_without_forwardable(self): >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 86d1733926f..76322fc187e 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -81,6 +81,7 @@ > ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_client_checksum > ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_service_checksum > ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable >+^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_no_pac > ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed > # > # The lack of KRB5SignedPath means we no longer return >-- >2.25.1 > > >From 44b973a772a1af258e7de18f6db7bb11e9a13854 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 19 Oct 2021 20:02:45 +1300 >Subject: [PATCH 043/190] CVE-2020-25719 tests/krb5: Add principal aliasing > test > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14686 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/alias_tests.py | 201 +++++++++++++++++++ > python/samba/tests/krb5/rfc4120_constants.py | 1 + > python/samba/tests/usage.py | 1 + > selftest/knownfail_heimdal_kdc | 7 + > selftest/knownfail_mit_kdc | 7 + > source4/selftest/tests.py | 10 + > 6 files changed, 227 insertions(+) > create mode 100755 python/samba/tests/krb5/alias_tests.py > >diff --git a/python/samba/tests/krb5/alias_tests.py b/python/samba/tests/krb5/alias_tests.py >new file mode 100755 >index 00000000000..60213845a44 >--- /dev/null >+++ b/python/samba/tests/krb5/alias_tests.py >@@ -0,0 +1,201 @@ >+#!/usr/bin/env python3 >+# Unix SMB/CIFS implementation. >+# Copyright (C) Stefan Metzmacher 2020 >+# Copyright (C) 2021 Catalyst.Net Ltd >+# >+# 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 >+ >+import ldb >+ >+from samba.tests import delete_force >+import samba.tests.krb5.kcrypto as kcrypto >+from samba.tests.krb5.kdc_base_test import KDCBaseTest >+from samba.tests.krb5.rfc4120_constants import ( >+ AES256_CTS_HMAC_SHA1_96, >+ ARCFOUR_HMAC_MD5, >+ KDC_ERR_CLIENT_NAME_MISMATCH, >+ NT_PRINCIPAL, >+) >+ >+sys.path.insert(0, 'bin/python') >+os.environ['PYTHONUNBUFFERED'] = '1' >+ >+global_asn1_print = False >+global_hexdump = False >+ >+ >+class AliasTests(KDCBaseTest): >+ def test_dc_alias_rename(self): >+ self._run_dc_alias(action='rename') >+ >+ def test_dc_alias_delete(self): >+ self._run_dc_alias(action='delete') >+ >+ def _run_dc_alias(self, action=None): >+ target_creds = self.get_dc_creds() >+ target_name = target_creds.get_username()[:-1] >+ >+ self._run_alias(target_name, lambda: target_creds, action=action) >+ >+ def test_create_alias_rename(self): >+ self._run_create_alias(action='rename') >+ >+ def test_create_alias_delete(self): >+ self._run_create_alias(action='delete') >+ >+ def _run_create_alias(self, action=None): >+ target_name = self.get_new_username() >+ >+ def create_target(): >+ samdb = self.get_samdb() >+ >+ realm = samdb.domain_dns_name().lower() >+ >+ hostname = f'{target_name}.{realm}' >+ spn = f'ldap/{hostname}' >+ >+ details = { >+ 'dNSHostName': hostname >+ } >+ >+ creds, fn = self.create_account( >+ samdb, >+ target_name, >+ account_type=self.AccountType.COMPUTER, >+ spn=spn, >+ additional_details=details) >+ >+ return creds >+ >+ self._run_alias(target_name, create_target, action=action) >+ >+ def _run_alias(self, target_name, target_creds_fn, action=None): >+ samdb = self.get_samdb() >+ >+ mach_name = self.get_new_username() >+ >+ # Create a machine account. >+ mach_creds, mach_dn = self.create_account( >+ samdb, mach_name, account_type=self.AccountType.COMPUTER) >+ self.addCleanup(delete_force, samdb, mach_dn) >+ >+ mach_sid = self.get_objectSid(samdb, mach_dn) >+ realm = mach_creds.get_realm() >+ >+ # The account salt doesn't change when the account is renamed. >+ old_salt = mach_creds.get_salt() >+ mach_creds.set_forced_salt(old_salt) >+ >+ # Rename the account to alias with the target account. >+ msg = ldb.Message(ldb.Dn(samdb, mach_dn)) >+ msg['sAMAccountName'] = ldb.MessageElement(target_name, >+ ldb.FLAG_MOD_REPLACE, >+ 'sAMAccountName') >+ samdb.modify(msg) >+ mach_creds.set_username(target_name) >+ >+ # Get a TGT for the machine account. >+ tgt = self.get_tgt(mach_creds, kdc_options='0', fresh=True) >+ >+ # Check the PAC. >+ pac_data = self.get_pac_data(tgt.ticket_private['authorization-data']) >+ >+ upn = f'{target_name}@{realm.lower()}' >+ >+ self.assertEqual(target_name, str(pac_data.account_name)) >+ self.assertEqual(mach_sid, pac_data.account_sid) >+ self.assertEqual(target_name, pac_data.logon_name) >+ self.assertEqual(upn, pac_data.upn) >+ self.assertEqual(realm, pac_data.domain_name) >+ >+ # Rename or delete the machine account. >+ if action == 'rename': >+ mach_name2 = self.get_new_username() >+ >+ msg = ldb.Message(ldb.Dn(samdb, mach_dn)) >+ msg['sAMAccountName'] = ldb.MessageElement(mach_name2, >+ ldb.FLAG_MOD_REPLACE, >+ 'sAMAccountName') >+ samdb.modify(msg) >+ elif action == 'delete': >+ samdb.delete(mach_dn) >+ else: >+ self.fail(action) >+ >+ # Get the credentials for the target account. >+ target_creds = target_creds_fn() >+ >+ # Look up the DNS host name of the target account. >+ target_dn = target_creds.get_dn() >+ res = samdb.search(target_dn, >+ scope=ldb.SCOPE_BASE, >+ attrs=['dNSHostName']) >+ target_hostname = str(res[0].get('dNSHostName', idx=0)) >+ >+ sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=['ldap', target_hostname]) >+ target_cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=[target_name]) >+ >+ target_decryption_key = self.TicketDecryptionKey_from_creds( >+ target_creds) >+ >+ authenticator_subkey = self.RandomKey(kcrypto.Enctype.AES256) >+ >+ etypes = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) >+ >+ def generate_s4u2self_padata(_kdc_exchange_dict, >+ _callback_dict, >+ req_body): >+ padata = self.PA_S4U2Self_create(name=target_cname, >+ realm=realm, >+ tgt_session_key=tgt.session_key, >+ ctype=None) >+ return [padata], req_body >+ >+ expected_error_mode = KDC_ERR_CLIENT_NAME_MISMATCH >+ >+ # Make a request using S4U2Self. The request should fail. >+ kdc_exchange_dict = self.tgs_exchange_dict( >+ expected_crealm=realm, >+ expected_cname=target_cname, >+ expected_srealm=realm, >+ expected_sname=sname, >+ ticket_decryption_key=target_decryption_key, >+ generate_padata_fn=generate_s4u2self_padata, >+ expected_error_mode=expected_error_mode, >+ check_error_fn=self.generic_check_kdc_error, >+ check_kdc_private_fn=self.generic_check_kdc_private, >+ tgt=tgt, >+ authenticator_subkey=authenticator_subkey, >+ kdc_options='0', >+ expect_pac=True) >+ >+ rep = self._generic_kdc_exchange(kdc_exchange_dict, >+ cname=None, >+ realm=realm, >+ sname=sname, >+ etypes=etypes) >+ self.check_error_rep(rep, expected_error_mode) >+ >+ >+if __name__ == '__main__': >+ global_asn1_print = False >+ global_hexdump = False >+ import unittest >+ unittest.main() >diff --git a/python/samba/tests/krb5/rfc4120_constants.py b/python/samba/tests/krb5/rfc4120_constants.py >index 39bb2db8e32..b643185f767 100644 >--- a/python/samba/tests/krb5/rfc4120_constants.py >+++ b/python/samba/tests/krb5/rfc4120_constants.py >@@ -81,6 +81,7 @@ KDC_ERR_SKEW = 37 > KDC_ERR_MODIFIED = 41 > KDC_ERR_INAPP_CKSUM = 50 > KDC_ERR_GENERIC = 60 >+KDC_ERR_CLIENT_NAME_MISMATCH = 75 > KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS = 93 > > # Extended error types >diff --git a/python/samba/tests/usage.py b/python/samba/tests/usage.py >index a7fb4631d0b..aed012cf767 100644 >--- a/python/samba/tests/usage.py >+++ b/python/samba/tests/usage.py >@@ -106,6 +106,7 @@ EXCLUDE_USAGE = { > 'python/samba/tests/krb5/rodc_tests.py', > 'python/samba/tests/krb5/salt_tests.py', > 'python/samba/tests/krb5/spn_tests.py', >+ 'python/samba/tests/krb5/alias_tests.py', > } > > EXCLUDE_HELP = { >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 76322fc187e..8a205f4ac14 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -109,3 +109,10 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_client_no_auth_data_required > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_service_no_auth_data_required > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_request_no_pac >+# >+# Alias tests >+# >+^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_delete >+^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_rename >+^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_delete >+^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_rename >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index 42a746a5177..16d30bc9685 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -364,3 +364,10 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_instance_spn_computer > ^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_our_domain_spn_computer > ^samba.tests.krb5.spn_tests.samba.tests.krb5.spn_tests.SpnTests.test_spn_3_part_our_realm_spn_computer >+# >+# Alias tests >+# >+^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_delete >+^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_rename >+^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_delete >+^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_rename >diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py >index f74e87345ee..41ae1e6219f 100755 >--- a/source4/selftest/tests.py >+++ b/source4/selftest/tests.py >@@ -1630,6 +1630,16 @@ planpythontestsuite( > 'FAST_SUPPORT': have_fast_support, > 'TKT_SIG_SUPPORT': tkt_sig_support > }) >+planpythontestsuite( >+ "ad_dc", >+ "samba.tests.krb5.alias_tests", >+ environ={ >+ 'ADMIN_USERNAME': '$USERNAME', >+ 'ADMIN_PASSWORD': '$PASSWORD', >+ 'STRICT_CHECKING': '0', >+ 'FAST_SUPPORT': have_fast_support, >+ 'TKT_SIG_SUPPORT': tkt_sig_support >+ }) > > for env in [ > 'vampire_dc', >-- >2.25.1 > > >From fb1985ffa3040d5dbacde99bb8b5d2364c027d8f Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Thu, 21 Oct 2021 11:45:23 +1300 >Subject: [PATCH 044/190] CVE-2020-25718 tests/krb5: Add tests for RODC-printed > and invalid TGTs > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14558 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 6 +- > python/samba/tests/krb5/kdc_tgs_tests.py | 808 +++++++++++++++++++ > python/samba/tests/krb5/rfc4120_constants.py | 1 + > selftest/knownfail_heimdal_kdc | 64 ++ > selftest/knownfail_mit_kdc | 67 ++ > 5 files changed, 944 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index cc23484ba2c..4fe7485c492 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -657,7 +657,8 @@ class KDCBaseTest(RawKerberosTest): > 'delegation_to_spn': None, > 'delegation_from_dn': None, > 'trusted_to_auth_for_delegation': False, >- 'fast_support': False >+ 'fast_support': False, >+ 'id': None > } > > account_opts = { >@@ -698,7 +699,8 @@ class KDCBaseTest(RawKerberosTest): > delegation_to_spn, > delegation_from_dn, > trusted_to_auth_for_delegation, >- fast_support): >+ fast_support, >+ id): > if account_type is self.AccountType.USER: > self.assertIsNone(spn) > self.assertIsNone(delegation_to_spn) >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index fbeb5fe61fb..74f1032163e 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -20,6 +20,13 @@ > import sys > import os > >+import ldb >+ >+ >+from samba import dsdb >+ >+from samba.dcerpc import krb5pac >+ > sys.path.insert(0, "bin/python") > os.environ["PYTHONUNBUFFERED"] = "1" > >@@ -32,6 +39,10 @@ from samba.tests.krb5.rfc4120_constants import ( > KRB_TGS_REP, > KDC_ERR_BADMATCH, > KDC_ERR_BADOPTION, >+ KDC_ERR_CLIENT_NAME_MISMATCH, >+ KDC_ERR_POLICY, >+ KDC_ERR_S_PRINCIPAL_UNKNOWN, >+ KDC_ERR_TGT_REVOKED, > NT_PRINCIPAL, > NT_SRV_INST, > ) >@@ -422,6 +433,803 @@ class KdcTgsTests(KDCBaseTest): > self._make_tgs_request(client_creds, service_creds, tgt, > expect_pac=False, expect_error=True) > >+ # Test making a TGS request. >+ def test_tgs_req(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ self._run_tgs(tgt, expected_error=0) >+ >+ def test_renew_req(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, renewable=True) >+ self._renew_tgt(tgt, expected_error=0) >+ >+ def test_validate_req(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, invalid=True) >+ self._validate_tgt(tgt, expected_error=0) >+ >+ def test_s4u2self_req(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ self._s4u2self(tgt, creds, expected_error=0) >+ >+ def test_user2user_req(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ self._user2user(tgt, creds, expected_error=0) >+ >+ # Test making a request without a PAC. >+ def test_tgs_no_pac(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, remove_pac=True) >+ self._run_tgs(tgt, expected_error=KDC_ERR_BADOPTION) >+ >+ def test_renew_no_pac(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, renewable=True, remove_pac=True) >+ self._renew_tgt(tgt, expected_error=KDC_ERR_BADOPTION) >+ >+ def test_validate_no_pac(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, invalid=True, remove_pac=True) >+ self._validate_tgt(tgt, expected_error=KDC_ERR_BADOPTION) >+ >+ def test_s4u2self_no_pac(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, remove_pac=True) >+ self._s4u2self(tgt, creds, expected_error=KDC_ERR_BADOPTION) >+ >+ def test_user2user_no_pac(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, remove_pac=True) >+ self._user2user(tgt, creds, expected_error=KDC_ERR_BADOPTION) >+ >+ # Test changing the SID in the PAC to that of another account. >+ def test_tgs_sid_mismatch_existing(self): >+ creds = self._get_creds() >+ existing_rid = self._get_existing_rid() >+ tgt = self._get_tgt(creds, new_rid=existing_rid) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_renew_sid_mismatch_existing(self): >+ creds = self._get_creds() >+ existing_rid = self._get_existing_rid() >+ tgt = self._get_tgt(creds, renewable=True, new_rid=existing_rid) >+ self._renew_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_validate_sid_mismatch_existing(self): >+ creds = self._get_creds() >+ existing_rid = self._get_existing_rid() >+ tgt = self._get_tgt(creds, invalid=True, new_rid=existing_rid) >+ self._validate_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_s4u2self_sid_mismatch_existing(self): >+ creds = self._get_creds() >+ existing_rid = self._get_existing_rid() >+ tgt = self._get_tgt(creds, new_rid=existing_rid) >+ self._s4u2self(tgt, creds, >+ expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_user2user_sid_mismatch_existing(self): >+ creds = self._get_creds() >+ existing_rid = self._get_existing_rid() >+ tgt = self._get_tgt(creds, new_rid=existing_rid) >+ self._user2user(tgt, creds, >+ expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ # Test changing the SID in the PAC to a non-existent one. >+ def test_tgs_sid_mismatch_nonexisting(self): >+ creds = self._get_creds() >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, new_rid=nonexistent_rid) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_renew_sid_mismatch_nonexisting(self): >+ creds = self._get_creds() >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, renewable=True, >+ new_rid=nonexistent_rid) >+ self._renew_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_validate_sid_mismatch_nonexisting(self): >+ creds = self._get_creds() >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, invalid=True, >+ new_rid=nonexistent_rid) >+ self._validate_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_s4u2self_sid_mismatch_nonexisting(self): >+ creds = self._get_creds() >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, new_rid=nonexistent_rid) >+ self._s4u2self(tgt, creds, >+ expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_user2user_sid_mismatch_nonexisting(self): >+ creds = self._get_creds() >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, new_rid=nonexistent_rid) >+ self._user2user(tgt, creds, >+ expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ # Test with an RODC-issued ticket where the client is revealed to the RODC. >+ def test_tgs_rodc_revealed(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._run_tgs(tgt, expected_error=0) >+ >+ def test_renew_rodc_revealed(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True) >+ self._renew_tgt(tgt, expected_error=0) >+ >+ def test_validate_rodc_revealed(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True) >+ self._validate_tgt(tgt, expected_error=0) >+ >+ def test_s4u2self_rodc_revealed(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._s4u2self(tgt, creds, expected_error=0) >+ >+ def test_user2user_rodc_revealed(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._user2user(tgt, creds, expected_error=0) >+ >+ # Test with an RODC-issued ticket where the SID in the PAC is changed to >+ # that of another account. >+ def test_tgs_rodc_sid_mismatch_existing(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ existing_rid = self._get_existing_rid(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_renew_rodc_sid_mismatch_existing(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ existing_rid = self._get_existing_rid(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True, >+ new_rid=existing_rid) >+ self._renew_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_validate_rodc_sid_mismatch_existing(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ existing_rid = self._get_existing_rid(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True, >+ new_rid=existing_rid) >+ self._validate_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_s4u2self_rodc_sid_mismatch_existing(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ existing_rid = self._get_existing_rid(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid) >+ self._s4u2self(tgt, creds, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_user2user_rodc_sid_mismatch_existing(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ existing_rid = self._get_existing_rid(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid) >+ self._user2user(tgt, creds, >+ expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ # Test with an RODC-issued ticket where the SID in the PAC is changed to a >+ # non-existent one. >+ def test_tgs_rodc_sid_mismatch_nonexisting(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_renew_rodc_sid_mismatch_nonexisting(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True, >+ new_rid=nonexistent_rid) >+ self._renew_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_validate_rodc_sid_mismatch_nonexisting(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True, >+ new_rid=nonexistent_rid) >+ self._validate_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_s4u2self_rodc_sid_mismatch_nonexisting(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid) >+ self._s4u2self(tgt, creds, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_user2user_rodc_sid_mismatch_nonexisting(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid) >+ self._user2user(tgt, creds, >+ expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ # Test with an RODC-issued ticket where the client is not revealed to the >+ # RODC. >+ def test_tgs_rodc_not_revealed(self): >+ creds = self._get_creds(replication_allowed=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ # TODO: error code >+ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_renew_rodc_not_revealed(self): >+ creds = self._get_creds(replication_allowed=True) >+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True) >+ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_validate_rodc_not_revealed(self): >+ creds = self._get_creds(replication_allowed=True) >+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True) >+ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_s4u2self_rodc_not_revealed(self): >+ creds = self._get_creds(replication_allowed=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._s4u2self(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_user2user_rodc_not_revealed(self): >+ creds = self._get_creds(replication_allowed=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._user2user(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ # Test with an RODC-issued ticket where the RODC account does not have the >+ # PARTIAL_SECRETS bit set. >+ def test_tgs_rodc_no_partial_secrets(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._remove_rodc_partial_secrets() >+ self._run_tgs(tgt, expected_error=KDC_ERR_POLICY) >+ >+ def test_renew_rodc_no_partial_secrets(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True) >+ self._remove_rodc_partial_secrets() >+ self._renew_tgt(tgt, expected_error=KDC_ERR_POLICY) >+ >+ def test_validate_rodc_no_partial_secrets(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True) >+ self._remove_rodc_partial_secrets() >+ self._validate_tgt(tgt, expected_error=KDC_ERR_POLICY) >+ >+ def test_s4u2self_rodc_no_partial_secrets(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._remove_rodc_partial_secrets() >+ self._s4u2self(tgt, creds, expected_error=KDC_ERR_POLICY) >+ >+ def test_user2user_rodc_no_partial_secrets(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._remove_rodc_partial_secrets() >+ self._user2user(tgt, creds, expected_error=KDC_ERR_POLICY) >+ >+ # Test with an RODC-issued ticket where the RODC account does not have an >+ # msDS-KrbTgtLink. >+ def test_tgs_rodc_no_krbtgt_link(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._remove_rodc_krbtgt_link() >+ self._run_tgs(tgt, expected_error=KDC_ERR_POLICY) >+ >+ def test_renew_rodc_no_krbtgt_link(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True) >+ self._remove_rodc_krbtgt_link() >+ self._renew_tgt(tgt, expected_error=KDC_ERR_POLICY) >+ >+ def test_validate_rodc_no_krbtgt_link(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True) >+ self._remove_rodc_krbtgt_link() >+ self._validate_tgt(tgt, expected_error=KDC_ERR_POLICY) >+ >+ def test_s4u2self_rodc_no_krbtgt_link(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._remove_rodc_krbtgt_link() >+ self._s4u2self(tgt, creds, expected_error=KDC_ERR_POLICY) >+ >+ def test_user2user_rodc_no_krbtgt_link(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._remove_rodc_krbtgt_link() >+ self._user2user(tgt, creds, expected_error=KDC_ERR_POLICY) >+ >+ # Test with an RODC-issued ticket where the client is not allowed to >+ # replicate to the RODC. >+ def test_tgs_rodc_not_allowed(self): >+ creds = self._get_creds(revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_renew_rodc_not_allowed(self): >+ creds = self._get_creds(revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True) >+ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_validate_rodc_not_allowed(self): >+ creds = self._get_creds(revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True) >+ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_s4u2self_rodc_not_allowed(self): >+ creds = self._get_creds(revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._s4u2self(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_user2user_rodc_not_allowed(self): >+ creds = self._get_creds(revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._user2user(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ # Test with an RODC-issued ticket where the client is denied from >+ # replicating to the RODC. >+ def test_tgs_rodc_denied(self): >+ creds = self._get_creds(replication_denied=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_renew_rodc_denied(self): >+ creds = self._get_creds(replication_denied=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True) >+ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_validate_rodc_denied(self): >+ creds = self._get_creds(replication_denied=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True) >+ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_s4u2self_rodc_denied(self): >+ creds = self._get_creds(replication_denied=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._s4u2self(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_user2user_rodc_denied(self): >+ creds = self._get_creds(replication_denied=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._user2user(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ # Test with an RODC-issued ticket where the client is both allowed and >+ # denied replicating to the RODC. >+ def test_tgs_rodc_allowed_denied(self): >+ creds = self._get_creds(replication_allowed=True, >+ replication_denied=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_renew_rodc_allowed_denied(self): >+ creds = self._get_creds(replication_allowed=True, >+ replication_denied=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True) >+ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_validate_rodc_allowed_denied(self): >+ creds = self._get_creds(replication_allowed=True, >+ replication_denied=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True) >+ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_s4u2self_rodc_allowed_denied(self): >+ creds = self._get_creds(replication_allowed=True, >+ replication_denied=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._s4u2self(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ def test_user2user_rodc_allowed_denied(self): >+ creds = self._get_creds(replication_allowed=True, >+ replication_denied=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True) >+ self._user2user(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED) >+ >+ # Test user-to-user with incorrect service principal names. >+ def test_user2user_matching_sname_host(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ user_name = self._get_mach_creds().get_username() >+ sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=['host', user_name]) >+ >+ self._user2user(tgt, creds, sname=sname, >+ expected_error=KDC_ERR_S_PRINCIPAL_UNKNOWN) >+ >+ def test_user2user_matching_sname_no_host(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ user_name = self._get_mach_creds().get_username() >+ sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=[user_name]) >+ >+ self._user2user(tgt, creds, sname=sname, >+ expected_error=KDC_ERR_BADMATCH) >+ >+ def test_user2user_wrong_sname(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ other_creds = self.get_service_creds() >+ user_name = other_creds.get_username() >+ sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=[user_name]) >+ >+ self._user2user(tgt, creds, sname=sname, >+ expected_error=(KDC_ERR_BADMATCH, >+ KDC_ERR_BADOPTION)) >+ >+ def test_user2user_non_existent_sname(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=['host', 'non_existent_user']) >+ >+ self._user2user(tgt, creds, sname=sname, >+ expected_error=KDC_ERR_S_PRINCIPAL_UNKNOWN) >+ >+ def test_user2user_service_ticket(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ service_creds = self.get_service_creds() >+ service_ticket = self.get_service_ticket(tgt, service_creds) >+ >+ self._user2user(service_ticket, creds, expected_error=KDC_ERR_POLICY) >+ >+ def _get_tgt(self, >+ client_creds, >+ renewable=False, >+ invalid=False, >+ from_rodc=False, >+ new_rid=None, >+ remove_pac=False): >+ self.assertFalse(renewable and invalid) >+ >+ if remove_pac: >+ self.assertIsNone(new_rid) >+ >+ tgt = self.get_tgt(client_creds) >+ >+ if from_rodc: >+ krbtgt_creds = self.get_mock_rodc_krbtgt_creds() >+ else: >+ krbtgt_creds = self.get_krbtgt_creds() >+ >+ if new_rid is not None: >+ def change_sid_fn(pac): >+ for pac_buffer in pac.buffers: >+ if pac_buffer.type == krb5pac.PAC_TYPE_LOGON_INFO: >+ logon_info = pac_buffer.info.info >+ >+ logon_info.info3.base.rid = new_rid >+ >+ return pac >+ >+ modify_pac_fn = change_sid_fn >+ else: >+ modify_pac_fn = None >+ >+ krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) >+ >+ if remove_pac: >+ checksum_keys = None >+ else: >+ checksum_keys = { >+ krb5pac.PAC_TYPE_KDC_CHECKSUM: krbtgt_key >+ } >+ >+ if renewable: >+ def set_renewable(enc_part): >+ # Set the renewable flag. >+ renewable_flag = krb5_asn1.TicketFlags('renewable') >+ pos = len(tuple(renewable_flag)) - 1 >+ >+ flags = enc_part['flags'] >+ self.assertLessEqual(pos, len(flags)) >+ >+ new_flags = flags[:pos] + '1' + flags[pos + 1:] >+ enc_part['flags'] = new_flags >+ >+ # Set the renew-till time to be in the future. >+ renew_till = self.get_KerberosTime(offset=100 * 60 * 60) >+ enc_part['renew-till'] = renew_till >+ >+ return enc_part >+ >+ modify_fn = set_renewable >+ elif invalid: >+ def set_invalid(enc_part): >+ # Set the invalid flag. >+ invalid_flag = krb5_asn1.TicketFlags('invalid') >+ pos = len(tuple(invalid_flag)) - 1 >+ >+ flags = enc_part['flags'] >+ self.assertLessEqual(pos, len(flags)) >+ >+ new_flags = flags[:pos] + '1' + flags[pos + 1:] >+ enc_part['flags'] = new_flags >+ >+ # Set the ticket start time to be in the past. >+ past_time = self.get_KerberosTime(offset=-100 * 60 * 60) >+ enc_part['starttime'] = past_time >+ >+ return enc_part >+ >+ modify_fn = set_invalid >+ else: >+ modify_fn = None >+ >+ return self.modified_ticket( >+ tgt, >+ new_ticket_key=krbtgt_key, >+ modify_fn=modify_fn, >+ modify_pac_fn=modify_pac_fn, >+ exclude_pac=remove_pac, >+ update_pac_checksums=not remove_pac, >+ checksum_keys=checksum_keys) >+ >+ def _remove_rodc_partial_secrets(self): >+ samdb = self.get_samdb() >+ >+ rodc_ctx = self.get_mock_rodc_ctx() >+ rodc_dn = ldb.Dn(samdb, rodc_ctx.acct_dn) >+ >+ def add_rodc_partial_secrets(): >+ msg = ldb.Message() >+ msg.dn = rodc_dn >+ msg['userAccountControl'] = ldb.MessageElement( >+ str(rodc_ctx.userAccountControl), >+ ldb.FLAG_MOD_REPLACE, >+ 'userAccountControl') >+ samdb.modify(msg) >+ >+ self.addCleanup(add_rodc_partial_secrets) >+ >+ uac = rodc_ctx.userAccountControl & ~dsdb.UF_PARTIAL_SECRETS_ACCOUNT >+ >+ msg = ldb.Message() >+ msg.dn = rodc_dn >+ msg['userAccountControl'] = ldb.MessageElement( >+ str(uac), >+ ldb.FLAG_MOD_REPLACE, >+ 'userAccountControl') >+ samdb.modify(msg) >+ >+ def _remove_rodc_krbtgt_link(self): >+ samdb = self.get_samdb() >+ >+ rodc_ctx = self.get_mock_rodc_ctx() >+ rodc_dn = ldb.Dn(samdb, rodc_ctx.acct_dn) >+ >+ def add_rodc_krbtgt_link(): >+ msg = ldb.Message() >+ msg.dn = rodc_dn >+ msg['msDS-KrbTgtLink'] = ldb.MessageElement( >+ rodc_ctx.new_krbtgt_dn, >+ ldb.FLAG_MOD_ADD, >+ 'msDS-KrbTgtLink') >+ samdb.modify(msg) >+ >+ self.addCleanup(add_rodc_krbtgt_link) >+ >+ msg = ldb.Message() >+ msg.dn = rodc_dn >+ msg['msDS-KrbTgtLink'] = ldb.MessageElement( >+ [], >+ ldb.FLAG_MOD_DELETE, >+ 'msDS-KrbTgtLink') >+ samdb.modify(msg) >+ >+ def _get_creds(self, >+ replication_allowed=False, >+ replication_denied=False, >+ revealed_to_rodc=False): >+ return self.get_cached_creds( >+ account_type=self.AccountType.COMPUTER, >+ opts={ >+ 'allowed_replication_mock': replication_allowed, >+ 'denied_replication_mock': replication_denied, >+ 'revealed_to_mock_rodc': revealed_to_rodc, >+ 'id': 0 >+ }) >+ >+ def _get_existing_rid(self, >+ replication_allowed=False, >+ replication_denied=False, >+ revealed_to_rodc=False): >+ other_creds = self.get_cached_creds( >+ account_type=self.AccountType.COMPUTER, >+ opts={ >+ 'allowed_replication_mock': replication_allowed, >+ 'denied_replication_mock': replication_denied, >+ 'revealed_to_mock_rodc': revealed_to_rodc, >+ 'id': 1 >+ }) >+ >+ samdb = self.get_samdb() >+ >+ other_dn = other_creds.get_dn() >+ other_sid = self.get_objectSid(samdb, other_dn) >+ >+ other_rid = int(other_sid.rsplit('-', 1)[1]) >+ >+ return other_rid >+ >+ def _get_mach_creds(self): >+ return self.get_cached_creds( >+ account_type=self.AccountType.COMPUTER, >+ opts={ >+ 'allowed_replication_mock': True, >+ 'denied_replication_mock': False, >+ 'revealed_to_mock_rodc': True, >+ 'id': 2 >+ }) >+ >+ def _get_non_existent_rid(self): >+ return (1 << 30) - 1 >+ >+ def _run_tgs(self, tgt, expected_error): >+ target_creds = self.get_service_creds() >+ self._tgs_req(tgt, expected_error, target_creds) >+ >+ def _renew_tgt(self, tgt, expected_error): >+ krbtgt_creds = self.get_krbtgt_creds() >+ kdc_options = str(krb5_asn1.KDCOptions('renew')) >+ self._tgs_req(tgt, expected_error, krbtgt_creds, >+ kdc_options=kdc_options) >+ >+ def _validate_tgt(self, tgt, expected_error): >+ krbtgt_creds = self.get_krbtgt_creds() >+ kdc_options = str(krb5_asn1.KDCOptions('validate')) >+ self._tgs_req(tgt, expected_error, krbtgt_creds, >+ kdc_options=kdc_options) >+ >+ def _s4u2self(self, tgt, tgt_creds, expected_error): >+ user_creds = self._get_mach_creds() >+ >+ user_name = user_creds.get_username() >+ user_cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=[user_name]) >+ user_realm = user_creds.get_realm() >+ >+ def generate_s4u2self_padata(_kdc_exchange_dict, >+ _callback_dict, >+ req_body): >+ padata = self.PA_S4U2Self_create( >+ name=user_cname, >+ realm=user_realm, >+ tgt_session_key=tgt.session_key, >+ ctype=None) >+ >+ return [padata], req_body >+ >+ self._tgs_req(tgt, expected_error, tgt_creds, >+ expected_cname=user_cname, >+ generate_padata_fn=generate_s4u2self_padata, >+ expect_claims=False) >+ >+ def _user2user(self, tgt, tgt_creds, expected_error, sname=None): >+ user_creds = self._get_mach_creds() >+ user_tgt = self.get_tgt(user_creds) >+ >+ kdc_options = str(krb5_asn1.KDCOptions('enc-tkt-in-skey')) >+ self._tgs_req(user_tgt, expected_error, tgt_creds, >+ kdc_options=kdc_options, >+ additional_ticket=tgt, >+ sname=sname) >+ >+ def _tgs_req(self, tgt, expected_error, target_creds, >+ kdc_options='0', >+ expected_cname=None, >+ additional_ticket=None, >+ generate_padata_fn=None, >+ sname=None, >+ expect_claims=True): >+ srealm = target_creds.get_realm() >+ >+ if sname is None: >+ target_name = target_creds.get_username() >+ if target_name == 'krbtgt': >+ sname = self.PrincipalName_create(name_type=NT_SRV_INST, >+ names=[target_name, srealm]) >+ else: >+ if target_name[-1] == '$': >+ target_name = target_name[:-1] >+ sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=['host', target_name]) >+ >+ if additional_ticket is not None: >+ additional_tickets = [additional_ticket.ticket] >+ decryption_key = additional_ticket.session_key >+ else: >+ additional_tickets = None >+ decryption_key = self.TicketDecryptionKey_from_creds( >+ target_creds) >+ >+ subkey = self.RandomKey(tgt.session_key.etype) >+ >+ etypes = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5) >+ >+ 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 >+ >+ if expected_cname is None: >+ expected_cname = tgt.cname >+ >+ kdc_exchange_dict = self.tgs_exchange_dict( >+ expected_crealm=tgt.crealm, >+ expected_cname=expected_cname, >+ expected_srealm=srealm, >+ expected_sname=sname, >+ ticket_decryption_key=decryption_key, >+ generate_padata_fn=generate_padata_fn, >+ 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, >+ tgt=tgt, >+ authenticator_subkey=subkey, >+ kdc_options=kdc_options, >+ expect_edata=False, >+ expect_claims=expect_claims) >+ >+ self._generic_kdc_exchange(kdc_exchange_dict, >+ cname=None, >+ realm=srealm, >+ sname=sname, >+ etypes=etypes, >+ additional_tickets=additional_tickets) >+ > > if __name__ == "__main__": > global_asn1_print = False >diff --git a/python/samba/tests/krb5/rfc4120_constants.py b/python/samba/tests/krb5/rfc4120_constants.py >index b643185f767..490cd255ec3 100644 >--- a/python/samba/tests/krb5/rfc4120_constants.py >+++ b/python/samba/tests/krb5/rfc4120_constants.py >@@ -72,6 +72,7 @@ KDC_ERR_POLICY = 12 > KDC_ERR_BADOPTION = 13 > KDC_ERR_ETYPE_NOSUPP = 14 > KDC_ERR_SUMTYPE_NOSUPP = 15 >+KDC_ERR_TGT_REVOKED = 20 > KDC_ERR_PREAUTH_FAILED = 24 > KDC_ERR_PREAUTH_REQUIRED = 25 > KDC_ERR_BAD_INTEGRITY = 31 >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 8a205f4ac14..a4ec57ea12c 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -116,3 +116,67 @@ > ^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_rename > ^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_delete > ^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_rename >+# >+# KDC TGT tests >+# >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_allowed_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_no_krbtgt_link >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_no_partial_secrets >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_not_allowed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_not_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_allowed_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_no_krbtgt_link >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_no_partial_secrets >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_not_allowed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_not_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_allowed_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_no_krbtgt_link >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_no_partial_secrets >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_not_allowed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_not_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_host >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_no_host >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_non_existent_sname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_req >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_allowed_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_no_krbtgt_link >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_no_partial_secrets >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_not_allowed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_not_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_allowed_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_no_krbtgt_link >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_no_partial_secrets >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_not_allowed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_not_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_nonexisting >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index 16d30bc9685..cdf35585aea 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -371,3 +371,70 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_rename > ^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_delete > ^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_rename >+# >+# KDC TGT tests >+# >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_allowed_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_no_krbtgt_link >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_no_partial_secrets >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_not_allowed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_not_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_req >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_allowed_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_no_krbtgt_link >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_no_partial_secrets >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_not_allowed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_not_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_allowed_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_no_krbtgt_link >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_no_partial_secrets >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_not_allowed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_not_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_no_host >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_req >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_allowed_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_no_krbtgt_link >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_no_partial_secrets >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_not_allowed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_not_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_allowed_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_denied >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_no_krbtgt_link >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_no_partial_secrets >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_not_allowed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_not_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_nonexisting >-- >2.25.1 > > >From fed6761ad6691a1cf75b44d2165aef3f0d18554b Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Thu, 28 Oct 2021 16:20:07 +1300 >Subject: [PATCH 045/190] CVE-2020-25719 tests/krb5: Add tests for including > authdata without a PAC > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14561 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 32 +++++++++++++++++++++++- > python/samba/tests/krb5/raw_testcase.py | 14 +++++++---- > selftest/knownfail_heimdal_kdc | 5 ++++ > selftest/knownfail_mit_kdc | 5 ++++ > 4 files changed, 50 insertions(+), 6 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 74f1032163e..5de79c30e1b 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -485,6 +485,34 @@ class KdcTgsTests(KDCBaseTest): > tgt = self._get_tgt(creds, remove_pac=True) > self._user2user(tgt, creds, expected_error=KDC_ERR_BADOPTION) > >+ # Test making a request with authdata and without a PAC. >+ def test_tgs_authdata_no_pac(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, remove_pac=True, allow_empty_authdata=True) >+ self._run_tgs(tgt, expected_error=KDC_ERR_BADOPTION) >+ >+ def test_renew_authdata_no_pac(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, renewable=True, remove_pac=True, >+ allow_empty_authdata=True) >+ self._renew_tgt(tgt, expected_error=KDC_ERR_BADOPTION) >+ >+ def test_validate_authdata_no_pac(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, invalid=True, remove_pac=True, >+ allow_empty_authdata=True) >+ self._validate_tgt(tgt, expected_error=KDC_ERR_BADOPTION) >+ >+ def test_s4u2self_authdata_no_pac(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, remove_pac=True, allow_empty_authdata=True) >+ self._s4u2self(tgt, creds, expected_error=KDC_ERR_BADOPTION) >+ >+ def test_user2user_authdata_no_pac(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, remove_pac=True, allow_empty_authdata=True) >+ self._user2user(tgt, creds, expected_error=KDC_ERR_BADOPTION) >+ > # Test changing the SID in the PAC to that of another account. > def test_tgs_sid_mismatch_existing(self): > creds = self._get_creds() >@@ -928,7 +956,8 @@ class KdcTgsTests(KDCBaseTest): > invalid=False, > from_rodc=False, > new_rid=None, >- remove_pac=False): >+ remove_pac=False, >+ allow_empty_authdata=False): > self.assertFalse(renewable and invalid) > > if remove_pac: >@@ -1011,6 +1040,7 @@ class KdcTgsTests(KDCBaseTest): > modify_fn=modify_fn, > modify_pac_fn=modify_pac_fn, > exclude_pac=remove_pac, >+ allow_empty_authdata=allow_empty_authdata, > update_pac_checksums=not remove_pac, > checksum_keys=checksum_keys) > >diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py >index 8e55790272a..b5ac393ea67 100644 >--- a/python/samba/tests/krb5/raw_testcase.py >+++ b/python/samba/tests/krb5/raw_testcase.py >@@ -3224,6 +3224,7 @@ class RawKerberosTest(TestCaseInTempDir): > modify_fn=None, > modify_pac_fn=None, > exclude_pac=False, >+ allow_empty_authdata=False, > update_pac_checksums=True, > checksum_keys=None, > include_checksums=None): >@@ -3332,8 +3333,10 @@ class RawKerberosTest(TestCaseInTempDir): > > # Replace the PAC in the authorization data and re-add it to the > # ticket enc-part. >- auth_data, _ = self.replace_pac(auth_data, new_pac, >- expect_pac=expect_pac) >+ auth_data, _ = self.replace_pac( >+ auth_data, new_pac, >+ expect_pac=expect_pac, >+ allow_empty_authdata=allow_empty_authdata) > enc_part['authorization-data'] = auth_data > > # Re-encrypt the ticket enc-part with the new key. >@@ -3454,7 +3457,8 @@ class RawKerberosTest(TestCaseInTempDir): > > kdc_checksum_buffer.info.signature = kdc_checksum > >- def replace_pac(self, auth_data, new_pac, expect_pac=True): >+ def replace_pac(self, auth_data, new_pac, expect_pac=True, >+ allow_empty_authdata=False): > if new_pac is not None: > self.assertElementEqual(new_pac, 'ad-type', AD_WIN2K_PAC) > self.assertElementPresent(new_pac, 'ad-data') >@@ -3483,7 +3487,7 @@ class RawKerberosTest(TestCaseInTempDir): > if expect_pac: > self.assertIsNotNone(old_pac, 'Expected PAC') > >- if relevant_elems: >+ if relevant_elems or allow_empty_authdata: > ad_relevant = self.der_encode( > relevant_elems, > asn1Spec=krb5_asn1.AD_IF_RELEVANT()) >@@ -3494,7 +3498,7 @@ class RawKerberosTest(TestCaseInTempDir): > else: > authdata_elem = None > >- if authdata_elem is not None: >+ if authdata_elem is not None or allow_empty_authdata: > new_auth_data.append(authdata_elem) > > if expect_pac: >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index a4ec57ea12c..69c7241b971 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -119,6 +119,7 @@ > # > # KDC TGT tests > # >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_allowed_denied > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_denied >@@ -130,6 +131,7 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_allowed_denied > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_denied >@@ -141,6 +143,7 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_allowed_denied > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_denied >@@ -152,6 +155,7 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_host > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_no_host > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac >@@ -169,6 +173,7 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_allowed_denied > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_denied >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index cdf35585aea..b5337da996e 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -374,6 +374,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > # > # KDC TGT tests > # >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_allowed_denied > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_denied >@@ -386,6 +387,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_req > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_allowed_denied >@@ -399,6 +401,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_allowed_denied > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_denied >@@ -411,6 +414,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_no_host > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_req >@@ -426,6 +430,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_allowed_denied > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_denied >-- >2.25.1 > > >From 9652053c30298043909e6db82ee93d48328643f7 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Thu, 21 Oct 2021 16:46:56 +1300 >Subject: [PATCH 046/190] CVE-2020-25721 tests/krb5: Add tests for extended > PAC_UPN_DNS_INFO PAC buffer > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14835 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 3 +- > python/samba/tests/krb5/kdc_tgs_tests.py | 51 +++++++++++++++++++++++- > python/samba/tests/krb5/raw_testcase.py | 41 +++++++++++++++++++ > python/samba/tests/krb5/s4u_tests.py | 2 + > selftest/knownfail_heimdal_kdc | 4 ++ > selftest/knownfail_mit_kdc | 4 ++ > 6 files changed, 103 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index 4fe7485c492..9be6cbab30b 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -1358,7 +1358,7 @@ class KDCBaseTest(RawKerberosTest): > > def get_tgt(self, creds, to_rodc=False, kdc_options=None, > expected_flags=None, unexpected_flags=None, >- expected_account_name=None, >+ expected_account_name=None, expected_upn_name=None, > expected_sid=None, > pac_request=True, expect_pac=True, fresh=False): > user_name = creds.get_username() >@@ -1410,6 +1410,7 @@ class KDCBaseTest(RawKerberosTest): > expected_srealm=realm, > expected_sname=sname, > expected_account_name=expected_account_name, >+ expected_upn_name=expected_upn_name, > expected_sid=expected_sid, > expected_salt=salt, > expected_flags=expected_flags, >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 5de79c30e1b..5313dbc6045 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -227,7 +227,10 @@ class KdcTgsTests(KDCBaseTest): > > def _make_tgs_request(self, client_creds, service_creds, tgt, > pac_request=None, expect_pac=True, >- expect_error=False): >+ expect_error=False, >+ expected_account_name=None, >+ expected_upn_name=None, >+ expected_sid=None): > client_account = client_creds.get_username() > cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, > names=[client_account]) >@@ -268,6 +271,9 @@ class KdcTgsTests(KDCBaseTest): > expected_cname=expected_cname, > expected_srealm=expected_srealm, > expected_sname=expected_sname, >+ expected_account_name=expected_account_name, >+ expected_upn_name=expected_upn_name, >+ expected_sid=expected_sid, > expected_supported_etypes=expected_supported_etypes, > ticket_decryption_key=target_decryption_key, > check_error_fn=check_error_fn, >@@ -433,6 +439,49 @@ class KdcTgsTests(KDCBaseTest): > self._make_tgs_request(client_creds, service_creds, tgt, > expect_pac=False, expect_error=True) > >+ def test_upn_dns_info_ex_user(self): >+ client_creds = self.get_client_creds() >+ self._run_upn_dns_info_ex_test(client_creds) >+ >+ def test_upn_dns_info_ex_mac(self): >+ mach_creds = self.get_mach_creds() >+ self._run_upn_dns_info_ex_test(mach_creds) >+ >+ def test_upn_dns_info_ex_upn_user(self): >+ client_creds = self.get_cached_creds( >+ account_type=self.AccountType.USER, >+ opts={'upn': 'upn_dns_info_test_upn0@bar'}) >+ self._run_upn_dns_info_ex_test(client_creds) >+ >+ def test_upn_dns_info_ex_upn_mac(self): >+ mach_creds = self.get_cached_creds( >+ account_type=self.AccountType.COMPUTER, >+ opts={'upn': 'upn_dns_info_test_upn1@bar'}) >+ self._run_upn_dns_info_ex_test(mach_creds) >+ >+ def _run_upn_dns_info_ex_test(self, client_creds): >+ service_creds = self.get_service_creds() >+ >+ samdb = self.get_samdb() >+ dn = client_creds.get_dn() >+ >+ account_name = client_creds.get_username() >+ upn_name = client_creds.get_upn() >+ if upn_name is None: >+ realm = client_creds.get_realm().lower() >+ upn_name = f'{account_name}@{realm}' >+ sid = self.get_objectSid(samdb, dn) >+ >+ tgt = self.get_tgt(client_creds, >+ expected_account_name=account_name, >+ expected_upn_name=upn_name, >+ expected_sid=sid) >+ >+ self._make_tgs_request(client_creds, service_creds, tgt, >+ expected_account_name=account_name, >+ expected_upn_name=upn_name, >+ expected_sid=sid) >+ > # Test making a TGS request. > def test_tgs_req(self): > creds = self._get_creds() >diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py >index b5ac393ea67..18ee8738eaa 100644 >--- a/python/samba/tests/krb5/raw_testcase.py >+++ b/python/samba/tests/krb5/raw_testcase.py >@@ -1986,6 +1986,7 @@ class RawKerberosTest(TestCaseInTempDir): > expected_srealm=None, > expected_sname=None, > expected_account_name=None, >+ expected_upn_name=None, > expected_sid=None, > expected_supported_etypes=None, > expected_flags=None, >@@ -2019,6 +2020,7 @@ class RawKerberosTest(TestCaseInTempDir): > expect_edata=None, > expect_pac=True, > expect_claims=True, >+ expect_upn_dns_info_ex=None, > to_rodc=False): > if expected_error_mode == 0: > expected_error_mode = () >@@ -2037,6 +2039,7 @@ class RawKerberosTest(TestCaseInTempDir): > 'expected_srealm': expected_srealm, > 'expected_sname': expected_sname, > 'expected_account_name': expected_account_name, >+ 'expected_upn_name': expected_upn_name, > 'expected_sid': expected_sid, > 'expected_supported_etypes': expected_supported_etypes, > 'expected_flags': expected_flags, >@@ -2070,6 +2073,7 @@ class RawKerberosTest(TestCaseInTempDir): > 'expect_edata': expect_edata, > 'expect_pac': expect_pac, > 'expect_claims': expect_claims, >+ 'expect_upn_dns_info_ex': expect_upn_dns_info_ex, > 'to_rodc': to_rodc > } > if callback_dict is None: >@@ -2084,6 +2088,7 @@ class RawKerberosTest(TestCaseInTempDir): > expected_srealm=None, > expected_sname=None, > expected_account_name=None, >+ expected_upn_name=None, > expected_sid=None, > expected_supported_etypes=None, > expected_flags=None, >@@ -2116,6 +2121,7 @@ class RawKerberosTest(TestCaseInTempDir): > expect_edata=None, > expect_pac=True, > expect_claims=True, >+ expect_upn_dns_info_ex=None, > expected_proxy_target=None, > expected_transited_services=None, > to_rodc=False): >@@ -2136,6 +2142,7 @@ class RawKerberosTest(TestCaseInTempDir): > 'expected_srealm': expected_srealm, > 'expected_sname': expected_sname, > 'expected_account_name': expected_account_name, >+ 'expected_upn_name': expected_upn_name, > 'expected_sid': expected_sid, > 'expected_supported_etypes': expected_supported_etypes, > 'expected_flags': expected_flags, >@@ -2168,6 +2175,7 @@ class RawKerberosTest(TestCaseInTempDir): > 'expect_edata': expect_edata, > 'expect_pac': expect_pac, > 'expect_claims': expect_claims, >+ 'expect_upn_dns_info_ex': expect_upn_dns_info_ex, > 'expected_proxy_target': expected_proxy_target, > 'expected_transited_services': expected_transited_services, > 'to_rodc': to_rodc >@@ -2584,6 +2592,12 @@ class RawKerberosTest(TestCaseInTempDir): > expected_account_name = kdc_exchange_dict['expected_account_name'] > expected_sid = kdc_exchange_dict['expected_sid'] > >+ expect_upn_dns_info_ex = kdc_exchange_dict['expect_upn_dns_info_ex'] >+ if expect_upn_dns_info_ex is None and ( >+ expected_account_name is not None >+ or expected_sid is not None): >+ expect_upn_dns_info_ex = True >+ > for pac_buffer in pac.buffers: > if pac_buffer.type == krb5pac.PAC_TYPE_CONSTRAINED_DELEGATION: > expected_proxy_target = kdc_exchange_dict[ >@@ -2618,6 +2632,31 @@ class RawKerberosTest(TestCaseInTempDir): > expected_rid = int(expected_sid.rsplit('-', 1)[1]) > self.assertEqual(expected_rid, logon_info.rid) > >+ elif pac_buffer.type == krb5pac.PAC_TYPE_UPN_DNS_INFO: >+ upn_dns_info = pac_buffer.info >+ upn_dns_info_ex = upn_dns_info.ex >+ >+ expected_realm = kdc_exchange_dict['expected_crealm'] >+ self.assertEqual(expected_realm, >+ upn_dns_info.dns_domain_name) >+ >+ expected_upn_name = kdc_exchange_dict['expected_upn_name'] >+ if expected_upn_name is not None: >+ self.assertEqual(expected_upn_name, >+ upn_dns_info.upn_name) >+ >+ if expect_upn_dns_info_ex: >+ self.assertIsNotNone(upn_dns_info_ex) >+ >+ if upn_dns_info_ex is not None: >+ if expected_account_name is not None: >+ self.assertEqual(expected_account_name, >+ upn_dns_info_ex.samaccountname) >+ >+ if expected_sid is not None: >+ self.assertEqual(expected_sid, >+ str(upn_dns_info_ex.objectsid)) >+ > def generic_check_kdc_error(self, > kdc_exchange_dict, > callback_dict, >@@ -3600,6 +3639,7 @@ class RawKerberosTest(TestCaseInTempDir): > padata, > kdc_options, > expected_account_name=None, >+ expected_upn_name=None, > expected_sid=None, > expected_flags=None, > unexpected_flags=None, >@@ -3634,6 +3674,7 @@ class RawKerberosTest(TestCaseInTempDir): > expected_srealm=expected_srealm, > expected_sname=expected_sname, > expected_account_name=expected_account_name, >+ expected_upn_name=expected_upn_name, > expected_sid=expected_sid, > expected_supported_etypes=expected_supported_etypes, > ticket_decryption_key=ticket_decryption_key, >diff --git a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py >index a80a7b3427e..5005affd6b3 100755 >--- a/python/samba/tests/krb5/s4u_tests.py >+++ b/python/samba/tests/krb5/s4u_tests.py >@@ -309,6 +309,7 @@ class S4UKerberosTests(KDCBaseTest): > tgt=service_tgt, > authenticator_subkey=authenticator_subkey, > kdc_options=str(kdc_options), >+ expect_upn_dns_info_ex=False, > expect_claims=False) > > self._generic_kdc_exchange(kdc_exchange_dict, >@@ -610,6 +611,7 @@ class S4UKerberosTests(KDCBaseTest): > kdc_options=kdc_options, > pac_options=pac_options, > expect_edata=expect_edata, >+ expect_upn_dns_info_ex=False, > expected_proxy_target=expected_proxy_target, > expected_transited_services=expected_transited_services, > expect_pac=expect_pac) >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 69c7241b971..342d69a6a03 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -155,6 +155,10 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_mac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_upn_mac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_upn_user >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_user > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_host > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_no_host >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index b5337da996e..ead0902b2d4 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -414,6 +414,10 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_mac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_upn_mac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_upn_user >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_user > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_no_host > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac >-- >2.25.1 > > >From d13dbff1f256314d13217dc6f4118e1078f8186a Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 22 Oct 2021 16:20:36 +0200 >Subject: [PATCH 047/190] CVE-2020-25719 CVE-2020-25717: selftest: remove > "gensec:require_pac" settings > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > selftest/selftest.pl | 2 -- > selftest/target/Samba4.pm | 2 -- > 2 files changed, 4 deletions(-) > >diff --git a/selftest/selftest.pl b/selftest/selftest.pl >index 9d4462323f5..75763ef3838 100755 >--- a/selftest/selftest.pl >+++ b/selftest/selftest.pl >@@ -586,8 +586,6 @@ sub write_clientconf($$$) > client min protocol = CORE > log level = $client_loglevel > torture:basedir = $clientdir >-#We don't want to pass our self-tests if the PAC code is wrong >- gensec:require_pac = true > #We don't want to run 'speed' tests for very long > torture:timelimit = 1 > winbind separator = / >diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm >index 4b302aa19de..aafe183dced 100755 >--- a/selftest/target/Samba4.pm >+++ b/selftest/target/Samba4.pm >@@ -785,8 +785,6 @@ sub provision_raw_step1($$) > notify:inotify = false > ldb:nosync = true > ldap server require strong auth = yes >-#We don't want to pass our self-tests if the PAC code is wrong >- gensec:require_pac = true > log file = $ctx->{logdir}/log.\%m > log level = $ctx->{server_loglevel} > lanman auth = Yes >-- >2.25.1 > > >From b8362aec700a01cc452026c699203d8a32b94e94 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 24 Aug 2021 17:11:24 +0200 >Subject: [PATCH 048/190] CVE-2020-25719 CVE-2020-25717: tests/krb5: Add tests > for connecting to services anonymously and without a PAC > >At the end of the patchset we assume NT_STATUS_NO_IMPERSONATION_TOKEN if >no PAC is available. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > python/samba/tests/krb5/test_ccache.py | 32 +++++++++--- > python/samba/tests/krb5/test_ldap.py | 70 ++++++++++++++++++++++---- > python/samba/tests/krb5/test_rpc.py | 46 ++++++++++++++--- > python/samba/tests/krb5/test_smb.py | 31 +++++++++--- > selftest/knownfail.d/no-pac | 4 ++ > source4/selftest/tests.py | 17 ++++--- > 6 files changed, 163 insertions(+), 37 deletions(-) > create mode 100644 selftest/knownfail.d/no-pac > >diff --git a/python/samba/tests/krb5/test_ccache.py b/python/samba/tests/krb5/test_ccache.py >index 040ae5cc9a1..4b016ed5816 100755 >--- a/python/samba/tests/krb5/test_ccache.py >+++ b/python/samba/tests/krb5/test_ccache.py >@@ -21,10 +21,11 @@ import sys > import os > > from ldb import SCOPE_SUBTREE >-from samba import gensec >+from samba import NTSTATUSError, gensec > from samba.auth import AuthContext > from samba.dcerpc import security > from samba.ndr import ndr_unpack >+from samba.ntstatus import NT_STATUS_NO_IMPERSONATION_TOKEN > > from samba.tests.krb5.kdc_base_test import KDCBaseTest > >@@ -41,11 +42,18 @@ class CcacheTests(KDCBaseTest): > """ > > def test_ccache(self): >+ self._run_ccache_test("ccacheusr") >+ >+ def test_ccache_no_pac(self): >+ self._run_ccache_test("ccacheusr_nopac", include_pac=False, >+ expect_anon=True, allow_error=True) >+ >+ def _run_ccache_test(self, user_name, include_pac=True, >+ expect_anon=False, allow_error=False): > # Create a user account and a machine account, along with a Kerberos > # credentials cache file where the service ticket authenticating the > # user are stored. > >- user_name = "ccacheusr" > mach_name = "ccachemac" > service = "host" > >@@ -67,12 +75,16 @@ class CcacheTests(KDCBaseTest): > # ticket, to ensure that the krbtgt ticket doesn't also need to be > # stored. > (creds, cachefile) = self.create_ccache_with_user(user_credentials, >- mach_credentials) >+ mach_credentials, >+ pac=include_pac) >+ # Remove the cached credentials file. >+ self.addCleanup(os.remove, cachefile.name) > > # Authenticate in-process to the machine account using the user's > # cached credentials. > > lp = self.get_lp() >+ lp.set('server role', 'active directory domain controller') > > settings = {} > settings["lp_ctx"] = lp >@@ -117,7 +129,16 @@ class CcacheTests(KDCBaseTest): > sid = ndr_unpack(security.dom_sid, ldb_res[0]["objectSid"][0]) > > # Retrieve the SIDs from the security token. >- session = gensec_server.session_info() >+ try: >+ session = gensec_server.session_info() >+ except NTSTATUSError as e: >+ if not allow_error: >+ self.fail() >+ >+ enum, estr = e.args >+ self.assertEqual(NT_STATUS_NO_IMPERSONATION_TOKEN, enum) >+ return >+ > token = session.security_token > token_sids = token.sids > self.assertGreater(len(token_sids), 0) >@@ -125,9 +146,6 @@ class CcacheTests(KDCBaseTest): > # Ensure that they match. > self.assertEqual(sid, token_sids[0]) > >- # Remove the cached credentials file. >- os.remove(cachefile.name) >- > > if __name__ == "__main__": > global_asn1_print = False >diff --git a/python/samba/tests/krb5/test_ldap.py b/python/samba/tests/krb5/test_ldap.py >index 7d9ffebe298..0205bdf6fb7 100755 >--- a/python/samba/tests/krb5/test_ldap.py >+++ b/python/samba/tests/krb5/test_ldap.py >@@ -20,10 +20,11 @@ > import sys > import os > >-from ldb import SCOPE_BASE, SCOPE_SUBTREE >+from ldb import LdbError, ERR_OPERATIONS_ERROR, SCOPE_BASE, SCOPE_SUBTREE > from samba.dcerpc import security > from samba.ndr import ndr_unpack > from samba.samdb import SamDB >+from samba import credentials > > from samba.tests.krb5.kdc_base_test import KDCBaseTest > >@@ -40,13 +41,20 @@ class LdapTests(KDCBaseTest): > """ > > def test_ldap(self): >+ self._run_ldap_test("ldapusr") >+ >+ def test_ldap_no_pac(self): >+ self._run_ldap_test("ldapusr_nopac", include_pac=False, >+ expect_anon=True, allow_error=True) >+ >+ def _run_ldap_test(self, user_name, include_pac=True, >+ expect_anon=False, allow_error=False): > # Create a user account and a machine account, along with a Kerberos > # credentials cache file where the service ticket authenticating the > # user are stored. > > samdb = self.get_samdb() > >- user_name = "ldapusr" > mach_name = samdb.host_dns_name() > service = "ldap" > >@@ -62,7 +70,10 @@ class LdapTests(KDCBaseTest): > (creds, cachefile) = self.create_ccache_with_user(user_credentials, > mach_credentials, > service, >- mach_name) >+ mach_name, >+ pac=include_pac) >+ # Remove the cached credentials file. >+ self.addCleanup(os.remove, cachefile.name) > > # Authenticate in-process to the machine account using the user's > # cached credentials. >@@ -74,22 +85,61 @@ class LdapTests(KDCBaseTest): > self.assertEqual(1, len(ldb_res)) > sid = ndr_unpack(security.dom_sid, ldb_res[0]["objectSid"][0]) > >+ # Connect to the machine account and retrieve the user SID. >+ try: >+ ldb_as_user = SamDB(url="ldap://%s" % mach_name, >+ credentials=creds, >+ lp=self.get_lp()) >+ except LdbError as e: >+ if not allow_error: >+ self.fail() >+ >+ enum, estr = e.args >+ self.assertEqual(ERR_OPERATIONS_ERROR, enum) >+ self.assertIn('NT_STATUS_NO_IMPERSONATION_TOKEN', estr) >+ return >+ >+ ldb_res = ldb_as_user.search('', >+ scope=SCOPE_BASE, >+ attrs=["tokenGroups"]) >+ self.assertEqual(1, len(ldb_res)) >+ >+ token_groups = ldb_res[0]["tokenGroups"] >+ token_sid = ndr_unpack(security.dom_sid, token_groups[0]) >+ >+ if expect_anon: >+ # Ensure we got an anonymous token. >+ self.assertEqual(security.SID_NT_ANONYMOUS, str(token_sid)) >+ token_sid = ndr_unpack(security.dom_sid, token_groups[1]) >+ self.assertEqual(security.SID_NT_NETWORK, str(token_sid)) >+ if len(token_groups) >= 3: >+ token_sid = ndr_unpack(security.dom_sid, token_groups[2]) >+ self.assertEqual(security.SID_NT_THIS_ORGANISATION, >+ str(token_sid)) >+ else: >+ # Ensure that they match. >+ self.assertEqual(sid, token_sid) >+ >+ def test_ldap_anonymous(self): >+ samdb = self.get_samdb() >+ mach_name = samdb.host_dns_name() >+ >+ anon_creds = credentials.Credentials() >+ anon_creds.set_anonymous() >+ > # Connect to the machine account and retrieve the user SID. > ldb_as_user = SamDB(url="ldap://%s" % mach_name, >- credentials=creds, >+ credentials=anon_creds, > lp=self.get_lp()) > ldb_res = ldb_as_user.search('', > scope=SCOPE_BASE, > attrs=["tokenGroups"]) > self.assertEqual(1, len(ldb_res)) > >+ # Ensure we got an anonymous token. > token_sid = ndr_unpack(security.dom_sid, ldb_res[0]["tokenGroups"][0]) >- >- # Ensure that they match. >- self.assertEqual(sid, token_sid) >- >- # Remove the cached credentials file. >- os.remove(cachefile.name) >+ self.assertEqual(security.SID_NT_ANONYMOUS, str(token_sid)) >+ self.assertEqual(len(ldb_res[0]["tokenGroups"]), 1) > > > if __name__ == "__main__": >diff --git a/python/samba/tests/krb5/test_rpc.py b/python/samba/tests/krb5/test_rpc.py >index ef8dd4dcbf5..e775257552e 100755 >--- a/python/samba/tests/krb5/test_rpc.py >+++ b/python/samba/tests/krb5/test_rpc.py >@@ -20,7 +20,9 @@ > import sys > import os > >+from samba import NTSTATUSError, credentials > from samba.dcerpc import lsa >+from samba.ntstatus import NT_STATUS_NO_IMPERSONATION_TOKEN > > from samba.tests.krb5.kdc_base_test import KDCBaseTest > >@@ -37,13 +39,20 @@ class RpcTests(KDCBaseTest): > """ > > def test_rpc(self): >+ self._run_rpc_test("rpcusr") >+ >+ def test_rpc_no_pac(self): >+ self._run_rpc_test("rpcusr_nopac", include_pac=False, >+ expect_anon=True, allow_error=True) >+ >+ def _run_rpc_test(self, user_name, include_pac=True, >+ expect_anon=False, allow_error=False): > # Create a user account and a machine account, along with a Kerberos > # credentials cache file where the service ticket authenticating the > # user are stored. > > samdb = self.get_samdb() > >- user_name = "rpcusr" > mach_name = samdb.host_dns_name() > service = "cifs" > >@@ -59,20 +68,45 @@ class RpcTests(KDCBaseTest): > (creds, cachefile) = self.create_ccache_with_user(user_credentials, > mach_credentials, > service, >- mach_name) >+ mach_name, >+ pac=include_pac) >+ # Remove the cached credentials file. >+ self.addCleanup(os.remove, cachefile.name) > > # Authenticate in-process to the machine account using the user's > # cached credentials. > > binding_str = "ncacn_np:%s[\\pipe\\lsarpc]" % mach_name >- conn = lsa.lsarpc(binding_str, self.get_lp(), creds) >+ try: >+ conn = lsa.lsarpc(binding_str, self.get_lp(), creds) >+ except NTSTATUSError as e: >+ if not allow_error: >+ self.fail() >+ >+ enum, estr = e.args >+ self.assertEqual(NT_STATUS_NO_IMPERSONATION_TOKEN, enum) >+ return > > (account_name, _) = conn.GetUserName(None, None, None) > >- self.assertEqual(user_name, account_name.string) >+ if expect_anon: >+ self.assertNotEqual(user_name, account_name.string) >+ else: >+ self.assertEqual(user_name, account_name.string) > >- # Remove the cached credentials file. >- os.remove(cachefile.name) >+ def test_rpc_anonymous(self): >+ samdb = self.get_samdb() >+ mach_name = samdb.host_dns_name() >+ >+ anon_creds = credentials.Credentials() >+ anon_creds.set_anonymous() >+ >+ binding_str = "ncacn_np:%s[\\pipe\\lsarpc]" % mach_name >+ conn = lsa.lsarpc(binding_str, self.get_lp(), anon_creds) >+ >+ (account_name, _) = conn.GetUserName(None, None, None) >+ >+ self.assertEqual('ANONYMOUS LOGON', account_name.string) > > > if __name__ == "__main__": >diff --git a/python/samba/tests/krb5/test_smb.py b/python/samba/tests/krb5/test_smb.py >index 1e70ed322bf..60f9a2da733 100755 >--- a/python/samba/tests/krb5/test_smb.py >+++ b/python/samba/tests/krb5/test_smb.py >@@ -21,8 +21,10 @@ import sys > import os > > from ldb import SCOPE_SUBTREE >+from samba import NTSTATUSError > from samba.dcerpc import security > from samba.ndr import ndr_unpack >+from samba.ntstatus import NT_STATUS_NO_IMPERSONATION_TOKEN > from samba.samba3 import libsmb_samba_internal as libsmb > from samba.samba3 import param as s3param > >@@ -41,13 +43,20 @@ class SmbTests(KDCBaseTest): > """ > > def test_smb(self): >+ self._run_smb_test("smbusr") >+ >+ def test_smb_no_pac(self): >+ self._run_smb_test("smbusr_nopac", include_pac=False, >+ expect_error=True) >+ >+ def _run_smb_test(self, user_name, include_pac=True, >+ expect_error=False): > # Create a user account and a machine account, along with a Kerberos > # credentials cache file where the service ticket authenticating the > # user are stored. > > samdb = self.get_samdb() > >- user_name = "smbusr" > mach_name = samdb.host_dns_name() > service = "cifs" > share = "tmp" >@@ -64,7 +73,10 @@ class SmbTests(KDCBaseTest): > (creds, cachefile) = self.create_ccache_with_user(user_credentials, > mach_credentials, > service, >- mach_name) >+ mach_name, >+ pac=include_pac) >+ # Remove the cached credentials file. >+ self.addCleanup(os.remove, cachefile.name) > > # Set the Kerberos 5 credentials cache environment variable. This is > # required because the codepath that gets run (gse_krb5) looks for it >@@ -95,16 +107,23 @@ class SmbTests(KDCBaseTest): > self.addCleanup(s3_lp.set, "client max protocol", max_protocol) > s3_lp.set("client max protocol", "NT1") > >- conn = libsmb.Conn(mach_name, share, lp=s3_lp, creds=creds) >+ try: >+ conn = libsmb.Conn(mach_name, share, lp=s3_lp, creds=creds) >+ except NTSTATUSError as e: >+ if not expect_error: >+ self.fail() >+ >+ enum, estr = e.args >+ self.assertEqual(NT_STATUS_NO_IMPERSONATION_TOKEN, enum) >+ return >+ else: >+ self.assertFalse(expect_error) > > (uid, gid, gids, sids, guest) = conn.posix_whoami() > > # Ensure that they match. > self.assertEqual(sid, sids[0]) > >- # Remove the cached credentials file. >- os.remove(cachefile.name) >- > > if __name__ == "__main__": > global_asn1_print = False >diff --git a/selftest/knownfail.d/no-pac b/selftest/knownfail.d/no-pac >new file mode 100644 >index 00000000000..9723d581c2a >--- /dev/null >+++ b/selftest/knownfail.d/no-pac >@@ -0,0 +1,4 @@ >+^samba.tests.krb5.test_ccache.samba.tests.krb5.test_ccache.CcacheTests.test_ccache_no_pac >+^samba.tests.krb5.test_ldap.samba.tests.krb5.test_ldap.LdapTests.test_ldap_no_pac >+^samba.tests.krb5.test_rpc.samba.tests.krb5.test_rpc.RpcTests.test_rpc_no_pac >+^samba.tests.krb5.test_smb.samba.tests.krb5.test_smb.SmbTests.test_smb_no_pac >diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py >index 41ae1e6219f..855f90e690b 100755 >--- a/source4/selftest/tests.py >+++ b/source4/selftest/tests.py >@@ -955,14 +955,15 @@ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ldap", > 'FAST_SUPPORT': have_fast_support, > 'TKT_SIG_SUPPORT': tkt_sig_support > }) >-planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_rpc", >- environ={ >- 'ADMIN_USERNAME': '$USERNAME', >- 'ADMIN_PASSWORD': '$PASSWORD', >- 'STRICT_CHECKING': '0', >- 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >- }) >+for env in ['ad_dc_default', 'ad_member']: >+ planoldpythontestsuite(env, "samba.tests.krb5.test_rpc", >+ environ={ >+ 'ADMIN_USERNAME': '$DC_USERNAME', >+ 'ADMIN_PASSWORD': '$DC_PASSWORD', >+ 'STRICT_CHECKING': '0', >+ 'FAST_SUPPORT': have_fast_support, >+ 'TKT_SIG_SUPPORT': tkt_sig_support >+ }) > planoldpythontestsuite("ad_dc_smb1", "samba.tests.krb5.test_smb", > environ={ > 'ADMIN_USERNAME': '$USERNAME', >-- >2.25.1 > > >From 3c9f3e8779c3350a81cda812f09511b014371660 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Mon, 4 Oct 2021 17:29:34 +0200 >Subject: [PATCH 049/190] CVE-2020-25717: s3:winbindd: make sure we default to > r->out.authoritative = true > >We need to make sure that temporary failures don't trigger a fallback >to the local SAM that silently ignores the domain name part for users. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/winbindd/winbindd_dual_srv.c | 7 +++++++ > source3/winbindd/winbindd_irpc.c | 7 +++++++ > source3/winbindd/winbindd_pam.c | 15 +++++++++++---- > source3/winbindd/winbindd_pam_auth_crap.c | 9 ++++++++- > source3/winbindd/winbindd_util.c | 7 +++++++ > 5 files changed, 40 insertions(+), 5 deletions(-) > >diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c >index 32d11e1fa57..0be5ae5554b 100644 >--- a/source3/winbindd/winbindd_dual_srv.c >+++ b/source3/winbindd/winbindd_dual_srv.c >@@ -941,6 +941,13 @@ NTSTATUS _winbind_SamLogon(struct pipes_struct *p, > union netr_Validation *validation = NULL; > bool interactive = false; > >+ /* >+ * Make sure we start with authoritative=true, >+ * it will only set to false if we don't know the >+ * domain. >+ */ >+ r->out.authoritative = true; >+ > domain = wb_child_domain(); > if (domain == NULL) { > return NT_STATUS_REQUEST_NOT_ACCEPTED; >diff --git a/source3/winbindd/winbindd_irpc.c b/source3/winbindd/winbindd_irpc.c >index e419736010b..918393c0827 100644 >--- a/source3/winbindd/winbindd_irpc.c >+++ b/source3/winbindd/winbindd_irpc.c >@@ -142,6 +142,13 @@ static NTSTATUS wb_irpc_SamLogon(struct irpc_message *msg, > const char *target_domain_name = NULL; > const char *account_name = NULL; > >+ /* >+ * Make sure we start with authoritative=true, >+ * it will only set to false if we don't know the >+ * domain. >+ */ >+ req->out.authoritative = true; >+ > switch (req->in.logon_level) { > case NetlogonInteractiveInformation: > case NetlogonServiceInformation: >diff --git a/source3/winbindd/winbindd_pam.c b/source3/winbindd/winbindd_pam.c >index ea315aecf6d..dfbaf52d482 100644 >--- a/source3/winbindd/winbindd_pam.c >+++ b/source3/winbindd/winbindd_pam.c >@@ -1866,7 +1866,7 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon( > { > fstring name_namespace, name_domain, name_user; > NTSTATUS result; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uint32_t flags = 0; > uint16_t validation_level = 0; > union netr_Validation *validation = NULL; >@@ -2435,6 +2435,13 @@ done: > result = NT_STATUS_NO_LOGON_SERVERS; > } > >+ /* >+ * Here we don't alter >+ * state->response->data.auth.authoritative based >+ * on the servers response >+ * as we don't want a fallback to the local sam >+ * for interactive PAM logons >+ */ > set_auth_errors(state->response, result); > > DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Plain-text authentication for user %s returned %s (PAM: %d)\n", >@@ -2649,7 +2656,7 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain, > const char *name_domain = NULL; > const char *workstation; > uint64_t logon_id = 0; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uint32_t flags = 0; > uint16_t validation_level; > union netr_Validation *validation = NULL; >@@ -2722,7 +2729,6 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain, > &validation_level, > &validation); > if (!NT_STATUS_IS_OK(result)) { >- state->response->data.auth.authoritative = authoritative; > goto done; > } > >@@ -2754,7 +2760,6 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain, > "from firewalled domain [%s]\n", > info3->base.account_name.string, > info3->base.logon_domain.string); >- state->response->data.auth.authoritative = true; > result = NT_STATUS_AUTHENTICATION_FIREWALL_FAILED; > goto done; > } >@@ -2776,6 +2781,8 @@ done: > } > > set_auth_errors(state->response, result); >+ state->response->data.auth.authoritative = authoritative; >+ > /* > * Log the winbind pam authentication, the logon_id will tie this to > * any of the logons invoked from this request. >diff --git a/source3/winbindd/winbindd_pam_auth_crap.c b/source3/winbindd/winbindd_pam_auth_crap.c >index dacb6566be6..a6f13806df9 100644 >--- a/source3/winbindd/winbindd_pam_auth_crap.c >+++ b/source3/winbindd/winbindd_pam_auth_crap.c >@@ -26,6 +26,7 @@ > > struct winbindd_pam_auth_crap_state { > struct winbindd_response *response; >+ bool authoritative; > uint32_t flags; > }; > >@@ -47,7 +48,7 @@ struct tevent_req *winbindd_pam_auth_crap_send( > if (req == NULL) { > return NULL; > } >- >+ state->authoritative = true; > state->flags = request->flags; > > if (state->flags & WBFLAG_PAM_AUTH_PAC) { >@@ -126,6 +127,11 @@ struct tevent_req *winbindd_pam_auth_crap_send( > > domain = find_auth_domain(request->flags, auth_domain); > if (domain == NULL) { >+ /* >+ * We don't know the domain so >+ * we're not authoritative >+ */ >+ state->authoritative = false; > tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER); > return tevent_req_post(req, ev); > } >@@ -186,6 +192,7 @@ NTSTATUS winbindd_pam_auth_crap_recv(struct tevent_req *req, > > if (tevent_req_is_nterror(req, &status)) { > set_auth_errors(response, status); >+ response->data.auth.authoritative = state->authoritative; > return status; > } > >diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c >index 90fb4fde8f0..6e3739f68c1 100644 >--- a/source3/winbindd/winbindd_util.c >+++ b/source3/winbindd/winbindd_util.c >@@ -2155,6 +2155,13 @@ void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain) > > void set_auth_errors(struct winbindd_response *resp, NTSTATUS result) > { >+ /* >+ * Make sure we start with authoritative=true, >+ * it will only set to false if we don't know the >+ * domain. >+ */ >+ resp->data.auth.authoritative = true; >+ > resp->data.auth.nt_status = NT_STATUS_V(result); > fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result)); > >-- >2.25.1 > > >From d80d31df5befa3a69ba235e1dbb87031818aa366 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Mon, 4 Oct 2021 17:29:34 +0200 >Subject: [PATCH 050/190] CVE-2020-25717: s4:auth/ntlm: make sure > auth_check_password() defaults to r->out.authoritative = true > >We need to make sure that temporary failures don't trigger a fallback >to the local SAM that silently ignores the domain name part for users. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source4/auth/ntlm/auth.c | 5 +++++ > 1 file changed, 5 insertions(+) > >diff --git a/source4/auth/ntlm/auth.c b/source4/auth/ntlm/auth.c >index e54eb7719f5..4c66f2c23cb 100644 >--- a/source4/auth/ntlm/auth.c >+++ b/source4/auth/ntlm/auth.c >@@ -169,6 +169,11 @@ _PUBLIC_ NTSTATUS auth_check_password(struct auth4_context *auth_ctx, > /*TODO: create a new event context here! */ > ev = auth_ctx->event_ctx; > >+ /* >+ * We are authoritative by default >+ */ >+ *pauthoritative = 1; >+ > subreq = auth_check_password_send(mem_ctx, > ev, > auth_ctx, >-- >2.25.1 > > >From b831a45252902939b11f367bc06eb9223e23f277 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 26 Oct 2021 17:42:41 +0200 >Subject: [PATCH 051/190] CVE-2020-25717: s4:torture: start with authoritative > = 1 > >This is not strictly needed, but makes it easier to audit >that we don't miss important places. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source4/torture/rpc/samlogon.c | 4 ++-- > source4/torture/rpc/schannel.c | 2 +- > 2 files changed, 3 insertions(+), 3 deletions(-) > >diff --git a/source4/torture/rpc/samlogon.c b/source4/torture/rpc/samlogon.c >index 8c2a97cf205..911407b16c5 100644 >--- a/source4/torture/rpc/samlogon.c >+++ b/source4/torture/rpc/samlogon.c >@@ -1407,7 +1407,7 @@ static bool test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, > > union netr_LogonLevel logon; > union netr_Validation validation; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uint32_t flags = 0; > > ZERO_STRUCT(logon); >@@ -1520,7 +1520,7 @@ bool test_InteractiveLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, > > union netr_LogonLevel logon; > union netr_Validation validation; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > struct dcerpc_binding_handle *b = p->binding_handle; > > ZERO_STRUCT(a); >diff --git a/source4/torture/rpc/schannel.c b/source4/torture/rpc/schannel.c >index 6784e56991c..85807e6aeab 100644 >--- a/source4/torture/rpc/schannel.c >+++ b/source4/torture/rpc/schannel.c >@@ -50,7 +50,7 @@ bool test_netlogon_ex_ops(struct dcerpc_pipe *p, struct torture_context *tctx, > struct netr_NetworkInfo ninfo; > union netr_LogonLevel logon; > union netr_Validation validation; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uint32_t _flags = 0; > DATA_BLOB names_blob, chal, lm_resp, nt_resp; > int i; >-- >2.25.1 > > >From 3b965ecba5a4b37fc0c6560aab1947f9aec2452a Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 26 Oct 2021 17:42:41 +0200 >Subject: [PATCH 052/190] CVE-2020-25717: s4:smb_server: start with > authoritative = 1 > >This is not strictly needed, but makes it easier to audit >that we don't miss important places. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source4/smb_server/smb/sesssetup.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > >diff --git a/source4/smb_server/smb/sesssetup.c b/source4/smb_server/smb/sesssetup.c >index 68cdd70feff..8428ca3fabb 100644 >--- a/source4/smb_server/smb/sesssetup.c >+++ b/source4/smb_server/smb/sesssetup.c >@@ -102,7 +102,7 @@ static void sesssetup_old_send(struct tevent_req *subreq) > struct auth_session_info *session_info; > struct smbsrv_session *smb_sess; > NTSTATUS status; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uint32_t flags; > > status = auth_check_password_recv(subreq, req, &user_info_dc, >@@ -243,7 +243,7 @@ static void sesssetup_nt1_send(struct tevent_req *subreq) > struct auth_user_info_dc *user_info_dc = NULL; > struct auth_session_info *session_info; > struct smbsrv_session *smb_sess; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uint32_t flags; > NTSTATUS status; > >-- >2.25.1 > > >From 3bef6f99b750b25ed475816275d862b3c24de37a Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 26 Oct 2021 17:42:41 +0200 >Subject: [PATCH 053/190] CVE-2020-25717: s4:auth_simple: start with > authoritative = 1 > >This is not strictly needed, but makes it easier to audit >that we don't miss important places. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source4/auth/ntlm/auth_simple.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > >diff --git a/source4/auth/ntlm/auth_simple.c b/source4/auth/ntlm/auth_simple.c >index 8df160cefc3..8301aec519c 100644 >--- a/source4/auth/ntlm/auth_simple.c >+++ b/source4/auth/ntlm/auth_simple.c >@@ -150,7 +150,7 @@ static void authenticate_ldap_simple_bind_done(struct tevent_req *subreq) > const struct tsocket_address *local_address = user_info->local_host; > const char *transport_protection = AUTHZ_TRANSPORT_PROTECTION_NONE; > struct auth_user_info_dc *user_info_dc = NULL; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uint32_t flags = 0; > NTSTATUS nt_status; > >-- >2.25.1 > > >From 34489dca910a5a0f146c77c2f6ecb26fc7f60c43 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 26 Oct 2021 17:42:41 +0200 >Subject: [PATCH 054/190] CVE-2020-25717: s3:ntlm_auth: start with > authoritative = 1 > >This is not strictly needed, but makes it easier to audit >that we don't miss important places. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/utils/ntlm_auth.c | 4 ++-- > source3/utils/ntlm_auth_diagnostics.c | 10 +++++----- > 2 files changed, 7 insertions(+), 7 deletions(-) > >diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c >index f887d6814d0..df1a87c8b55 100644 >--- a/source3/utils/ntlm_auth.c >+++ b/source3/utils/ntlm_auth.c >@@ -1931,7 +1931,7 @@ static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mod > TALLOC_FREE(mem_ctx); > > } else { >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > > if (!domain) { > domain = smb_xstrdup(get_winbind_domain()); >@@ -2447,7 +2447,7 @@ static bool check_auth_crap(void) > char *hex_lm_key; > char *hex_user_session_key; > char *error_string; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > > setbuf(stdout, NULL); > >diff --git a/source3/utils/ntlm_auth_diagnostics.c b/source3/utils/ntlm_auth_diagnostics.c >index 41591a8de33..fc0fc19bacb 100644 >--- a/source3/utils/ntlm_auth_diagnostics.c >+++ b/source3/utils/ntlm_auth_diagnostics.c >@@ -54,7 +54,7 @@ static bool test_lm_ntlm_broken(enum ntlm_break break_which) > DATA_BLOB lm_response = data_blob(NULL, 24); > DATA_BLOB nt_response = data_blob(NULL, 24); > DATA_BLOB session_key = data_blob(NULL, 16); >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uchar lm_key[8]; > uchar user_session_key[16]; > uchar lm_hash[16]; >@@ -177,7 +177,7 @@ static bool test_ntlm_in_lm(void) > NTSTATUS nt_status; > uint32_t flags = 0; > DATA_BLOB nt_response = data_blob(NULL, 24); >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uchar lm_key[8]; > uchar lm_hash[16]; > uchar user_session_key[16]; >@@ -245,7 +245,7 @@ static bool test_ntlm_in_both(void) > uint32_t flags = 0; > DATA_BLOB nt_response = data_blob(NULL, 24); > DATA_BLOB session_key = data_blob(NULL, 16); >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uint8_t lm_key[8]; > uint8_t lm_hash[16]; > uint8_t user_session_key[16]; >@@ -322,7 +322,7 @@ static bool test_lmv2_ntlmv2_broken(enum ntlm_break break_which) > DATA_BLOB lmv2_response = data_blob_null; > DATA_BLOB ntlmv2_session_key = data_blob_null; > DATA_BLOB names_blob = NTLMv2_generate_names_blob(NULL, get_winbind_netbios_name(), get_winbind_domain()); >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uchar user_session_key[16]; > DATA_BLOB chall = get_challenge(); > char *error_string; >@@ -452,7 +452,7 @@ static bool test_plaintext(enum ntlm_break break_which) > char *password; > smb_ucs2_t *nt_response_ucs2; > size_t converted_size; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uchar user_session_key[16]; > uchar lm_key[16]; > static const uchar zeros[8] = { 0, }; >-- >2.25.1 > > >From a86bd7b1ad49d4e848f5ad266280a0e7d325b194 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 26 Oct 2021 17:42:41 +0200 >Subject: [PATCH 055/190] CVE-2020-25717: s3:torture: start with authoritative > = 1 > >This is not strictly needed, but makes it easier to audit >that we don't miss important places. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/torture/pdbtest.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > >diff --git a/source3/torture/pdbtest.c b/source3/torture/pdbtest.c >index 146320ff453..67a1aa0b2d5 100644 >--- a/source3/torture/pdbtest.c >+++ b/source3/torture/pdbtest.c >@@ -277,7 +277,7 @@ static bool test_auth(TALLOC_CTX *mem_ctx, struct samu *pdb_entry) > struct netr_SamInfo6 *info6_wbc = NULL; > NTSTATUS status; > bool ok; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > int rc; > > rc = SMBOWFencrypt(pdb_get_nt_passwd(pdb_entry), challenge_8, >-- >2.25.1 > > >From 177c4fc1966cd963540ac94f1864c6663c266d2f Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 26 Oct 2021 17:42:41 +0200 >Subject: [PATCH 056/190] CVE-2020-25717: s3:rpcclient: start with > authoritative = 1 > >This is not strictly needed, but makes it easier to audit >that we don't miss important places. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/rpcclient/cmd_netlogon.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > >diff --git a/source3/rpcclient/cmd_netlogon.c b/source3/rpcclient/cmd_netlogon.c >index 18e1862ebaa..bb5b9dafed4 100644 >--- a/source3/rpcclient/cmd_netlogon.c >+++ b/source3/rpcclient/cmd_netlogon.c >@@ -497,7 +497,7 @@ static NTSTATUS cmd_netlogon_sam_logon(struct rpc_pipe_client *cli, > uint32_t logon_param = 0; > const char *workstation = NULL; > struct netr_SamInfo3 *info3 = NULL; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > uint32_t flags = 0; > uint16_t validation_level; > union netr_Validation *validation = NULL; >-- >2.25.1 > > >From 914018c54dbc1cd0b279d4bf4bbe97427615193f Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 26 Oct 2021 17:42:41 +0200 >Subject: [PATCH 057/190] CVE-2020-25717: s3:auth: start with authoritative = 1 > >This is not strictly needed, but makes it easier to audit >that we don't miss important places. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/auth/auth_generic.c | 2 +- > source3/auth/auth_samba4.c | 2 +- > 2 files changed, 2 insertions(+), 2 deletions(-) > >diff --git a/source3/auth/auth_generic.c b/source3/auth/auth_generic.c >index 0e9245fc23d..0bd81b25cd4 100644 >--- a/source3/auth/auth_generic.c >+++ b/source3/auth/auth_generic.c >@@ -418,7 +418,7 @@ NTSTATUS auth_check_password_session_info(struct auth4_context *auth_context, > { > NTSTATUS nt_status; > void *server_info; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > struct tevent_context *ev = NULL; > struct tevent_req *subreq = NULL; > bool ok; >diff --git a/source3/auth/auth_samba4.c b/source3/auth/auth_samba4.c >index 770e6a33190..ff8dc94d296 100644 >--- a/source3/auth/auth_samba4.c >+++ b/source3/auth/auth_samba4.c >@@ -120,7 +120,7 @@ static NTSTATUS check_samba4_security( > NTSTATUS nt_status; > struct auth_user_info_dc *user_info_dc; > struct auth4_context *auth4_context; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > struct auth_serversupplied_info *server_info = NULL; > > nt_status = make_auth4_context_s4(auth_context, mem_ctx, &auth4_context); >-- >2.25.1 > > >From 0663bb79beba81bb9c443fc427468f65bdd740d5 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 26 Oct 2021 17:42:41 +0200 >Subject: [PATCH 058/190] CVE-2020-25717: auth/ntlmssp: start with > authoritative = 1 > >This is not strictly needed, but makes it easier to audit >that we don't miss important places. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > auth/ntlmssp/ntlmssp_server.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > >diff --git a/auth/ntlmssp/ntlmssp_server.c b/auth/ntlmssp/ntlmssp_server.c >index 001238278d7..939aa0ef4aa 100644 >--- a/auth/ntlmssp/ntlmssp_server.c >+++ b/auth/ntlmssp/ntlmssp_server.c >@@ -799,7 +799,7 @@ static void ntlmssp_server_auth_done(struct tevent_req *subreq) > struct gensec_security *gensec_security = state->gensec_security; > struct gensec_ntlmssp_context *gensec_ntlmssp = state->gensec_ntlmssp; > struct auth4_context *auth_context = gensec_security->auth_context; >- uint8_t authoritative = 0; >+ uint8_t authoritative = 1; > NTSTATUS status; > > status = auth_context->check_ntlm_password_recv(subreq, >-- >2.25.1 > > >From 6da6847b8a1dcd74e92f776185c53b4207b1f7a2 Mon Sep 17 00:00:00 2001 >From: Samuel Cabrero <scabrero@samba.org> >Date: Tue, 28 Sep 2021 10:43:40 +0200 >Subject: [PATCH 059/190] CVE-2020-25717: loadparm: Add new parameter "min > domain uid" > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Samuel Cabrero <scabrero@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > docs-xml/smbdotconf/security/mindomainuid.xml | 17 +++++++++++++++++ > docs-xml/smbdotconf/winbind/idmapconfig.xml | 4 ++++ > lib/param/loadparm.c | 4 ++++ > source3/param/loadparm.c | 2 ++ > 4 files changed, 27 insertions(+) > create mode 100644 docs-xml/smbdotconf/security/mindomainuid.xml > >diff --git a/docs-xml/smbdotconf/security/mindomainuid.xml b/docs-xml/smbdotconf/security/mindomainuid.xml >new file mode 100644 >index 00000000000..46ae795d730 >--- /dev/null >+++ b/docs-xml/smbdotconf/security/mindomainuid.xml >@@ -0,0 +1,17 @@ >+<samba:parameter name="min domain uid" >+ type="integer" >+ context="G" >+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc"> >+<description> >+ <para> >+ The integer parameter specifies the minimum uid allowed when mapping a >+ local account to a domain account. >+ </para> >+ >+ <para> >+ Note that this option interacts with the configured <emphasis>idmap ranges</emphasis>! >+ </para> >+</description> >+ >+<value type="default">1000</value> >+</samba:parameter> >diff --git a/docs-xml/smbdotconf/winbind/idmapconfig.xml b/docs-xml/smbdotconf/winbind/idmapconfig.xml >index 1374040fb29..f70f11df757 100644 >--- a/docs-xml/smbdotconf/winbind/idmapconfig.xml >+++ b/docs-xml/smbdotconf/winbind/idmapconfig.xml >@@ -80,6 +80,9 @@ > authoritative for a unix ID to SID mapping, so it must be set > for each individually configured domain and for the default > configuration. The configured ranges must be mutually disjoint. >+ </para> >+ <para> >+ Note that the low value interacts with the <smbconfoption name="min domain uid"/> option! > </para></listitem> > </varlistentry> > >@@ -115,4 +118,5 @@ > </programlisting> > > </description> >+<related>min domain uid</related> > </samba:parameter> >diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c >index 2eac1ba7c38..95373497695 100644 >--- a/lib/param/loadparm.c >+++ b/lib/param/loadparm.c >@@ -2994,6 +2994,10 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx) > "server smb3 encryption algorithms", > DEFAULT_SMB3_ENCRYPTION_ALGORITHMS); > >+ lpcfg_do_global_parameter(lp_ctx, >+ "min domain uid", >+ "1000"); >+ > for (i = 0; parm_table[i].label; i++) { > if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) { > lp_ctx->flags[i] |= FLAG_DEFAULT; >diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c >index 6c9830563c3..e98d1738bb4 100644 >--- a/source3/param/loadparm.c >+++ b/source3/param/loadparm.c >@@ -976,6 +976,8 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals) > Globals.server_smb3_encryption_algorithms = > str_list_make_v3_const(NULL, DEFAULT_SMB3_ENCRYPTION_ALGORITHMS, NULL); > >+ Globals.min_domain_uid = 1000; >+ > /* Now put back the settings that were set with lp_set_cmdline() */ > apply_lp_set_cmdline(); > } >-- >2.25.1 > > >From 5f0e62fa1e9460bad8824c19a9eeff8354502247 Mon Sep 17 00:00:00 2001 >From: Samuel Cabrero <scabrero@samba.org> >Date: Tue, 5 Oct 2021 12:31:29 +0200 >Subject: [PATCH 060/190] CVE-2020-25717: selftest: Add ad_member_no_nss_wb > environment > >This environment creates an AD member that doesn't have >'nss_winbind' configured, while winbindd is still started. > >For testing we map a DOMAIN\root user to the local root >account and unix token of the local root user. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Samuel Cabrero <scabrero@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > selftest/target/Samba.pm | 1 + > selftest/target/Samba3.pm | 63 ++++++++++++++++++++++++++++++++++++++- > 2 files changed, 63 insertions(+), 1 deletion(-) > >diff --git a/selftest/target/Samba.pm b/selftest/target/Samba.pm >index 10353008b88..69e6dcee591 100644 >--- a/selftest/target/Samba.pm >+++ b/selftest/target/Samba.pm >@@ -610,6 +610,7 @@ sub get_interface($) > fipsadmember => 57, > offlineadmem => 58, > s2kmember => 59, >+ admemnonsswb => 60, > > rootdnsforwarder => 64, > >diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm >index fdbba8411bc..fb84e4de5b9 100755 >--- a/selftest/target/Samba3.pm >+++ b/selftest/target/Samba3.pm >@@ -240,6 +240,7 @@ sub check_env($$) > ad_member_fips => ["ad_dc_fips"], > ad_member_offlogon => ["ad_dc"], > ad_member_oneway => ["fl2000dc"], >+ ad_member_no_nss_wb => ["ad_dc"], > > clusteredmember => ["nt4_dc"], > ); >@@ -653,8 +654,15 @@ sub provision_ad_member > $dcvars, > $trustvars_f, > $trustvars_e, >+ $extra_member_options, > $force_fips_mode, >- $offline_logon) = @_; >+ $offline_logon, >+ $no_nss_winbind) = @_; >+ >+ if (defined($offline_logon) && defined($no_nss_winbind)) { >+ warn ("Offline logon incompatible with no nss winbind\n"); >+ return undef; >+ } > > my $prefix_abs = abs_path($prefix); > my @dirs = (); >@@ -696,6 +704,10 @@ sub provision_ad_member > $netbios_aliases = "netbios aliases = foo bar"; > } > >+ unless (defined($extra_member_options)) { >+ $extra_member_options = ""; >+ } >+ > my $member_options = " > security = ads > workgroup = $dcvars->{DOMAIN} >@@ -719,6 +731,10 @@ sub provision_ad_member > rpc_daemon:epmd = fork > rpc_daemon:lsasd = fork > >+ # Begin extra member options >+ $extra_member_options >+ # End extra member options >+ > [sub_dug] > path = $share_dir/D_%D/U_%U/G_%G > writeable = yes >@@ -920,6 +936,11 @@ sub provision_ad_member > $ENV{SOCKET_WRAPPER_DIR} = $swrap_env; > > } else { >+ if (defined($no_nss_winbind)) { >+ $ret->{NSS_WRAPPER_MODULE_SO_PATH} = ""; >+ $ret->{NSS_WRAPPER_MODULE_FN_PREFIX} = ""; >+ } >+ > if (not $self->check_or_start( > env_vars => $ret, > nmbd => "yes", >@@ -1398,6 +1419,7 @@ sub setup_ad_member_fips > $dcvars, > $trustvars_f, > $trustvars_e, >+ undef, > 1); > } > >@@ -1422,9 +1444,48 @@ sub setup_ad_member_offlogon > $trustvars_f, > $trustvars_e, > undef, >+ undef, > 1); > } > >+sub setup_ad_member_no_nss_wb >+{ >+ my ($self, >+ $prefix, >+ $dcvars, >+ $trustvars_f, >+ $trustvars_e) = @_; >+ >+ # If we didn't build with ADS, pretend this env was never available >+ if (not $self->have_ads()) { >+ return "UNKNOWN"; >+ } >+ >+ print "PROVISIONING AD MEMBER WITHOUT NSS WINBIND..."; >+ >+ my $extra_member_options = " >+ username map = $prefix/lib/username.map >+"; >+ >+ my $ret = $self->provision_ad_member($prefix, >+ "ADMEMNONSSWB", >+ $dcvars, >+ $trustvars_f, >+ $trustvars_e, >+ $extra_member_options, >+ undef, >+ undef, >+ 1); >+ >+ open(USERMAP, ">$prefix/lib/username.map") or die("Unable to open $prefix/lib/username.map"); >+ print USERMAP " >+root = $dcvars->{DOMAIN}/root >+"; >+ close(USERMAP); >+ >+ return $ret; >+} >+ > sub setup_simpleserver > { > my ($self, $path) = @_; >-- >2.25.1 > > >From 8060df5a8774d1cbf76b3ee32b66a88239e45865 Mon Sep 17 00:00:00 2001 >From: Samuel Cabrero <scabrero@samba.org> >Date: Tue, 5 Oct 2021 16:56:06 +0200 >Subject: [PATCH 061/190] CVE-2020-25717: selftest: Add a test for the new 'min > domain uid' parameter > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Samuel Cabrero <scabrero@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > .../samba/tests/krb5/test_min_domain_uid.py | 121 ++++++++++++++++++ > python/samba/tests/usage.py | 1 + > selftest/knownfail.d/min_domain_uid | 1 + > source4/selftest/tests.py | 7 + > 4 files changed, 130 insertions(+) > create mode 100755 python/samba/tests/krb5/test_min_domain_uid.py > create mode 100644 selftest/knownfail.d/min_domain_uid > >diff --git a/python/samba/tests/krb5/test_min_domain_uid.py b/python/samba/tests/krb5/test_min_domain_uid.py >new file mode 100755 >index 00000000000..77414b239f0 >--- /dev/null >+++ b/python/samba/tests/krb5/test_min_domain_uid.py >@@ -0,0 +1,121 @@ >+#!/usr/bin/env python3 >+# Unix SMB/CIFS implementation. >+# Copyright (C) Samuel Cabrero 2021 >+# >+# 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 >+import pwd >+import ctypes >+ >+from samba.tests import env_get_var_value >+from samba.samba3 import libsmb_samba_internal as libsmb >+from samba.samba3 import param as s3param >+from samba import NTSTATUSError, ntstatus >+ >+from samba.tests.krb5.kdc_base_test import KDCBaseTest >+from samba.credentials import MUST_USE_KERBEROS, DONT_USE_KERBEROS >+ >+sys.path.insert(0, "bin/python") >+os.environ["PYTHONUNBUFFERED"] = "1" >+ >+class SmbMinDomainUid(KDCBaseTest): >+ """Test for SMB authorization without NSS winbind. In such setup domain >+ accounts are mapped to local accounts using the 'username map' option. >+ """ >+ >+ def setUp(self): >+ super(KDCBaseTest, self).setUp() >+ >+ # Create a user account, along with a Kerberos credentials cache file >+ # where the service ticket authenticating the user are stored. >+ self.samdb = self.get_samdb() >+ >+ self.mach_name = env_get_var_value('SERVER') >+ self.user_name = "root" >+ self.service = "cifs" >+ self.share = "tmp" >+ >+ # Create the user account. >+ (self.user_creds, _) = self.create_account(self.samdb, self.user_name) >+ >+ # Build the global inject file path >+ server_conf = env_get_var_value('SMB_CONF_PATH') >+ server_conf_dir = os.path.dirname(server_conf) >+ self.global_inject = os.path.join(server_conf_dir, "global_inject.conf") >+ >+ def _test_min_uid(self, creds): >+ # Assert unix root uid is less than 'idmap config ADDOMAIN' minimum >+ s3_lp = s3param.get_context() >+ s3_lp.load(self.get_lp().configfile) >+ >+ domain_range = s3_lp.get("idmap config * : range").split('-') >+ domain_range_low = int(domain_range[0]) >+ unix_root_pw = pwd.getpwnam(self.user_name) >+ self.assertLess(unix_root_pw.pw_uid, domain_range_low) >+ self.assertLess(unix_root_pw.pw_gid, domain_range_low) >+ >+ conn = libsmb.Conn(self.mach_name, self.share, lp=s3_lp, creds=creds) >+ # Disconnect >+ conn = None >+ >+ # Restrict access to local root account uid >+ with open(self.global_inject, 'w') as f: >+ f.write("min domain uid = %s\n" % (unix_root_pw.pw_uid + 1)) >+ >+ with self.assertRaises(NTSTATUSError) as cm: >+ conn = libsmb.Conn(self.mach_name, >+ self.share, >+ lp=s3_lp, >+ creds=creds) >+ code = ctypes.c_uint32(cm.exception.args[0]).value >+ self.assertEqual(code, ntstatus.NT_STATUS_INVALID_TOKEN) >+ >+ # check that the local root account uid is now allowed >+ with open(self.global_inject, 'w') as f: >+ f.write("min domain uid = %s\n" % unix_root_pw.pw_uid) >+ >+ conn = libsmb.Conn(self.mach_name, self.share, lp=s3_lp, creds=creds) >+ # Disconnect >+ conn = None >+ >+ with open(self.global_inject, 'w') as f: >+ f.truncate() >+ >+ def test_min_domain_uid_krb5(self): >+ krb5_state = self.user_creds.get_kerberos_state() >+ self.user_creds.set_kerberos_state(MUST_USE_KERBEROS) >+ ret = self._test_min_uid(self.user_creds) >+ self.user_creds.set_kerberos_state(krb5_state) >+ return ret >+ >+ def test_min_domain_uid_ntlmssp(self): >+ krb5_state = self.user_creds.get_kerberos_state() >+ self.user_creds.set_kerberos_state(DONT_USE_KERBEROS) >+ ret = self._test_min_uid(self.user_creds) >+ self.user_creds.set_kerberos_state(krb5_state) >+ return ret >+ >+ def tearDown(self): >+ # Ensure no leftovers in global inject file >+ with open(self.global_inject, 'w') as f: >+ f.truncate() >+ >+ super(KDCBaseTest, self).tearDown() >+ >+if __name__ == "__main__": >+ import unittest >+ unittest.main() >diff --git a/python/samba/tests/usage.py b/python/samba/tests/usage.py >index aed012cf767..42aa1afc998 100644 >--- a/python/samba/tests/usage.py >+++ b/python/samba/tests/usage.py >@@ -107,6 +107,7 @@ EXCLUDE_USAGE = { > 'python/samba/tests/krb5/salt_tests.py', > 'python/samba/tests/krb5/spn_tests.py', > 'python/samba/tests/krb5/alias_tests.py', >+ 'python/samba/tests/krb5/test_min_domain_uid.py', > } > > EXCLUDE_HELP = { >diff --git a/selftest/knownfail.d/min_domain_uid b/selftest/knownfail.d/min_domain_uid >new file mode 100644 >index 00000000000..982bb26a439 >--- /dev/null >+++ b/selftest/knownfail.d/min_domain_uid >@@ -0,0 +1 @@ >+^samba.tests.krb5.test_smb_no_nss_wb.samba.*.SmbNoNssWbTests.test_min_uid\(ad_member_no_nss_wb:local\) >diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py >index 855f90e690b..2e9603e8f34 100755 >--- a/source4/selftest/tests.py >+++ b/source4/selftest/tests.py >@@ -972,6 +972,13 @@ planoldpythontestsuite("ad_dc_smb1", "samba.tests.krb5.test_smb", > 'FAST_SUPPORT': have_fast_support, > 'TKT_SIG_SUPPORT': tkt_sig_support > }) >+planoldpythontestsuite("ad_member_no_nss_wb:local", >+ "samba.tests.krb5.test_min_domain_uid", >+ environ={ >+ 'ADMIN_USERNAME': '$DC_USERNAME', >+ 'ADMIN_PASSWORD': '$DC_PASSWORD', >+ 'STRICT_CHECKING': '0' >+ }) > > for env in ["ad_dc", smbv1_disabled_testenv]: > planoldpythontestsuite(env, "samba.tests.smb", extra_args=['-U"$USERNAME%$PASSWORD"']) >-- >2.25.1 > > >From cbbdc0410e24de056686837a1dd711a40e53a82c Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 8 Oct 2021 19:57:18 +0200 >Subject: [PATCH 062/190] CVE-2020-25717: s3:auth: let > auth3_generate_session_info_pac() forward the low level errors > >Mapping everything to ACCESS_DENIED makes it hard to debug problems, >which may happen because of our more restrictive behaviour in future. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/auth/auth_generic.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > >diff --git a/source3/auth/auth_generic.c b/source3/auth/auth_generic.c >index 0bd81b25cd4..61abbab2a02 100644 >--- a/source3/auth/auth_generic.c >+++ b/source3/auth/auth_generic.c >@@ -166,7 +166,7 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > if (!NT_STATUS_IS_OK(status)) { > DEBUG(1, ("Failed to map kerberos pac to server info (%s)\n", > nt_errstr(status))); >- status = NT_STATUS_ACCESS_DENIED; >+ status = nt_status_squash(status); > goto done; > } > >-- >2.25.1 > > >From 4079efa67d951148f0662404c0dfaad0eab88a7d Mon Sep 17 00:00:00 2001 >From: Samuel Cabrero <scabrero@samba.org> >Date: Tue, 28 Sep 2021 10:45:11 +0200 >Subject: [PATCH 063/190] CVE-2020-25717: s3:auth: Check minimum domain uid > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Samuel Cabrero <scabrero@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/auth/auth_util.c | 16 ++++++++++++++++ > 1 file changed, 16 insertions(+) > >diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c >index 0a1cf4803e4..9ff7256bbed 100644 >--- a/source3/auth/auth_util.c >+++ b/source3/auth/auth_util.c >@@ -2117,6 +2117,22 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, > } > } > goto out; >+ } else if ((lp_security() == SEC_ADS || lp_security() == SEC_DOMAIN) && >+ !is_myname(domain) && pwd->pw_uid < lp_min_domain_uid()) { >+ /* >+ * !is_myname(domain) because when smbd starts tries to setup >+ * the guest user info, calling this function with nobody >+ * username. Nobody is usually uid 65535 but it can be changed >+ * to a regular user with 'guest account' parameter >+ */ >+ nt_status = NT_STATUS_INVALID_TOKEN; >+ DBG_NOTICE("Username '%s%s%s' is invalid on this system, " >+ "it does not meet 'min domain uid' " >+ "restriction (%u < %u): %s\n", >+ nt_domain, lp_winbind_separator(), nt_username, >+ pwd->pw_uid, lp_min_domain_uid(), >+ nt_errstr(nt_status)); >+ goto out; > } > > result = make_server_info(tmp_ctx); >-- >2.25.1 > > >From dc704aa9590d31c68ec24bb5ac83ca34be4cbba2 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 8 Oct 2021 17:40:30 +0200 >Subject: [PATCH 064/190] CVE-2020-25717: s3:auth: we should not try to > autocreate the guest account > >We should avoid autocreation of users as much as possible. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/auth/user_krb5.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > >diff --git a/source3/auth/user_krb5.c b/source3/auth/user_krb5.c >index 8998f9c8f8a..074e8c7eb71 100644 >--- a/source3/auth/user_krb5.c >+++ b/source3/auth/user_krb5.c >@@ -155,7 +155,7 @@ NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, > if (!fuser) { > return NT_STATUS_NO_MEMORY; > } >- pw = smb_getpwnam(mem_ctx, fuser, &unixuser, true); >+ pw = smb_getpwnam(mem_ctx, fuser, &unixuser, false); > } > > /* extra sanity check that the guest account is valid */ >-- >2.25.1 > > >From 5e5c3b4804eee37d23418ec4798a68e8220f6ddf Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 8 Oct 2021 18:08:20 +0200 >Subject: [PATCH 065/190] CVE-2020-25717: s3:auth: no longer let > check_account() autocreate local users > >So far we autocreated local user accounts based on just the >account_name (just ignoring any domain part). > >This only happens via a possible 'add user script', >which is not typically defined on domain members >and on NT4 DCs local users already exist in the >local passdb anyway. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/auth/auth_util.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > >diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c >index 9ff7256bbed..be2f466526f 100644 >--- a/source3/auth/auth_util.c >+++ b/source3/auth/auth_util.c >@@ -1912,7 +1912,7 @@ static NTSTATUS check_account(TALLOC_CTX *mem_ctx, const char *domain, > return NT_STATUS_NO_MEMORY; > } > >- passwd = smb_getpwnam(mem_ctx, dom_user, &real_username, true ); >+ passwd = smb_getpwnam(mem_ctx, dom_user, &real_username, false); > if (!passwd) { > DEBUG(3, ("Failed to find authenticated user %s via " > "getpwnam(), denying access.\n", dom_user)); >-- >2.25.1 > > >From a51f2f9f58f437b6c76eef6fb5b1b9ae48b07da1 Mon Sep 17 00:00:00 2001 >From: Ralph Boehme <slow@samba.org> >Date: Fri, 8 Oct 2021 12:33:16 +0200 >Subject: [PATCH 066/190] CVE-2020-25717: s3:auth: remove fallbacks in > smb_getpwnam() > >So far we tried getpwnam("DOMAIN\account") first and >always did a fallback to getpwnam("account") completely >ignoring the domain part, this just causes problems >as we mix "DOMAIN1\account", "DOMAIN2\account", >and "account"! > >As we require a running winbindd for domain member setups >we should no longer do a fallback to just "account" for >users served by winbindd! > >For users of the local SAM don't use this code path, >as check_sam_security() doesn't call check_account(). > >The only case where smb_getpwnam("account") happens is >when map_username() via ("username map [script]") mapped >"DOMAIN\account" to something without '\', but that is >explicitly desired by the admin. > >Note: use 'git show -w' > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Ralph Boehme <slow@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > selftest/knownfail.d/ktest | 26 +++++++++++++ > source3/auth/auth_util.c | 77 +++++++++++++++++++++----------------- > 2 files changed, 68 insertions(+), 35 deletions(-) > create mode 100644 selftest/knownfail.d/ktest > >diff --git a/selftest/knownfail.d/ktest b/selftest/knownfail.d/ktest >new file mode 100644 >index 00000000000..809612ba0b9 >--- /dev/null >+++ b/selftest/knownfail.d/ktest >@@ -0,0 +1,26 @@ >+^samba3.rpc.lsa.lookupsids.krb5.with.old.ccache.ncacn_np.with..smb2...lsa.LookupSidsReply.ktest >+^samba3.rpc.lsa.lookupsids.krb5.ncacn_np.with..smb2...lsa.LookupSidsReply.ktest >+^samba3.blackbox.rpcclient.krb5.ncacn_np.with..krb5...rpcclient.ktest:local >+^samba3.blackbox.rpcclient.krb5.ncacn_np.with..spnego,krb5...rpcclient.ktest:local >+^samba3.rpc.lsa.lookupsids.krb5.with.old.ccache.ncacn_np.with..smb2,connect...lsa.LookupSidsReply.ktest >+^samba3.rpc.lsa.lookupsids.krb5.ncacn_np.with..smb2,connect...lsa.LookupSidsReply.ktest >+^samba3.rpc.lsa.lookupsids.krb5.with.old.ccache.ncacn_np.with..smb2,packet...lsa.LookupSidsReply.ktest >+^samba3.rpc.lsa.lookupsids.krb5.ncacn_np.with..smb2,packet...lsa.LookupSidsReply.ktest >+^samba3.blackbox.rpcclient.krb5.ncacn_np.with..krb5,packet...rpcclient.ktest:local >+^samba3.blackbox.rpcclient.krb5.ncacn_np.with..spnego,krb5,packet...rpcclient.ktest:local >+^samba3.rpc.lsa.lookupsids.krb5.with.old.ccache.ncacn_np.with..smb2,sign...lsa.LookupSidsReply.ktest >+^samba3.rpc.lsa.lookupsids.krb5.ncacn_np.with..smb2,sign...lsa.LookupSidsReply.ktest >+^samba3.blackbox.rpcclient.krb5.ncacn_np.with..krb5,sign...rpcclient.ktest:local >+^samba3.blackbox.rpcclient.krb5.ncacn_np.with..spnego,krb5,sign...rpcclient.ktest:local >+^samba3.rpc.lsa.lookupsids.krb5.with.old.ccache.ncacn_np.with..smb2,seal...lsa.LookupSidsReply.ktest >+^samba3.rpc.lsa.lookupsids.krb5.ncacn_np.with..smb2,seal...lsa.LookupSidsReply.ktest >+^samba3.blackbox.rpcclient.krb5.ncacn_np.with..krb5,seal...rpcclient.ktest:local >+^samba3.blackbox.rpcclient.krb5.ncacn_np.with..spnego,krb5,seal...rpcclient.ktest:local >+^samba3.blackbox.smbclient_krb5.old.ccache..smbclient.ktest:local >+^samba3.blackbox.smbclient_krb5.new.ccache..smbclient.ktest:local >+^samba3.blackbox.smbclient_large_file..krb5.smbclient.large.posix.write.read.ktest:local >+^samba3.blackbox.smbclient_large_file..krb5.cmp.of.read.and.written.files.ktest:local >+^samba3.blackbox.smbclient_krb5.old.ccache.--client-protection=encrypt.smbclient.ktest:local >+^samba3.blackbox.smbclient_krb5.new.ccache.--client-protection=encrypt.smbclient.ktest:local >+^samba3.blackbox.smbclient_large_file.--client-protection=encrypt.krb5.smbclient.large.posix.write.read.ktest:local >+^samba3.blackbox.smbclient_large_file.--client-protection=encrypt.krb5.cmp.of.read.and.written.files.ktest:local >diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c >index be2f466526f..26fa227e9b0 100644 >--- a/source3/auth/auth_util.c >+++ b/source3/auth/auth_util.c >@@ -1947,7 +1947,7 @@ struct passwd *smb_getpwnam( TALLOC_CTX *mem_ctx, const char *domuser, > { > struct passwd *pw = NULL; > char *p = NULL; >- char *username = NULL; >+ const char *username = NULL; > > /* we only save a copy of the username it has been mangled > by winbindd use default domain */ >@@ -1966,48 +1966,55 @@ struct passwd *smb_getpwnam( TALLOC_CTX *mem_ctx, const char *domuser, > /* code for a DOMAIN\user string */ > > if ( p ) { >- pw = Get_Pwnam_alloc( mem_ctx, domuser ); >- if ( pw ) { >- /* make sure we get the case of the username correct */ >- /* work around 'winbind use default domain = yes' */ >- >- if ( lp_winbind_use_default_domain() && >- !strchr_m( pw->pw_name, *lp_winbind_separator() ) ) { >- char *domain; >- >- /* split the domain and username into 2 strings */ >- *p = '\0'; >- domain = username; >- >- *p_save_username = talloc_asprintf(mem_ctx, >- "%s%c%s", >- domain, >- *lp_winbind_separator(), >- pw->pw_name); >- if (!*p_save_username) { >- TALLOC_FREE(pw); >- return NULL; >- } >- } else { >- *p_save_username = talloc_strdup(mem_ctx, pw->pw_name); >- } >+ const char *domain = NULL; > >- /* whew -- done! */ >- return pw; >+ /* split the domain and username into 2 strings */ >+ *p = '\0'; >+ domain = username; >+ p++; >+ username = p; >+ >+ if (strequal(domain, get_global_sam_name())) { >+ /* >+ * This typically don't happen >+ * as check_sam_Security() >+ * don't call make_server_info_info3() >+ * and thus check_account(). >+ * >+ * But we better keep this. >+ */ >+ goto username_only; > } > >- /* setup for lookup of just the username */ >- /* remember that p and username are overlapping memory */ >- >- p++; >- username = talloc_strdup(mem_ctx, p); >- if (!username) { >+ pw = Get_Pwnam_alloc( mem_ctx, domuser ); >+ if (pw == NULL) { > return NULL; > } >+ /* make sure we get the case of the username correct */ >+ /* work around 'winbind use default domain = yes' */ >+ >+ if ( lp_winbind_use_default_domain() && >+ !strchr_m( pw->pw_name, *lp_winbind_separator() ) ) { >+ *p_save_username = talloc_asprintf(mem_ctx, >+ "%s%c%s", >+ domain, >+ *lp_winbind_separator(), >+ pw->pw_name); >+ if (!*p_save_username) { >+ TALLOC_FREE(pw); >+ return NULL; >+ } >+ } else { >+ *p_save_username = talloc_strdup(mem_ctx, pw->pw_name); >+ } >+ >+ /* whew -- done! */ >+ return pw; >+ > } > > /* just lookup a plain username */ >- >+username_only: > pw = Get_Pwnam_alloc(mem_ctx, username); > > /* Create local user if requested but only if winbindd >-- >2.25.1 > > >From ff24958db064a1a1698a52adebe7939812935e28 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 21 Sep 2021 13:13:52 +0200 >Subject: [PATCH 067/190] CVE-2020-25717: s3:lib: add > lp_allow_trusted_domains() logic to is_allowed_domain() > >is_allowed_domain() is a central place we already use to >trigger NT_STATUS_AUTHENTICATION_FIREWALL_FAILED, so >we can add additional logic there. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/lib/util_names.c | 15 ++++++++++++++- > 1 file changed, 14 insertions(+), 1 deletion(-) > >diff --git a/source3/lib/util_names.c b/source3/lib/util_names.c >index f0e9f699f29..b62ddb302c6 100644 >--- a/source3/lib/util_names.c >+++ b/source3/lib/util_names.c >@@ -69,5 +69,18 @@ bool is_allowed_domain(const char *domain_name) > } > } > >- return true; >+ if (lp_allow_trusted_domains()) { >+ return true; >+ } >+ >+ if (strequal(lp_workgroup(), domain_name)) { >+ return true; >+ } >+ >+ if (is_myname(domain_name)) { >+ return true; >+ } >+ >+ DBG_NOTICE("Not trusted domain '%s'\n", domain_name); >+ return false; > } >-- >2.25.1 > > >From b67c2374c78542008e018557a3dcd9b7653ca46e Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Mon, 4 Oct 2021 18:03:55 +0200 >Subject: [PATCH 068/190] CVE-2020-25717: s3:auth: don't let create_local_token > depend on !winbind_ping() > >We always require a running winbindd on a domain member, so >we should better fail a request instead of silently alter >the behaviour, which results in a different unix token, just >because winbindd might be restarted. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/auth/auth_util.c | 10 ++++------ > 1 file changed, 4 insertions(+), 6 deletions(-) > >diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c >index 26fa227e9b0..dec854d85c3 100644 >--- a/source3/auth/auth_util.c >+++ b/source3/auth/auth_util.c >@@ -570,13 +570,11 @@ NTSTATUS create_local_token(TALLOC_CTX *mem_ctx, > } > > /* >- * If winbind is not around, we can not make much use of the SIDs the >- * domain controller provided us with. Likewise if the user name was >- * mapped to some local unix user. >+ * If the user name was mapped to some local unix user, >+ * we can not make much use of the SIDs the >+ * domain controller provided us with. > */ >- >- if (((lp_server_role() == ROLE_DOMAIN_MEMBER) && !winbind_ping()) || >- (server_info->nss_token)) { >+ if (server_info->nss_token) { > char *found_username = NULL; > status = create_token_from_username(session_info, > server_info->unix_name, >-- >2.25.1 > > >From 0265afb17f0854c50336beeb13fcd3f285547818 Mon Sep 17 00:00:00 2001 >From: Alexander Bokovoy <ab@samba.org> >Date: Wed, 11 Nov 2020 18:50:45 +0200 >Subject: [PATCH 069/190] CVE-2020-25717: Add FreeIPA domain controller role > >As we want to reduce use of 'classic domain controller' role but FreeIPA >relies on it internally, add a separate role to mark FreeIPA domain >controller role. > >It means that role won't result in ROLE_STANDALONE. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Alexander Bokovoy <ab@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > docs-xml/smbdotconf/security/serverrole.xml | 7 ++++ > lib/param/loadparm_server_role.c | 2 ++ > lib/param/param_table.c | 1 + > lib/param/util.c | 1 + > libcli/netlogon/netlogon.c | 2 +- > libds/common/roles.h | 1 + > source3/auth/auth.c | 3 ++ > source3/auth/auth_sam.c | 14 ++++---- > source3/include/smb_macros.h | 2 +- > source3/lib/netapi/joindomain.c | 1 + > source3/param/loadparm.c | 4 ++- > source3/passdb/lookup_sid.c | 2 +- > source3/passdb/machine_account_secrets.c | 7 ++-- > source3/registry/reg_backend_prod_options.c | 1 + > source3/rpc_server/dssetup/srv_dssetup_nt.c | 1 + > source3/smbd/server.c | 2 +- > source3/winbindd/winbindd_misc.c | 2 +- > source3/winbindd/winbindd_util.c | 40 ++++++++++++++++----- > source4/auth/ntlm/auth.c | 1 + > source4/kdc/kdc-heimdal.c | 1 + > source4/rpc_server/samr/dcesrv_samr.c | 2 ++ > 21 files changed, 72 insertions(+), 25 deletions(-) > >diff --git a/docs-xml/smbdotconf/security/serverrole.xml b/docs-xml/smbdotconf/security/serverrole.xml >index 9511c61c96d..b8b83a127b5 100644 >--- a/docs-xml/smbdotconf/security/serverrole.xml >+++ b/docs-xml/smbdotconf/security/serverrole.xml >@@ -78,6 +78,13 @@ > url="http://wiki.samba.org/index.php/Samba4/HOWTO">Samba4 > HOWTO</ulink></para> > >+ <para><anchor id="IPA-DC"/><emphasis>SERVER ROLE = IPA DOMAIN CONTROLLER</emphasis></para> >+ >+ <para>This mode of operation runs Samba in a hybrid mode for IPA >+ domain controller, providing forest trust to Active Directory. >+ This role requires special configuration performed by IPA installers >+ and should not be used manually by any administrator. >+ </para> > </description> > > <related>security</related> >diff --git a/lib/param/loadparm_server_role.c b/lib/param/loadparm_server_role.c >index 7a6bc770723..a78d1ab9cf3 100644 >--- a/lib/param/loadparm_server_role.c >+++ b/lib/param/loadparm_server_role.c >@@ -42,6 +42,7 @@ static const struct srv_role_tab { > { ROLE_DOMAIN_BDC, "ROLE_DOMAIN_BDC" }, > { ROLE_DOMAIN_PDC, "ROLE_DOMAIN_PDC" }, > { ROLE_ACTIVE_DIRECTORY_DC, "ROLE_ACTIVE_DIRECTORY_DC" }, >+ { ROLE_IPA_DC, "ROLE_IPA_DC"}, > { 0, NULL } > }; > >@@ -140,6 +141,7 @@ bool lp_is_security_and_server_role_valid(int server_role, int security) > case ROLE_DOMAIN_PDC: > case ROLE_DOMAIN_BDC: > case ROLE_ACTIVE_DIRECTORY_DC: >+ case ROLE_IPA_DC: > if (security == SEC_USER) { > valid = true; > } >diff --git a/lib/param/param_table.c b/lib/param/param_table.c >index d9301152d94..9fac73ef113 100644 >--- a/lib/param/param_table.c >+++ b/lib/param/param_table.c >@@ -109,6 +109,7 @@ static const struct enum_list enum_server_role[] = { > {ROLE_ACTIVE_DIRECTORY_DC, "active directory domain controller"}, > {ROLE_ACTIVE_DIRECTORY_DC, "domain controller"}, > {ROLE_ACTIVE_DIRECTORY_DC, "dc"}, >+ {ROLE_IPA_DC, "IPA primary domain controller"}, > {-1, NULL} > }; > >diff --git a/lib/param/util.c b/lib/param/util.c >index cd8e74b9d8f..9a0fc102de8 100644 >--- a/lib/param/util.c >+++ b/lib/param/util.c >@@ -255,6 +255,7 @@ const char *lpcfg_sam_name(struct loadparm_context *lp_ctx) > case ROLE_DOMAIN_BDC: > case ROLE_DOMAIN_PDC: > case ROLE_ACTIVE_DIRECTORY_DC: >+ case ROLE_IPA_DC: > return lpcfg_workgroup(lp_ctx); > default: > return lpcfg_netbios_name(lp_ctx); >diff --git a/libcli/netlogon/netlogon.c b/libcli/netlogon/netlogon.c >index 239503e85b6..59af460dc4e 100644 >--- a/libcli/netlogon/netlogon.c >+++ b/libcli/netlogon/netlogon.c >@@ -93,7 +93,7 @@ NTSTATUS pull_netlogon_samlogon_response(DATA_BLOB *data, TALLOC_CTX *mem_ctx, > if (ndr->offset < ndr->data_size) { > TALLOC_FREE(ndr); > /* >- * We need to handle a bug in FreeIPA (at least <= 4.1.2). >+ * We need to handle a bug in IPA (at least <= 4.1.2). > * > * They include the ip address information without setting > * NETLOGON_NT_VERSION_5EX_WITH_IP, while using >diff --git a/libds/common/roles.h b/libds/common/roles.h >index 4772c8d7d3f..03ba1915b21 100644 >--- a/libds/common/roles.h >+++ b/libds/common/roles.h >@@ -33,6 +33,7 @@ enum server_role { > > /* not in samr.idl */ > ROLE_ACTIVE_DIRECTORY_DC = 4, >+ ROLE_IPA_DC = 5, > > /* To determine the role automatically, this is not a valid role */ > ROLE_AUTO = 100 >diff --git a/source3/auth/auth.c b/source3/auth/auth.c >index ce6bf6c5621..fec19c76dbb 100644 >--- a/source3/auth/auth.c >+++ b/source3/auth/auth.c >@@ -544,6 +544,7 @@ NTSTATUS make_auth3_context_for_ntlm(TALLOC_CTX *mem_ctx, > break; > case ROLE_DOMAIN_BDC: > case ROLE_DOMAIN_PDC: >+ case ROLE_IPA_DC: > role = "'DC'"; > methods = "anonymous sam winbind sam_ignoredomain"; > break; >@@ -575,6 +576,7 @@ NTSTATUS make_auth3_context_for_netlogon(TALLOC_CTX *mem_ctx, > switch (lp_server_role()) { > case ROLE_DOMAIN_BDC: > case ROLE_DOMAIN_PDC: >+ case ROLE_IPA_DC: > methods = "sam_netlogon3 winbind"; > break; > >@@ -596,6 +598,7 @@ NTSTATUS make_auth3_context_for_winbind(TALLOC_CTX *mem_ctx, > case ROLE_DOMAIN_MEMBER: > case ROLE_DOMAIN_BDC: > case ROLE_DOMAIN_PDC: >+ case ROLE_IPA_DC: > methods = "sam"; > break; > case ROLE_ACTIVE_DIRECTORY_DC: >diff --git a/source3/auth/auth_sam.c b/source3/auth/auth_sam.c >index e8e0d543f8c..a2ce1013975 100644 >--- a/source3/auth/auth_sam.c >+++ b/source3/auth/auth_sam.c >@@ -143,12 +143,13 @@ static NTSTATUS auth_samstrict_auth(const struct auth_context *auth_context, > break; > case ROLE_DOMAIN_PDC: > case ROLE_DOMAIN_BDC: >+ case ROLE_IPA_DC: > if (!is_local_name && !is_my_domain) { > /* If we are running on a DC that has PASSDB module with domain > * information, check if DNS forest name is matching the domain >- * name. This is the case of FreeIPA domain controller when >- * trusted AD DCs attempt to authenticate FreeIPA users using >- * the forest root domain (which is the only domain in FreeIPA). >+ * name. This is the case of IPA domain controller when >+ * trusted AD DCs attempt to authenticate IPA users using >+ * the forest root domain (which is the only domain in IPA). > */ > struct pdb_domain_info *dom_info = NULL; > >@@ -234,6 +235,7 @@ static NTSTATUS auth_sam_netlogon3_auth(const struct auth_context *auth_context, > switch (lp_server_role()) { > case ROLE_DOMAIN_PDC: > case ROLE_DOMAIN_BDC: >+ case ROLE_IPA_DC: > break; > default: > DBG_ERR("Invalid server role\n"); >@@ -252,9 +254,9 @@ static NTSTATUS auth_sam_netlogon3_auth(const struct auth_context *auth_context, > if (!is_my_domain) { > /* If we are running on a DC that has PASSDB module with domain > * information, check if DNS forest name is matching the domain >- * name. This is the case of FreeIPA domain controller when >- * trusted AD DCs attempt to authenticate FreeIPA users using >- * the forest root domain (which is the only domain in FreeIPA). >+ * name. This is the case of IPA domain controller when >+ * trusted AD DCs attempt to authenticate IPA users using >+ * the forest root domain (which is the only domain in IPA). > */ > struct pdb_domain_info *dom_info = NULL; > dom_info = pdb_get_domain_info(mem_ctx); >diff --git a/source3/include/smb_macros.h b/source3/include/smb_macros.h >index 39a7c0d6911..ba2c76764d1 100644 >--- a/source3/include/smb_macros.h >+++ b/source3/include/smb_macros.h >@@ -203,7 +203,7 @@ copy an IP address from one buffer to another > Check to see if we are a DC for this domain > *****************************************************************************/ > >-#define IS_DC (lp_server_role()==ROLE_DOMAIN_PDC || lp_server_role()==ROLE_DOMAIN_BDC || lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) >+#define IS_DC (lp_server_role()==ROLE_DOMAIN_PDC || lp_server_role()==ROLE_DOMAIN_BDC || lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_server_role() == ROLE_IPA_DC) > #define IS_AD_DC (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) > > /* >diff --git a/source3/lib/netapi/joindomain.c b/source3/lib/netapi/joindomain.c >index da40a887172..4fe5a3b8eef 100644 >--- a/source3/lib/netapi/joindomain.c >+++ b/source3/lib/netapi/joindomain.c >@@ -378,6 +378,7 @@ WERROR NetGetJoinInformation_l(struct libnetapi_ctx *ctx, > case ROLE_DOMAIN_MEMBER: > case ROLE_DOMAIN_PDC: > case ROLE_DOMAIN_BDC: >+ case ROLE_IPA_DC: > *r->out.name_type = NetSetupDomainName; > break; > case ROLE_STANDALONE: >diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c >index e98d1738bb4..e37c7ec8b03 100644 >--- a/source3/param/loadparm.c >+++ b/source3/param/loadparm.c >@@ -4421,6 +4421,7 @@ int lp_default_server_announce(void) > default_server_announce |= SV_TYPE_DOMAIN_MEMBER; > break; > case ROLE_DOMAIN_PDC: >+ case ROLE_IPA_DC: > default_server_announce |= SV_TYPE_DOMAIN_CTRL; > break; > case ROLE_DOMAIN_BDC: >@@ -4446,7 +4447,8 @@ int lp_default_server_announce(void) > bool lp_domain_master(void) > { > if (Globals._domain_master == Auto) >- return (lp_server_role() == ROLE_DOMAIN_PDC); >+ return (lp_server_role() == ROLE_DOMAIN_PDC || >+ lp_server_role() == ROLE_IPA_DC); > > return (bool)Globals._domain_master; > } >diff --git a/source3/passdb/lookup_sid.c b/source3/passdb/lookup_sid.c >index 0e01467b3cb..a551bcfd24a 100644 >--- a/source3/passdb/lookup_sid.c >+++ b/source3/passdb/lookup_sid.c >@@ -121,7 +121,7 @@ bool lookup_name(TALLOC_CTX *mem_ctx, > > /* If we are running on a DC that has PASSDB module with domain > * information, check if DNS forest name is matching the domain >- * name. This is the case of FreeIPA domain controller when >+ * name. This is the case of IPA domain controller when > * trusted AD DC looks up users found in a Global Catalog of > * the forest root domain. */ > if (!check_global_sam && (IS_DC)) { >diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c >index 1964eb5a448..f98f0c98674 100644 >--- a/source3/passdb/machine_account_secrets.c >+++ b/source3/passdb/machine_account_secrets.c >@@ -198,7 +198,8 @@ bool secrets_fetch_domain_guid(const char *domain, struct GUID *guid) > dyn_guid = (struct GUID *)secrets_fetch(key, &size); > > if (!dyn_guid) { >- if (lp_server_role() == ROLE_DOMAIN_PDC) { >+ if (lp_server_role() == ROLE_DOMAIN_PDC || >+ lp_server_role() == ROLE_IPA_DC) { > new_guid = GUID_random(); > if (!secrets_store_domain_guid(domain, &new_guid)) > return False; >@@ -314,9 +315,7 @@ static const char *trust_keystr(const char *domain) > > enum netr_SchannelType get_default_sec_channel(void) > { >- if (lp_server_role() == ROLE_DOMAIN_BDC || >- lp_server_role() == ROLE_DOMAIN_PDC || >- lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) { >+ if (IS_DC) { > return SEC_CHAN_BDC; > } else { > return SEC_CHAN_WKSTA; >diff --git a/source3/registry/reg_backend_prod_options.c b/source3/registry/reg_backend_prod_options.c >index 655c587ac40..7bd3f324c37 100644 >--- a/source3/registry/reg_backend_prod_options.c >+++ b/source3/registry/reg_backend_prod_options.c >@@ -40,6 +40,7 @@ static int prod_options_fetch_values(const char *key, struct regval_ctr *regvals > switch (lp_server_role()) { > case ROLE_DOMAIN_PDC: > case ROLE_DOMAIN_BDC: >+ case ROLE_IPA_DC: > value_ascii = "LanmanNT"; > break; > case ROLE_STANDALONE: >diff --git a/source3/rpc_server/dssetup/srv_dssetup_nt.c b/source3/rpc_server/dssetup/srv_dssetup_nt.c >index 64569382695..932452bc13b 100644 >--- a/source3/rpc_server/dssetup/srv_dssetup_nt.c >+++ b/source3/rpc_server/dssetup/srv_dssetup_nt.c >@@ -63,6 +63,7 @@ static WERROR fill_dsrole_dominfo_basic(TALLOC_CTX *ctx, > basic->domain = get_global_sam_name(); > break; > case ROLE_DOMAIN_PDC: >+ case ROLE_IPA_DC: > basic->role = DS_ROLE_PRIMARY_DC; > basic->domain = get_global_sam_name(); > break; >diff --git a/source3/smbd/server.c b/source3/smbd/server.c >index b9fb7e855b8..d7f5b4b73c0 100644 >--- a/source3/smbd/server.c >+++ b/source3/smbd/server.c >@@ -1930,7 +1930,7 @@ extern void build_options(bool screen); > exit_daemon("smbd can not open secrets.tdb", EACCES); > } > >- if (lp_server_role() == ROLE_DOMAIN_BDC || lp_server_role() == ROLE_DOMAIN_PDC) { >+ if (lp_server_role() == ROLE_DOMAIN_BDC || lp_server_role() == ROLE_DOMAIN_PDC || lp_server_role() == ROLE_IPA_DC) { > struct loadparm_context *lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers()); > if (!open_schannel_session_store(NULL, lp_ctx)) { > exit_daemon("ERROR: Samba cannot open schannel store for secured NETLOGON operations.", EACCES); >diff --git a/source3/winbindd/winbindd_misc.c b/source3/winbindd/winbindd_misc.c >index 451ad6aee14..db7e1c87dee 100644 >--- a/source3/winbindd/winbindd_misc.c >+++ b/source3/winbindd/winbindd_misc.c >@@ -76,7 +76,7 @@ static char *get_trust_type_string(TALLOC_CTX *mem_ctx, > case SEC_CHAN_BDC: { > int role = lp_server_role(); > >- if (role == ROLE_DOMAIN_PDC) { >+ if (role == ROLE_DOMAIN_PDC || role == ROLE_IPA_DC) { > s = talloc_strdup(mem_ctx, "PDC"); > if (s == NULL) { > return NULL; >diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c >index 6e3739f68c1..fe68adec534 100644 >--- a/source3/winbindd/winbindd_util.c >+++ b/source3/winbindd/winbindd_util.c >@@ -1322,15 +1322,37 @@ bool init_domain_list(void) > secure_channel_type = SEC_CHAN_LOCAL; > } > >- status = add_trusted_domain(get_global_sam_name(), >- NULL, >- get_global_sam_sid(), >- LSA_TRUST_TYPE_DOWNLEVEL, >- trust_flags, >- 0, /* trust_attribs */ >- secure_channel_type, >- NULL, >- &domain); >+ if ((pdb_domain_info != NULL) && (role == ROLE_IPA_DC)) { >+ /* This is IPA DC that presents itself as >+ * an Active Directory domain controller to trusted AD >+ * forests but in fact is a classic domain controller. >+ */ >+ trust_flags = NETR_TRUST_FLAG_PRIMARY; >+ trust_flags |= NETR_TRUST_FLAG_IN_FOREST; >+ trust_flags |= NETR_TRUST_FLAG_NATIVE; >+ trust_flags |= NETR_TRUST_FLAG_OUTBOUND; >+ trust_flags |= NETR_TRUST_FLAG_TREEROOT; >+ status = add_trusted_domain(pdb_domain_info->name, >+ pdb_domain_info->dns_domain, >+ &pdb_domain_info->sid, >+ LSA_TRUST_TYPE_UPLEVEL, >+ trust_flags, >+ LSA_TRUST_ATTRIBUTE_WITHIN_FOREST, >+ secure_channel_type, >+ NULL, >+ &domain); >+ TALLOC_FREE(pdb_domain_info); >+ } else { >+ status = add_trusted_domain(get_global_sam_name(), >+ NULL, >+ get_global_sam_sid(), >+ LSA_TRUST_TYPE_DOWNLEVEL, >+ trust_flags, >+ 0, /* trust_attribs */ >+ secure_channel_type, >+ NULL, >+ &domain); >+ } > if (!NT_STATUS_IS_OK(status)) { > DBG_ERR("Failed to add local SAM to " > "domain to winbindd's internal list\n"); >diff --git a/source4/auth/ntlm/auth.c b/source4/auth/ntlm/auth.c >index 4c66f2c23cb..ea9ff70ce80 100644 >--- a/source4/auth/ntlm/auth.c >+++ b/source4/auth/ntlm/auth.c >@@ -737,6 +737,7 @@ const char **auth_methods_from_lp(TALLOC_CTX *mem_ctx, struct loadparm_context * > case ROLE_DOMAIN_BDC: > case ROLE_DOMAIN_PDC: > case ROLE_ACTIVE_DIRECTORY_DC: >+ case ROLE_IPA_DC: > auth_methods = str_list_make(mem_ctx, "anonymous sam winbind sam_ignoredomain", NULL); > break; > } >diff --git a/source4/kdc/kdc-heimdal.c b/source4/kdc/kdc-heimdal.c >index c695004cafc..ce32d3cb1b3 100644 >--- a/source4/kdc/kdc-heimdal.c >+++ b/source4/kdc/kdc-heimdal.c >@@ -276,6 +276,7 @@ static NTSTATUS kdc_task_init(struct task_server *task) > return NT_STATUS_INVALID_DOMAIN_ROLE; > case ROLE_DOMAIN_PDC: > case ROLE_DOMAIN_BDC: >+ case ROLE_IPA_DC: > task_server_terminate( > task, "Cannot start KDC as a 'classic Samba' DC", false); > return NT_STATUS_INVALID_DOMAIN_ROLE; >diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c >index cda887d45ee..29c509522be 100644 >--- a/source4/rpc_server/samr/dcesrv_samr.c >+++ b/source4/rpc_server/samr/dcesrv_samr.c >@@ -575,6 +575,7 @@ static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state > break; > case ROLE_DOMAIN_PDC: > case ROLE_DOMAIN_BDC: >+ case ROLE_IPA_DC: > case ROLE_AUTO: > return NT_STATUS_INTERNAL_ERROR; > case ROLE_DOMAIN_MEMBER: >@@ -723,6 +724,7 @@ static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state, > break; > case ROLE_DOMAIN_PDC: > case ROLE_DOMAIN_BDC: >+ case ROLE_IPA_DC: > case ROLE_AUTO: > return NT_STATUS_INTERNAL_ERROR; > case ROLE_DOMAIN_MEMBER: >-- >2.25.1 > > >From 7a907bd6dbcec6f3fcb5ee3d6f6c19e57348a332 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 5 Oct 2021 18:11:57 +0200 >Subject: [PATCH 070/190] CVE-2020-25717: auth/gensec: always require a PAC in > domain mode (DC or member) > >AD domains always provide a PAC unless UF_NO_AUTH_DATA_REQUIRED is set >on the service account, which can only be explicitly configured, >but that's an invalid configuration! > >We still try to support standalone servers in an MIT realm, >as legacy setup. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > auth/gensec/gensec_util.c | 27 +++++++++++++++++++++++---- > selftest/knownfail.d/no-pac | 4 ---- > 2 files changed, 23 insertions(+), 8 deletions(-) > delete mode 100644 selftest/knownfail.d/no-pac > >diff --git a/auth/gensec/gensec_util.c b/auth/gensec/gensec_util.c >index e411751c3af..1075b9fde87 100644 >--- a/auth/gensec/gensec_util.c >+++ b/auth/gensec/gensec_util.c >@@ -25,6 +25,8 @@ > #include "auth/gensec/gensec_internal.h" > #include "auth/common_auth.h" > #include "../lib/util/asn1.h" >+#include "param/param.h" >+#include "libds/common/roles.h" > > #undef DBGC_CLASS > #define DBGC_CLASS DBGC_AUTH >@@ -48,10 +50,27 @@ NTSTATUS gensec_generate_session_info_pac(TALLOC_CTX *mem_ctx, > session_info_flags |= AUTH_SESSION_INFO_DEFAULT_GROUPS; > > if (!pac_blob) { >- if (gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) { >- DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access\n", >- principal_string)); >- return NT_STATUS_ACCESS_DENIED; >+ enum server_role server_role = >+ lpcfg_server_role(gensec_security->settings->lp_ctx); >+ >+ /* >+ * For any domain setup (DC or member) we require having >+ * a PAC, as the service ticket comes from an AD DC, >+ * which will always provide a PAC, unless >+ * UF_NO_AUTH_DATA_REQUIRED is configured for our >+ * account, but that's just an invalid configuration, >+ * the admin configured for us! >+ * >+ * As a legacy case, we still allow kerberos tickets from an MIT >+ * realm, but only in standalone mode. In that mode we'll only >+ * ever accept a kerberos authentication with a keytab file >+ * being explicitly configured via the 'keytab method' option. >+ */ >+ if (server_role != ROLE_STANDALONE) { >+ DBG_WARNING("Unable to find PAC in ticket from %s, " >+ "failing to allow access\n", >+ principal_string); >+ return NT_STATUS_NO_IMPERSONATION_TOKEN; > } > DBG_NOTICE("Unable to find PAC for %s, resorting to local " > "user lookup\n", principal_string); >diff --git a/selftest/knownfail.d/no-pac b/selftest/knownfail.d/no-pac >deleted file mode 100644 >index 9723d581c2a..00000000000 >--- a/selftest/knownfail.d/no-pac >+++ /dev/null >@@ -1,4 +0,0 @@ >-^samba.tests.krb5.test_ccache.samba.tests.krb5.test_ccache.CcacheTests.test_ccache_no_pac >-^samba.tests.krb5.test_ldap.samba.tests.krb5.test_ldap.LdapTests.test_ldap_no_pac >-^samba.tests.krb5.test_rpc.samba.tests.krb5.test_rpc.RpcTests.test_rpc_no_pac >-^samba.tests.krb5.test_smb.samba.tests.krb5.test_smb.SmbTests.test_smb_no_pac >-- >2.25.1 > > >From 11d9dd7f14ced502ad21416025cdf1d8a806fee2 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Mon, 11 Oct 2021 23:17:19 +0200 >Subject: [PATCH 071/190] CVE-2020-25717: s4:auth: remove unused > auth_generate_session_info_principal() > >We'll require a PAC at the main gensec layer already. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source4/auth/auth.h | 8 ------ > source4/auth/ntlm/auth.c | 49 ++++-------------------------------- > source4/auth/ntlm/auth_sam.c | 12 --------- > 3 files changed, 5 insertions(+), 64 deletions(-) > >diff --git a/source4/auth/auth.h b/source4/auth/auth.h >index 3f9fb1ae3cb..6b7db99cbe2 100644 >--- a/source4/auth/auth.h >+++ b/source4/auth/auth.h >@@ -69,14 +69,6 @@ struct auth_operations { > TALLOC_CTX *mem_ctx, > struct auth_user_info_dc **interim_info, > bool *authoritative); >- >- /* Lookup a 'session info interim' return based only on the principal or DN */ >- NTSTATUS (*get_user_info_dc_principal)(TALLOC_CTX *mem_ctx, >- struct auth4_context *auth_context, >- const char *principal, >- struct ldb_dn *user_dn, >- struct auth_user_info_dc **interim_info); >- uint32_t flags; > }; > > struct auth_method_context { >diff --git a/source4/auth/ntlm/auth.c b/source4/auth/ntlm/auth.c >index ea9ff70ce80..3dd2ffc9276 100644 >--- a/source4/auth/ntlm/auth.c >+++ b/source4/auth/ntlm/auth.c >@@ -86,48 +86,6 @@ _PUBLIC_ NTSTATUS auth_get_challenge(struct auth4_context *auth_ctx, uint8_t cha > return NT_STATUS_OK; > } > >-/**************************************************************************** >-Used in the gensec_gssapi and gensec_krb5 server-side code, where the >-PAC isn't available, and for tokenGroups in the DSDB stack. >- >- Supply either a principal or a DN >-****************************************************************************/ >-static NTSTATUS auth_generate_session_info_principal(struct auth4_context *auth_ctx, >- TALLOC_CTX *mem_ctx, >- const char *principal, >- struct ldb_dn *user_dn, >- uint32_t session_info_flags, >- struct auth_session_info **session_info) >-{ >- NTSTATUS nt_status; >- struct auth_method_context *method; >- struct auth_user_info_dc *user_info_dc; >- >- for (method = auth_ctx->methods; method; method = method->next) { >- if (!method->ops->get_user_info_dc_principal) { >- continue; >- } >- >- nt_status = method->ops->get_user_info_dc_principal(mem_ctx, auth_ctx, principal, user_dn, &user_info_dc); >- if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) { >- continue; >- } >- if (!NT_STATUS_IS_OK(nt_status)) { >- return nt_status; >- } >- >- nt_status = auth_generate_session_info_wrapper(auth_ctx, mem_ctx, >- user_info_dc, >- user_info_dc->info->account_name, >- session_info_flags, session_info); >- talloc_free(user_info_dc); >- >- return nt_status; >- } >- >- return NT_STATUS_NOT_IMPLEMENTED; >-} >- > /** > * Check a user's Plaintext, LM or NTLM password. > * (sync version) >@@ -627,8 +585,11 @@ static NTSTATUS auth_generate_session_info_pac(struct auth4_context *auth_ctx, > TALLOC_CTX *tmp_ctx; > > if (!pac_blob) { >- return auth_generate_session_info_principal(auth_ctx, mem_ctx, principal_name, >- NULL, session_info_flags, session_info); >+ /* >+ * This should already be catched at the main >+ * gensec layer, but better check twice >+ */ >+ return NT_STATUS_INTERNAL_ERROR; > } > > tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context"); >diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c >index a521bc94bc4..dbbf97665db 100644 >--- a/source4/auth/ntlm/auth_sam.c >+++ b/source4/auth/ntlm/auth_sam.c >@@ -938,22 +938,11 @@ static NTSTATUS authsam_want_check(struct auth_method_context *ctx, > return NT_STATUS_OK; > } > >-/* Wrapper for the auth subsystem pointer */ >-static NTSTATUS authsam_get_user_info_dc_principal_wrapper(TALLOC_CTX *mem_ctx, >- struct auth4_context *auth_context, >- const char *principal, >- struct ldb_dn *user_dn, >- struct auth_user_info_dc **user_info_dc) >-{ >- return authsam_get_user_info_dc_principal(mem_ctx, auth_context->lp_ctx, auth_context->sam_ctx, >- principal, user_dn, user_info_dc); >-} > static const struct auth_operations sam_ignoredomain_ops = { > .name = "sam_ignoredomain", > .want_check = authsam_ignoredomain_want_check, > .check_password_send = authsam_check_password_send, > .check_password_recv = authsam_check_password_recv, >- .get_user_info_dc_principal = authsam_get_user_info_dc_principal_wrapper, > }; > > static const struct auth_operations sam_ops = { >@@ -961,7 +950,6 @@ static const struct auth_operations sam_ops = { > .want_check = authsam_want_check, > .check_password_send = authsam_check_password_send, > .check_password_recv = authsam_check_password_recv, >- .get_user_info_dc_principal = authsam_get_user_info_dc_principal_wrapper, > }; > > _PUBLIC_ NTSTATUS auth4_sam_init(TALLOC_CTX *); >-- >2.25.1 > > >From f2c836b78576c32ebd823f341122c0eacff5200f Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 21 Sep 2021 12:27:28 +0200 >Subject: [PATCH 072/190] CVE-2020-25717: s3:ntlm_auth: fix memory leaks in > ntlm_auth_generate_session_info_pac() > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/utils/ntlm_auth.c | 18 ++++++++++++------ > 1 file changed, 12 insertions(+), 6 deletions(-) > >diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c >index df1a87c8b55..b94a5268ea7 100644 >--- a/source3/utils/ntlm_auth.c >+++ b/source3/utils/ntlm_auth.c >@@ -818,23 +818,27 @@ static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_c > if (!p) { > DEBUG(3, ("[%s] Doesn't look like a valid principal\n", > princ_name)); >- return NT_STATUS_LOGON_FAILURE; >+ status = NT_STATUS_LOGON_FAILURE; >+ goto done; > } > > user = talloc_strndup(mem_ctx, princ_name, p - princ_name); > if (!user) { >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto done; > } > > realm = talloc_strdup(talloc_tos(), p + 1); > if (!realm) { >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto done; > } > > if (!strequal(realm, lp_realm())) { > DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm)); > if (!lp_allow_trusted_domains()) { >- return NT_STATUS_LOGON_FAILURE; >+ status = NT_STATUS_LOGON_FAILURE; >+ goto done; > } > } > >@@ -842,7 +846,8 @@ static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_c > domain = talloc_strdup(mem_ctx, > logon_info->info3.base.logon_domain.string); > if (!domain) { >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto done; > } > DEBUG(10, ("Domain is [%s] (using PAC)\n", domain)); > } else { >@@ -872,7 +877,8 @@ static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_c > domain = talloc_strdup(mem_ctx, realm); > } > if (!domain) { >- return NT_STATUS_NO_MEMORY; >+ status = NT_STATUS_NO_MEMORY; >+ goto done; > } > DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain)); > } >-- >2.25.1 > > >From 37ee0bb6fa82bf16eb4e1a960f84c2033d662012 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 21 Sep 2021 12:44:01 +0200 >Subject: [PATCH 073/190] CVE-2020-25717: s3:ntlm_auth: let > ntlm_auth_generate_session_info_pac() base the name on the PAC LOGON_INFO > only > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14801 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/utils/ntlm_auth.c | 91 ++++++++++++--------------------------- > 1 file changed, 28 insertions(+), 63 deletions(-) > >diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c >index b94a5268ea7..ad58d96100d 100644 >--- a/source3/utils/ntlm_auth.c >+++ b/source3/utils/ntlm_auth.c >@@ -790,10 +790,8 @@ static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_c > struct PAC_LOGON_INFO *logon_info = NULL; > char *unixuser; > NTSTATUS status; >- char *domain = NULL; >- char *realm = NULL; >- char *user = NULL; >- char *p; >+ const char *domain = ""; >+ const char *user = ""; > > tmp_ctx = talloc_new(mem_ctx); > if (!tmp_ctx) { >@@ -810,79 +808,46 @@ static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_c > if (!NT_STATUS_IS_OK(status)) { > goto done; > } >- } >- >- DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name)); >- >- p = strchr_m(princ_name, '@'); >- if (!p) { >- DEBUG(3, ("[%s] Doesn't look like a valid principal\n", >- princ_name)); >- status = NT_STATUS_LOGON_FAILURE; >+ } else { >+ status = NT_STATUS_ACCESS_DENIED; >+ DBG_WARNING("Kerberos ticket for[%s] has no PAC: %s\n", >+ princ_name, nt_errstr(status)); > goto done; > } > >- user = talloc_strndup(mem_ctx, princ_name, p - princ_name); >- if (!user) { >- status = NT_STATUS_NO_MEMORY; >- goto done; >+ if (logon_info->info3.base.account_name.string != NULL) { >+ user = logon_info->info3.base.account_name.string; >+ } else { >+ user = ""; >+ } >+ if (logon_info->info3.base.logon_domain.string != NULL) { >+ domain = logon_info->info3.base.logon_domain.string; >+ } else { >+ domain = ""; > } > >- realm = talloc_strdup(talloc_tos(), p + 1); >- if (!realm) { >- status = NT_STATUS_NO_MEMORY; >+ if (strlen(user) == 0 || strlen(domain) == 0) { >+ status = NT_STATUS_ACCESS_DENIED; >+ DBG_WARNING("Kerberos ticket for[%s] has invalid " >+ "account_name[%s]/logon_domain[%s]: %s\n", >+ princ_name, >+ logon_info->info3.base.account_name.string, >+ logon_info->info3.base.logon_domain.string, >+ nt_errstr(status)); > goto done; > } > >- if (!strequal(realm, lp_realm())) { >- DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm)); >+ DBG_NOTICE("Kerberos ticket principal name is [%s] " >+ "account_name[%s]/logon_domain[%s]\n", >+ princ_name, user, domain); >+ >+ if (!strequal(domain, lp_workgroup())) { > if (!lp_allow_trusted_domains()) { > status = NT_STATUS_LOGON_FAILURE; > goto done; > } > } > >- if (logon_info && logon_info->info3.base.logon_domain.string) { >- domain = talloc_strdup(mem_ctx, >- logon_info->info3.base.logon_domain.string); >- if (!domain) { >- status = NT_STATUS_NO_MEMORY; >- goto done; >- } >- DEBUG(10, ("Domain is [%s] (using PAC)\n", domain)); >- } else { >- >- /* If we have winbind running, we can (and must) shorten the >- username by using the short netbios name. Otherwise we will >- have inconsistent user names. With Kerberos, we get the >- fully qualified realm, with ntlmssp we get the short >- name. And even w2k3 does use ntlmssp if you for example >- connect to an ip address. */ >- >- wbcErr wbc_status; >- struct wbcDomainInfo *info = NULL; >- >- DEBUG(10, ("Mapping [%s] to short name using winbindd\n", >- realm)); >- >- wbc_status = wbcDomainInfo(realm, &info); >- >- if (WBC_ERROR_IS_OK(wbc_status)) { >- domain = talloc_strdup(mem_ctx, >- info->short_name); >- wbcFreeMemory(info); >- } else { >- DEBUG(3, ("Could not find short name: %s\n", >- wbcErrorString(wbc_status))); >- domain = talloc_strdup(mem_ctx, realm); >- } >- if (!domain) { >- status = NT_STATUS_NO_MEMORY; >- goto done; >- } >- DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain)); >- } >- > unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user); > if (!unixuser) { > status = NT_STATUS_NO_MEMORY; >-- >2.25.1 > > >From 9a0dccfda2634d1ffd8099d40e99bf0b9a7d97bb Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Mon, 4 Oct 2021 19:42:20 +0200 >Subject: [PATCH 074/190] CVE-2020-25717: s3:auth: let > auth3_generate_session_info_pac() delegate everything to > make_server_info_wbcAuthUserInfo() > >This consolidates the code paths used for NTLMSSP and Kerberos! > >I checked what we were already doing for NTLMSSP, which is this: > >a) source3/auth/auth_winbind.c calls wbcAuthenticateUserEx() >b) as a domain member we require a valid response from winbindd, > otherwise we'll return NT_STATUS_NO_LOGON_SERVERS >c) we call make_server_info_wbcAuthUserInfo(), which internally > calls make_server_info_info3() >d) auth_check_ntlm_password() calls > smb_pam_accountcheck(unix_username, rhost), where rhost > is only an ipv4 or ipv6 address (without reverse dns lookup) >e) from auth3_check_password_send/auth3_check_password_recv() > server_returned_info will be passed to auth3_generate_session_info(), > triggered by gensec_session_info(), which means we'll call into > create_local_token() in order to transform auth_serversupplied_info > into auth_session_info. > >For Kerberos gensec_session_info() will call >auth3_generate_session_info_pac() via the gensec_generate_session_info_pac() >helper function. The current logic is this: > >a) gensec_generate_session_info_pac() is the function that > evaluates the 'gensec:require_pac', which defaulted to 'no' > before. >b) auth3_generate_session_info_pac() called > wbcAuthenticateUserEx() in order to pass the PAC blob > to winbindd, but only to prime its cache, e.g. netsamlogon cache > and others. Most failures were just ignored. >c) If the PAC blob is available, it extracted the PAC_LOGON_INFO > from it. >d) Then we called the horrible get_user_from_kerberos_info() function: > - It uses a first part of the tickets principal name (before the @) > as username and combines that with the 'logon_info->base.logon_domain' > if the logon_info (PAC) is present. > - As a fallback without a PAC it's tries to ask winbindd for a mapping > from realm to netbios domain name. > - Finally is falls back to using the realm as netbios domain name > With this information is builds 'userdomain+winbind_separator+useraccount' > and calls map_username() followed by smb_getpwnam() with create=true, > Note this is similar to the make_server_info_info3() => check_account() > => smb_getpwnam() logic under 3. > - It also calls smb_pam_accountcheck(), but may pass the reverse DNS lookup name > instead of the ip address as rhost. > - It does some MAP_TO_GUEST_ON_BAD_UID logic and auto creates the > guest account. >e) We called create_info3_from_pac_logon_info() >f) make_session_info_krb5() calls gets called and triggers this: > - If get_user_from_kerberos_info() mapped to guest, it calls > make_server_info_guest() > - If create_info3_from_pac_logon_info() created a info3 from logon_info, > it calls make_server_info_info3() > - Without a PAC it tries pdb_getsampwnam()/make_server_info_sam() with > a fallback to make_server_info_pw() > From there it calls create_local_token() > >I tried to change auth3_generate_session_info_pac() to behave similar >to auth_winbind.c together with auth3_generate_session_info() as >a domain member, as we now rely on a PAC: > >a) As domain member we require a PAC and always call wbcAuthenticateUserEx() > and require a valid response! >b) we call make_server_info_wbcAuthUserInfo(), which internally > calls make_server_info_info3(). Note make_server_info_info3() > handles MAP_TO_GUEST_ON_BAD_UID and make_server_info_guest() > internally. >c) Similar to auth_check_ntlm_password() we now call > smb_pam_accountcheck(unix_username, rhost), where rhost > is only an ipv4 or ipv6 address (without reverse dns lookup) >d) From there it calls create_local_token() > >As standalone server (in an MIT realm) we continue >with the already existing code logic, which works without a PAC: >a) we keep smb_getpwnam() with create=true logic as it > also requires an explicit 'add user script' option. >b) In the following commits we assert that there's > actually no PAC in this mode, which means we can > remove unused and confusing code. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14646 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/auth/auth_generic.c | 137 ++++++++++++++++++++++++++++-------- > 1 file changed, 109 insertions(+), 28 deletions(-) > >diff --git a/source3/auth/auth_generic.c b/source3/auth/auth_generic.c >index 61abbab2a02..9c99d4119ea 100644 >--- a/source3/auth/auth_generic.c >+++ b/source3/auth/auth_generic.c >@@ -46,6 +46,7 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > uint32_t session_info_flags, > struct auth_session_info **session_info) > { >+ enum server_role server_role = lp_server_role(); > TALLOC_CTX *tmp_ctx; > struct PAC_LOGON_INFO *logon_info = NULL; > struct netr_SamInfo3 *info3_copy = NULL; >@@ -54,39 +55,59 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > char *ntuser; > char *ntdomain; > char *username; >- char *rhost; >+ const char *rhost; > struct passwd *pw; > NTSTATUS status; >- int rc; > > tmp_ctx = talloc_new(mem_ctx); > if (!tmp_ctx) { > return NT_STATUS_NO_MEMORY; > } > >- if (pac_blob) { >-#ifdef HAVE_KRB5 >+ if (tsocket_address_is_inet(remote_address, "ip")) { >+ rhost = tsocket_address_inet_addr_string( >+ remote_address, tmp_ctx); >+ if (rhost == NULL) { >+ status = NT_STATUS_NO_MEMORY; >+ goto done; >+ } >+ } else { >+ rhost = "127.0.0.1"; >+ } >+ >+ if (server_role != ROLE_STANDALONE) { > struct wbcAuthUserParams params = { 0 }; > struct wbcAuthUserInfo *info = NULL; > struct wbcAuthErrorInfo *err = NULL; >+ struct auth_serversupplied_info *server_info = NULL; >+ char *original_user_name = NULL; >+ char *p = NULL; > wbcErr wbc_err; > >+ if (pac_blob == NULL) { >+ /* >+ * This should already be catched at the main >+ * gensec layer, but better check twice >+ */ >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto done; >+ } >+ > /* > * Let winbind decode the PAC. > * This will also store the user > * data in the netsamlogon cache. > * >- * We need to do this *before* we >- * call get_user_from_kerberos_info() >- * as that does a user lookup that >- * expects info in the netsamlogon cache. >- * >- * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=11259 >+ * This used to be a cache prime >+ * optimization, but now we delegate >+ * all logic to winbindd, as we require >+ * winbindd as domain member anyway. > */ > params.level = WBC_AUTH_USER_LEVEL_PAC; > params.password.pac.data = pac_blob->data; > params.password.pac.length = pac_blob->length; > >+ /* we are contacting the privileged pipe */ > become_root(); > wbc_err = wbcAuthenticateUserEx(¶ms, &info, &err); > unbecome_root(); >@@ -99,18 +120,90 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > */ > > switch (wbc_err) { >- case WBC_ERR_WINBIND_NOT_AVAILABLE: > case WBC_ERR_SUCCESS: > break; >+ case WBC_ERR_WINBIND_NOT_AVAILABLE: >+ status = NT_STATUS_NO_LOGON_SERVERS; >+ DBG_ERR("winbindd not running - " >+ "but required as domain member: %s\n", >+ nt_errstr(status)); >+ goto done; > case WBC_ERR_AUTH_ERROR: > status = NT_STATUS(err->nt_status); > wbcFreeMemory(err); > goto done; >+ case WBC_ERR_NO_MEMORY: >+ status = NT_STATUS_NO_MEMORY; >+ goto done; > default: > status = NT_STATUS_LOGON_FAILURE; > goto done; > } > >+ status = make_server_info_wbcAuthUserInfo(tmp_ctx, >+ info->account_name, >+ info->domain_name, >+ info, &server_info); >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(10, ("make_server_info_wbcAuthUserInfo failed: %s\n", >+ nt_errstr(status))); >+ goto done; >+ } >+ >+ /* We skip doing this step if the caller asked us not to */ >+ if (!(server_info->guest)) { >+ const char *unix_username = server_info->unix_name; >+ >+ /* We might not be root if we are an RPC call */ >+ become_root(); >+ status = smb_pam_accountcheck(unix_username, rhost); >+ unbecome_root(); >+ >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(3, ("check_ntlm_password: PAM Account for user [%s] " >+ "FAILED with error %s\n", >+ unix_username, nt_errstr(status))); >+ goto done; >+ } >+ >+ DEBUG(5, ("check_ntlm_password: PAM Account for user [%s] " >+ "succeeded\n", unix_username)); >+ } >+ >+ DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name)); >+ >+ p = strchr_m(princ_name, '@'); >+ if (!p) { >+ DEBUG(3, ("[%s] Doesn't look like a valid principal\n", >+ princ_name)); >+ status = NT_STATUS_LOGON_FAILURE; >+ goto done; >+ } >+ >+ original_user_name = talloc_strndup(tmp_ctx, princ_name, p - princ_name); >+ if (original_user_name == NULL) { >+ status = NT_STATUS_NO_MEMORY; >+ goto done; >+ } >+ >+ status = create_local_token(mem_ctx, >+ server_info, >+ NULL, >+ original_user_name, >+ session_info); >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(10, ("create_local_token failed: %s\n", >+ nt_errstr(status))); >+ goto done; >+ } >+ >+ goto session_info_ready; >+ } >+ >+ /* This is the standalone legacy code path */ >+ >+ if (pac_blob != NULL) { >+#ifdef HAVE_KRB5 > status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL, > NULL, NULL, 0, &logon_info); > #else >@@ -121,22 +214,6 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > } > } > >- rc = get_remote_hostname(remote_address, >- &rhost, >- tmp_ctx); >- if (rc < 0) { >- status = NT_STATUS_NO_MEMORY; >- goto done; >- } >- if (strequal(rhost, "UNKNOWN")) { >- rhost = tsocket_address_inet_addr_string(remote_address, >- tmp_ctx); >- if (rhost == NULL) { >- status = NT_STATUS_NO_MEMORY; >- goto done; >- } >- } >- > status = get_user_from_kerberos_info(tmp_ctx, rhost, > princ_name, logon_info, > &is_mapped, &is_guest, >@@ -170,6 +247,8 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > goto done; > } > >+session_info_ready: >+ > /* setup the string used by %U */ > set_current_user_info((*session_info)->unix_info->sanitized_username, > (*session_info)->unix_info->unix_name, >@@ -179,7 +258,9 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > lp_load_with_shares(get_dyn_CONFIGFILE()); > > DEBUG(5, (__location__ "OK: user: %s domain: %s client: %s\n", >- ntuser, ntdomain, rhost)); >+ (*session_info)->info->account_name, >+ (*session_info)->info->domain_name, >+ rhost)); > > status = NT_STATUS_OK; > >-- >2.25.1 > > >From 9c7a7140185a892af663fc4a834047a8f2046896 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 5 Oct 2021 17:14:01 +0200 >Subject: [PATCH 075/190] CVE-2020-25717: selftest: configure 'ktest' env with > winbindd and idmap_autorid > >The 'ktest' environment was/is designed to test kerberos in an active >directory member setup. It was created at a time we wanted to test >smbd/winbindd with kerberos without having the source4 ad dc available. > >This still applies to testing the build with system krb5 libraries >but without relying on a running ad dc. > >As a domain member setup requires a running winbindd, we should test it >that way, in order to reflect a valid setup. > >As a side effect it provides a way to demonstrate that we can accept >smb connections authenticated via kerberos, but no connection to >a domain controller! In order get this working offline, we need an >idmap backend with ID_TYPE_BOTH support, so we use 'autorid', which >should be the default choice. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14646 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > selftest/knownfail.d/ktest | 26 -------------------------- > selftest/target/Samba3.pm | 12 +++++------- > 2 files changed, 5 insertions(+), 33 deletions(-) > delete mode 100644 selftest/knownfail.d/ktest > >diff --git a/selftest/knownfail.d/ktest b/selftest/knownfail.d/ktest >deleted file mode 100644 >index 809612ba0b9..00000000000 >--- a/selftest/knownfail.d/ktest >+++ /dev/null >@@ -1,26 +0,0 @@ >-^samba3.rpc.lsa.lookupsids.krb5.with.old.ccache.ncacn_np.with..smb2...lsa.LookupSidsReply.ktest >-^samba3.rpc.lsa.lookupsids.krb5.ncacn_np.with..smb2...lsa.LookupSidsReply.ktest >-^samba3.blackbox.rpcclient.krb5.ncacn_np.with..krb5...rpcclient.ktest:local >-^samba3.blackbox.rpcclient.krb5.ncacn_np.with..spnego,krb5...rpcclient.ktest:local >-^samba3.rpc.lsa.lookupsids.krb5.with.old.ccache.ncacn_np.with..smb2,connect...lsa.LookupSidsReply.ktest >-^samba3.rpc.lsa.lookupsids.krb5.ncacn_np.with..smb2,connect...lsa.LookupSidsReply.ktest >-^samba3.rpc.lsa.lookupsids.krb5.with.old.ccache.ncacn_np.with..smb2,packet...lsa.LookupSidsReply.ktest >-^samba3.rpc.lsa.lookupsids.krb5.ncacn_np.with..smb2,packet...lsa.LookupSidsReply.ktest >-^samba3.blackbox.rpcclient.krb5.ncacn_np.with..krb5,packet...rpcclient.ktest:local >-^samba3.blackbox.rpcclient.krb5.ncacn_np.with..spnego,krb5,packet...rpcclient.ktest:local >-^samba3.rpc.lsa.lookupsids.krb5.with.old.ccache.ncacn_np.with..smb2,sign...lsa.LookupSidsReply.ktest >-^samba3.rpc.lsa.lookupsids.krb5.ncacn_np.with..smb2,sign...lsa.LookupSidsReply.ktest >-^samba3.blackbox.rpcclient.krb5.ncacn_np.with..krb5,sign...rpcclient.ktest:local >-^samba3.blackbox.rpcclient.krb5.ncacn_np.with..spnego,krb5,sign...rpcclient.ktest:local >-^samba3.rpc.lsa.lookupsids.krb5.with.old.ccache.ncacn_np.with..smb2,seal...lsa.LookupSidsReply.ktest >-^samba3.rpc.lsa.lookupsids.krb5.ncacn_np.with..smb2,seal...lsa.LookupSidsReply.ktest >-^samba3.blackbox.rpcclient.krb5.ncacn_np.with..krb5,seal...rpcclient.ktest:local >-^samba3.blackbox.rpcclient.krb5.ncacn_np.with..spnego,krb5,seal...rpcclient.ktest:local >-^samba3.blackbox.smbclient_krb5.old.ccache..smbclient.ktest:local >-^samba3.blackbox.smbclient_krb5.new.ccache..smbclient.ktest:local >-^samba3.blackbox.smbclient_large_file..krb5.smbclient.large.posix.write.read.ktest:local >-^samba3.blackbox.smbclient_large_file..krb5.cmp.of.read.and.written.files.ktest:local >-^samba3.blackbox.smbclient_krb5.old.ccache.--client-protection=encrypt.smbclient.ktest:local >-^samba3.blackbox.smbclient_krb5.new.ccache.--client-protection=encrypt.smbclient.ktest:local >-^samba3.blackbox.smbclient_large_file.--client-protection=encrypt.krb5.smbclient.large.posix.write.read.ktest:local >-^samba3.blackbox.smbclient_large_file.--client-protection=encrypt.krb5.cmp.of.read.and.written.files.ktest:local >diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm >index fb84e4de5b9..6fd8ab77650 100755 >--- a/selftest/target/Samba3.pm >+++ b/selftest/target/Samba3.pm >@@ -1954,7 +1954,6 @@ sub setup_ktest > workgroup = KTEST > realm = ktest.samba.example.com > security = ads >- username map = $prefix/lib/username.map > server signing = required > server min protocol = SMB3_00 > client max protocol = SMB3 >@@ -1962,6 +1961,10 @@ sub setup_ktest > # This disables NTLM auth against the local SAM, which > # we use can then test this setting by. > ntlm auth = disabled >+ >+ idmap config * : backend = autorid >+ idmap config * : range = 1000000-1999999 >+ idmap config * : rangesize = 100000 > "; > > my $ret = $self->provision( >@@ -1987,12 +1990,6 @@ sub setup_ktest > > $ret->{KRB5_CONFIG} = $ctx->{krb5_conf}; > >- open(USERMAP, ">$prefix/lib/username.map") or die("Unable to open $prefix/lib/username.map"); >- print USERMAP " >-$ret->{USERNAME} = KTEST\\Administrator >-"; >- close(USERMAP); >- > #This is the secrets.tdb created by 'net ads join' from Samba3 to a > #Samba4 DC with the same parameters as are being used here. The > #domain SID is S-1-5-21-1071277805-689288055-3486227160 >@@ -2044,6 +2041,7 @@ $ret->{USERNAME} = KTEST\\Administrator > if (not $self->check_or_start( > env_vars => $ret, > nmbd => "yes", >+ winbindd => "offline", > smbd => "yes")) { > return undef; > } >-- >2.25.1 > > >From 53a2f18805902e2c3ca870dc7e625963c33b069e Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Tue, 5 Oct 2021 18:12:49 +0200 >Subject: [PATCH 076/190] CVE-2020-25717: s3:auth: let > auth3_generate_session_info_pac() reject a PAC in standalone mode > >We should be strict in standalone mode, that we only support MIT realms >without a PAC in order to keep the code sane. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/auth/auth_generic.c | 29 +++++++++-------------------- > 1 file changed, 9 insertions(+), 20 deletions(-) > >diff --git a/source3/auth/auth_generic.c b/source3/auth/auth_generic.c >index 9c99d4119ea..5fb16213d28 100644 >--- a/source3/auth/auth_generic.c >+++ b/source3/auth/auth_generic.c >@@ -48,8 +48,6 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > { > enum server_role server_role = lp_server_role(); > TALLOC_CTX *tmp_ctx; >- struct PAC_LOGON_INFO *logon_info = NULL; >- struct netr_SamInfo3 *info3_copy = NULL; > bool is_mapped; > bool is_guest; > char *ntuser; >@@ -203,19 +201,20 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > /* This is the standalone legacy code path */ > > if (pac_blob != NULL) { >-#ifdef HAVE_KRB5 >- status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL, >- NULL, NULL, 0, &logon_info); >-#else >- status = NT_STATUS_ACCESS_DENIED; >-#endif >+ /* >+ * In standalone mode we don't expect a PAC! >+ * we only support MIT realms >+ */ >+ status = NT_STATUS_BAD_TOKEN_TYPE; >+ DBG_WARNING("Unexpected PAC for [%s] in standalone mode - %s\n", >+ princ_name, nt_errstr(status)); > if (!NT_STATUS_IS_OK(status)) { > goto done; > } > } > > status = get_user_from_kerberos_info(tmp_ctx, rhost, >- princ_name, logon_info, >+ princ_name, NULL, > &is_mapped, &is_guest, > &ntuser, &ntdomain, > &username, &pw); >@@ -226,19 +225,9 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > goto done; > } > >- /* Get the info3 from the PAC data if we have it */ >- if (logon_info) { >- status = create_info3_from_pac_logon_info(tmp_ctx, >- logon_info, >- &info3_copy); >- if (!NT_STATUS_IS_OK(status)) { >- goto done; >- } >- } >- > status = make_session_info_krb5(mem_ctx, > ntuser, ntdomain, username, pw, >- info3_copy, is_guest, is_mapped, NULL /* No session key for now, caller will sort it out */, >+ NULL, is_guest, is_mapped, NULL /* No session key for now, caller will sort it out */, > session_info); > if (!NT_STATUS_IS_OK(status)) { > DEBUG(1, ("Failed to map kerberos pac to server info (%s)\n", >-- >2.25.1 > > >From 9bc5cb7e9a5e242cf8df13b5ea6f1f589e57ba95 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 8 Oct 2021 17:59:59 +0200 >Subject: [PATCH 077/190] CVE-2020-25717: s3:auth: simplify > get_user_from_kerberos_info() by removing the unused logon_info argument > >This code is only every called in standalone mode on a MIT realm, >it means we never have a PAC and we also don't have winbindd arround. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/auth/auth_generic.c | 2 +- > source3/auth/proto.h | 1 - > source3/auth/user_krb5.c | 57 +++++++------------------------------ > 3 files changed, 11 insertions(+), 49 deletions(-) > >diff --git a/source3/auth/auth_generic.c b/source3/auth/auth_generic.c >index 5fb16213d28..2ddb9c6bf04 100644 >--- a/source3/auth/auth_generic.c >+++ b/source3/auth/auth_generic.c >@@ -214,7 +214,7 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > } > > status = get_user_from_kerberos_info(tmp_ctx, rhost, >- princ_name, NULL, >+ princ_name, > &is_mapped, &is_guest, > &ntuser, &ntdomain, > &username, &pw); >diff --git a/source3/auth/proto.h b/source3/auth/proto.h >index 036d4004c38..596cbd68a7f 100644 >--- a/source3/auth/proto.h >+++ b/source3/auth/proto.h >@@ -417,7 +417,6 @@ struct PAC_LOGON_INFO; > NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, > const char *cli_name, > const char *princ_name, >- struct PAC_LOGON_INFO *logon_info, > bool *is_mapped, > bool *mapped_to_guest, > char **ntuser, >diff --git a/source3/auth/user_krb5.c b/source3/auth/user_krb5.c >index 074e8c7eb71..7b69ca6c222 100644 >--- a/source3/auth/user_krb5.c >+++ b/source3/auth/user_krb5.c >@@ -31,7 +31,6 @@ > NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, > const char *cli_name, > const char *princ_name, >- struct PAC_LOGON_INFO *logon_info, > bool *is_mapped, > bool *mapped_to_guest, > char **ntuser, >@@ -40,8 +39,8 @@ NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, > struct passwd **_pw) > { > NTSTATUS status; >- char *domain = NULL; >- char *realm = NULL; >+ const char *domain = NULL; >+ const char *realm = NULL; > char *user = NULL; > char *p; > char *fuser = NULL; >@@ -62,55 +61,16 @@ NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, > return NT_STATUS_NO_MEMORY; > } > >- realm = talloc_strdup(talloc_tos(), p + 1); >- if (!realm) { >- return NT_STATUS_NO_MEMORY; >- } >+ realm = p + 1; > > if (!strequal(realm, lp_realm())) { > DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm)); > if (!lp_allow_trusted_domains()) { > return NT_STATUS_LOGON_FAILURE; > } >- } >- >- if (logon_info && logon_info->info3.base.logon_domain.string) { >- domain = talloc_strdup(mem_ctx, >- logon_info->info3.base.logon_domain.string); >- if (!domain) { >- return NT_STATUS_NO_MEMORY; >- } >- DEBUG(10, ("Domain is [%s] (using PAC)\n", domain)); >+ domain = realm; > } else { >- >- /* If we have winbind running, we can (and must) shorten the >- username by using the short netbios name. Otherwise we will >- have inconsistent user names. With Kerberos, we get the >- fully qualified realm, with ntlmssp we get the short >- name. And even w2k3 does use ntlmssp if you for example >- connect to an ip address. */ >- >- wbcErr wbc_status; >- struct wbcDomainInfo *info = NULL; >- >- DEBUG(10, ("Mapping [%s] to short name using winbindd\n", >- realm)); >- >- wbc_status = wbcDomainInfo(realm, &info); >- >- if (WBC_ERROR_IS_OK(wbc_status)) { >- domain = talloc_strdup(mem_ctx, >- info->short_name); >- wbcFreeMemory(info); >- } else { >- DEBUG(3, ("Could not find short name: %s\n", >- wbcErrorString(wbc_status))); >- domain = talloc_strdup(mem_ctx, realm); >- } >- if (!domain) { >- return NT_STATUS_NO_MEMORY; >- } >- DEBUG(10, ("Domain is [%s] (using Winbind)\n", domain)); >+ domain = lp_workgroup(); > } > > fuser = talloc_asprintf(mem_ctx, >@@ -175,7 +135,11 @@ NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, > return NT_STATUS_NO_MEMORY; > } > *ntuser = user; >- *ntdomain = domain; >+ *ntdomain = talloc_strdup(mem_ctx, domain); >+ if (*ntdomain == NULL) { >+ return NT_STATUS_NO_MEMORY; >+ } >+ > *_pw = pw; > > return NT_STATUS_OK; >@@ -282,7 +246,6 @@ NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx, > NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, > const char *cli_name, > const char *princ_name, >- struct PAC_LOGON_INFO *logon_info, > bool *is_mapped, > bool *mapped_to_guest, > char **ntuser, >-- >2.25.1 > > >From 1cf0ac642058c56dd66e51d1074f1aa7c9179ca9 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 8 Oct 2021 18:03:04 +0200 >Subject: [PATCH 078/190] CVE-2020-25717: s3:auth: simplify > make_session_info_krb5() by removing unused arguments > >This is only ever be called in standalone mode with an MIT realm, >so we don't have a PAC/info3 structure. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14556 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >--- > source3/auth/auth_generic.c | 2 +- > source3/auth/proto.h | 2 -- > source3/auth/user_krb5.c | 20 +------------------- > 3 files changed, 2 insertions(+), 22 deletions(-) > >diff --git a/source3/auth/auth_generic.c b/source3/auth/auth_generic.c >index 2ddb9c6bf04..fc7a7549e8e 100644 >--- a/source3/auth/auth_generic.c >+++ b/source3/auth/auth_generic.c >@@ -227,7 +227,7 @@ static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, > > status = make_session_info_krb5(mem_ctx, > ntuser, ntdomain, username, pw, >- NULL, is_guest, is_mapped, NULL /* No session key for now, caller will sort it out */, >+ is_guest, is_mapped, > session_info); > if (!NT_STATUS_IS_OK(status)) { > DEBUG(1, ("Failed to map kerberos pac to server info (%s)\n", >diff --git a/source3/auth/proto.h b/source3/auth/proto.h >index 596cbd68a7f..9bffce7a808 100644 >--- a/source3/auth/proto.h >+++ b/source3/auth/proto.h >@@ -428,9 +428,7 @@ NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx, > char *ntdomain, > char *username, > struct passwd *pw, >- const struct netr_SamInfo3 *info3, > bool mapped_to_guest, bool username_was_mapped, >- DATA_BLOB *session_key, > struct auth_session_info **session_info); > > /* The following definitions come from auth/auth_samba4.c */ >diff --git a/source3/auth/user_krb5.c b/source3/auth/user_krb5.c >index 7b69ca6c222..b8f37cbeee0 100644 >--- a/source3/auth/user_krb5.c >+++ b/source3/auth/user_krb5.c >@@ -150,9 +150,7 @@ NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx, > char *ntdomain, > char *username, > struct passwd *pw, >- const struct netr_SamInfo3 *info3, > bool mapped_to_guest, bool username_was_mapped, >- DATA_BLOB *session_key, > struct auth_session_info **session_info) > { > NTSTATUS status; >@@ -166,20 +164,6 @@ NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx, > return status; > } > >- } else if (info3) { >- /* pass the unmapped username here since map_username() >- will be called again in make_server_info_info3() */ >- >- status = make_server_info_info3(mem_ctx, >- ntuser, ntdomain, >- &server_info, >- info3); >- if (!NT_STATUS_IS_OK(status)) { >- DEBUG(1, ("make_server_info_info3 failed: %s!\n", >- nt_errstr(status))); >- return status; >- } >- > } else { > /* > * We didn't get a PAC, we have to make up the user >@@ -231,7 +215,7 @@ NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx, > > server_info->nss_token |= username_was_mapped; > >- status = create_local_token(mem_ctx, server_info, session_key, ntuser, session_info); >+ status = create_local_token(mem_ctx, server_info, NULL, ntuser, session_info); > talloc_free(server_info); > if (!NT_STATUS_IS_OK(status)) { > DEBUG(10,("failed to create local token: %s\n", >@@ -261,9 +245,7 @@ NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx, > char *ntdomain, > char *username, > struct passwd *pw, >- const struct netr_SamInfo3 *info3, > bool mapped_to_guest, bool username_was_mapped, >- DATA_BLOB *session_key, > struct auth_session_info **session_info) > { > return NT_STATUS_NOT_IMPLEMENTED; >-- >2.25.1 > > >From 5f673f5765198e77e6d0d3b9af935dcf460e8c5c Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 8 Oct 2021 15:49:31 +1300 >Subject: [PATCH 079/190] CVE-2020-25722 s4:dsdb:tests: Add missing self.fail() > calls > >Without these calls the tests could pass if an expected error did not >occur. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14832 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> > >[abartlet@samba.org Included in backport as changing ACLs while > ACL tests are not checking for unexpected success would be bad] >--- > source4/dsdb/tests/python/acl.py | 32 ++++++++++++++++++++++++++++++++ > 1 file changed, 32 insertions(+) > >diff --git a/source4/dsdb/tests/python/acl.py b/source4/dsdb/tests/python/acl.py >index dcb4017699d..c844eb7359f 100755 >--- a/source4/dsdb/tests/python/acl.py >+++ b/source4/dsdb/tests/python/acl.py >@@ -1646,6 +1646,8 @@ userPassword: thatsAcomplPASS1 > except LdbError as e31: > (num, _) = e31.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ pass # Not self.fail() as we normally want success. > > def test_reset_password3(self): > """Grant WP and see what happens (unicodePwd)""" >@@ -1707,6 +1709,8 @@ userPassword: thatsAcomplPASS1 > except LdbError as e34: > (num, _) = e34.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ pass # Not self.fail() as we normally want success > > > class AclExtendedTests(AclTests): >@@ -2023,6 +2027,8 @@ class AclSPNTests(AclTests): > except LdbError as e39: > (num, _) = e39.args > self.assertEqual(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) >+ else: >+ self.fail() > > mod = "(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;%s)" % str(self.user_sid1) > self.sd_utils.dacl_add_ace(ctx.acct_dn, mod) >@@ -2061,29 +2067,39 @@ class AclSPNTests(AclTests): > except LdbError as e40: > (num, _) = e40.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > try: > self.replace_spn(self.ldb_user1, ctx.acct_dn, "ldap/%s.%s/DomainDnsZones.%s" % > (ctx.myname, ctx.dnsdomain, ctx.dnsdomain)) > except LdbError as e41: > (num, _) = e41.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > try: > self.replace_spn(self.ldb_user1, ctx.acct_dn, "nosuchservice/%s/%s" % ("abcd", "abcd")) > except LdbError as e42: > (num, _) = e42.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > try: > self.replace_spn(self.ldb_user1, ctx.acct_dn, "GC/%s.%s/%s" % > (ctx.myname, ctx.dnsdomain, netbiosdomain)) > except LdbError as e43: > (num, _) = e43.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > try: > self.replace_spn(self.ldb_user1, ctx.acct_dn, "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s" % > (ctx.ntds_guid, ctx.dnsdomain)) > except LdbError as e44: > (num, _) = e44.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > > def test_computer_spn(self): > # with WP, any value can be set >@@ -2129,6 +2145,8 @@ class AclSPNTests(AclTests): > except LdbError as e45: > (num, _) = e45.args > self.assertEqual(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) >+ else: >+ self.fail() > > mod = "(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;%s)" % str(self.user_sid1) > self.sd_utils.dacl_add_ace(self.computerdn, mod) >@@ -2147,41 +2165,55 @@ class AclSPNTests(AclTests): > except LdbError as e46: > (num, _) = e46.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > try: > self.replace_spn(self.ldb_user1, self.computerdn, "HOST/%s.%s/%s" % > (self.computername, self.dcctx.dnsdomain, netbiosdomain)) > except LdbError as e47: > (num, _) = e47.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > try: > self.replace_spn(self.ldb_user1, self.computerdn, "HOST/%s/%s" % > (self.computername, self.dcctx.dnsdomain)) > except LdbError as e48: > (num, _) = e48.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > try: > self.replace_spn(self.ldb_user1, self.computerdn, "HOST/%s.%s/%s" % > (self.computername, self.dcctx.dnsdomain, self.dcctx.dnsdomain)) > except LdbError as e49: > (num, _) = e49.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > try: > self.replace_spn(self.ldb_user1, self.computerdn, "GC/%s.%s/%s" % > (self.computername, self.dcctx.dnsdomain, self.dcctx.dnsforest)) > except LdbError as e50: > (num, _) = e50.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > try: > self.replace_spn(self.ldb_user1, self.computerdn, "ldap/%s/%s" % (self.computername, netbiosdomain)) > except LdbError as e51: > (num, _) = e51.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > try: > self.replace_spn(self.ldb_user1, self.computerdn, "ldap/%s.%s/ForestDnsZones.%s" % > (self.computername, self.dcctx.dnsdomain, self.dcctx.dnsdomain)) > except LdbError as e52: > (num, _) = e52.args > self.assertEqual(num, ERR_CONSTRAINT_VIOLATION) >+ else: >+ self.fail() > > def test_spn_rwdc(self): > self.dc_spn_test(self.dcctx) >-- >2.25.1 > > >From 2a844e51ea375a2328944ad9e2828b792a2f6470 Mon Sep 17 00:00:00 2001 >From: Nadezhda Ivanova <nivanova@symas.com> >Date: Mon, 25 Oct 2021 14:54:56 +0300 >Subject: [PATCH 080/190] CVE-2020-25722: s4-acl: test Control Access Rights > honor the Applies-to attribute > >Validate Writes and Control Access Rights should only grant access if the >object is of the type listed in the Right's appliesTo attribute. >Tests to verify this behavior > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14832 > >Signed-off-by: Nadezhda Ivanova <nivanova@symas.com> >Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > selftest/knownfail.d/bug-14832 | 1 + > source4/dsdb/tests/python/acl.py | 17 +++++++++++++++++ > 2 files changed, 18 insertions(+) > create mode 100644 selftest/knownfail.d/bug-14832 > >diff --git a/selftest/knownfail.d/bug-14832 b/selftest/knownfail.d/bug-14832 >new file mode 100644 >index 00000000000..059a1778e65 >--- /dev/null >+++ b/selftest/knownfail.d/bug-14832 >@@ -0,0 +1 @@ >+^samba4.ldap.acl.python\(.*\).__main__.AclSPNTests.test_user_spn\(.*\) >\ No newline at end of file >diff --git a/source4/dsdb/tests/python/acl.py b/source4/dsdb/tests/python/acl.py >index c844eb7359f..b341f1b3332 100755 >--- a/source4/dsdb/tests/python/acl.py >+++ b/source4/dsdb/tests/python/acl.py >@@ -1925,6 +1925,8 @@ class AclSPNTests(AclTests): > self.computername = "testcomp8" > self.test_user = "spn_test_user8" > self.computerdn = "CN=%s,CN=computers,%s" % (self.computername, self.base_dn) >+ self.user_object = "user_with_spn" >+ self.user_object_dn = "CN=%s,CN=Users,%s" % (self.user_object, self.base_dn) > self.dc_dn = "CN=%s,OU=Domain Controllers,%s" % (self.dcname, self.base_dn) > self.site = "Default-First-Site-Name" > self.rodcctx = DCJoinContext(server=host, creds=creds, lp=lp, >@@ -1946,6 +1948,7 @@ class AclSPNTests(AclTests): > self.dcctx.cleanup_old_join() > delete_force(self.ldb_admin, "cn=%s,cn=computers,%s" % (self.computername, self.base_dn)) > delete_force(self.ldb_admin, self.get_user_dn(self.test_user)) >+ delete_force(self.ldb_admin, self.user_object_dn) > > del self.ldb_user1 > >@@ -2221,6 +2224,20 @@ class AclSPNTests(AclTests): > def test_spn_rodc(self): > self.dc_spn_test(self.rodcctx) > >+ def test_user_spn(self): >+ #grant SW to a regular user and try to set the spn on a user object >+ #should get ERR_INSUFFICIENT_ACCESS_RIGHTS, since Validate-SPN only applies to computer >+ self.ldb_admin.newuser(self.user_object, self.user_pass) >+ mod = "(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;%s)" % str(self.user_sid1) >+ self.sd_utils.dacl_add_ace(self.user_object_dn, mod) >+ try: >+ self.replace_spn(self.ldb_user1, self.user_object_dn, "nosuchservice/%s/%s" % ("abcd", "abcd")) >+ except LdbError as e60: >+ (num, _) = e60.args >+ self.assertEqual(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) >+ else: >+ self.fail() >+ > def test_delete_add_spn(self): > # Grant Validated-SPN property. > mod = f'(OA;;SW;{security.GUID_DRS_VALIDATE_SPN};;{self.user_sid1})' >-- >2.25.1 > > >From f6e13bdd12cc416f1b8bb604868ae865df543d8f Mon Sep 17 00:00:00 2001 >From: Nadezhda Ivanova <nivanova@symas.com> >Date: Mon, 18 Oct 2021 14:27:59 +0300 >Subject: [PATCH 081/190] CVE-2020-25722: s4-acl: Make sure Control Access > Rights honor the Applies-to attribute > >Validate Writes and Control Access Rights only grant access if the >object is of the type listed in the Right's appliesTo attribute. For >example, even though a Validated-SPN access may be granted to a user >object in the SD, it should only pass if the object is of class >computer This patch enforces the appliesTo attribute classes for >access checks from within the ldb stack. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14832 > >Signed-off-by: Nadezhda Ivanova <nivanova@symas.com> >Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > selftest/knownfail.d/bug-14832 | 1 - > source4/dsdb/common/util.c | 11 +++ > source4/dsdb/samdb/ldb_modules/acl.c | 87 +++++++++++++++++++---- > source4/dsdb/samdb/ldb_modules/acl_util.c | 40 +++++++++++ > source4/dsdb/samdb/ldb_modules/dirsync.c | 13 +++- > source4/dsdb/samdb/ldb_modules/samldb.c | 56 ++++++++------- > 6 files changed, 168 insertions(+), 40 deletions(-) > delete mode 100644 selftest/knownfail.d/bug-14832 > >diff --git a/selftest/knownfail.d/bug-14832 b/selftest/knownfail.d/bug-14832 >deleted file mode 100644 >index 059a1778e65..00000000000 >--- a/selftest/knownfail.d/bug-14832 >+++ /dev/null >@@ -1 +0,0 @@ >-^samba4.ldap.acl.python\(.*\).__main__.AclSPNTests.test_user_spn\(.*\) >\ No newline at end of file >diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c >index 769e589e265..9b4afa45215 100644 >--- a/source4/dsdb/common/util.c >+++ b/source4/dsdb/common/util.c >@@ -1182,6 +1182,17 @@ struct ldb_dn *samdb_sites_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx) > return new_dn; > } > >+struct ldb_dn *samdb_extended_rights_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx) >+{ >+ struct ldb_dn *new_dn; >+ >+ new_dn = ldb_dn_copy(mem_ctx, ldb_get_config_basedn(sam_ctx)); >+ if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Extended-Rights")) { >+ talloc_free(new_dn); >+ return NULL; >+ } >+ return new_dn; >+} > /* > work out the domain sid for the current open ldb > */ >diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c >index 802969d50d5..723c5ae07ca 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl.c >+++ b/source4/dsdb/samdb/ldb_modules/acl.c >@@ -701,7 +701,12 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx, > return LDB_SUCCESS; > } > >- ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module), >+ ret = acl_check_extended_right(tmp_ctx, >+ module, >+ req, >+ objectclass, >+ sd, >+ acl_user_token(module), > GUID_DRS_VALIDATE_SPN, > SEC_ADS_SELF_WRITE, > sid); >@@ -914,7 +919,7 @@ static int acl_add(struct ldb_module *module, struct ldb_request *req) > return ldb_next_request(module, req); > } > >-/* ckecks if modifications are allowed on "Member" attribute */ >+/* checks if modifications are allowed on "Member" attribute */ > static int acl_check_self_membership(TALLOC_CTX *mem_ctx, > struct ldb_module *module, > struct ldb_request *req, >@@ -928,6 +933,16 @@ static int acl_check_self_membership(TALLOC_CTX *mem_ctx, > struct ldb_context *ldb = ldb_module_get_ctx(module); > struct ldb_dn *user_dn; > struct ldb_message_element *member_el; >+ const struct ldb_message *msg = NULL; >+ >+ if (req->operation == LDB_MODIFY) { >+ msg = req->op.mod.message; >+ } else if (req->operation == LDB_ADD) { >+ msg = req->op.add.message; >+ } else { >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ > /* if we have wp, we can do whatever we like */ > if (acl_check_access_on_attribute(module, > mem_ctx, >@@ -938,13 +953,13 @@ static int acl_check_self_membership(TALLOC_CTX *mem_ctx, > return LDB_SUCCESS; > } > /* if we are adding/deleting ourselves, check for self membership */ >- ret = dsdb_find_dn_by_sid(ldb, mem_ctx, >- &acl_user_token(module)->sids[PRIMARY_USER_SID_INDEX], >+ ret = dsdb_find_dn_by_sid(ldb, mem_ctx, >+ &acl_user_token(module)->sids[PRIMARY_USER_SID_INDEX], > &user_dn); > if (ret != LDB_SUCCESS) { > return ret; > } >- member_el = ldb_msg_find_element(req->op.mod.message, "member"); >+ member_el = ldb_msg_find_element(msg, "member"); > if (!member_el) { > return ldb_operr(ldb); > } >@@ -958,13 +973,18 @@ static int acl_check_self_membership(TALLOC_CTX *mem_ctx, > return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; > } > } >- ret = acl_check_extended_right(mem_ctx, sd, acl_user_token(module), >+ ret = acl_check_extended_right(mem_ctx, >+ module, >+ req, >+ objectclass, >+ sd, >+ acl_user_token(module), > GUID_DRS_SELF_MEMBERSHIP, > SEC_ADS_SELF_WRITE, > sid); > if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { > dsdb_acl_debug(sd, acl_user_token(module), >- req->op.mod.message->dn, >+ msg->dn, > true, > 10); > } >@@ -1024,6 +1044,9 @@ static int acl_check_password_rights( > * so we don't have to strict verification of the input. > */ > ret = acl_check_extended_right(tmp_ctx, >+ module, >+ req, >+ objectclass, > sd, > acl_user_token(module), > GUID_DRS_USER_CHANGE_PASSWORD, >@@ -1047,7 +1070,12 @@ static int acl_check_password_rights( > * the only caller is samdb_set_password_internal(), > * so we don't have to strict verification of the input. > */ >- ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module), >+ ret = acl_check_extended_right(tmp_ctx, >+ module, >+ req, >+ objectclass, >+ sd, >+ acl_user_token(module), > GUID_DRS_FORCE_CHANGE_PASSWORD, > SEC_ADS_CONTROL_ACCESS, > sid); >@@ -1100,7 +1128,12 @@ static int acl_check_password_rights( > if (rep_attr_cnt > 0) { > pav->pwd_reset = true; > >- ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module), >+ ret = acl_check_extended_right(tmp_ctx, >+ module, >+ req, >+ objectclass, >+ sd, >+ acl_user_token(module), > GUID_DRS_FORCE_CHANGE_PASSWORD, > SEC_ADS_CONTROL_ACCESS, > sid); >@@ -1110,7 +1143,12 @@ static int acl_check_password_rights( > if (add_attr_cnt != del_attr_cnt) { > pav->pwd_reset = true; > >- ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module), >+ ret = acl_check_extended_right(tmp_ctx, >+ module, >+ req, >+ objectclass, >+ sd, >+ acl_user_token(module), > GUID_DRS_FORCE_CHANGE_PASSWORD, > SEC_ADS_CONTROL_ACCESS, > sid); >@@ -1120,7 +1158,12 @@ static int acl_check_password_rights( > if (add_val_cnt == 1 && del_val_cnt == 1) { > pav->pwd_reset = false; > >- ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module), >+ ret = acl_check_extended_right(tmp_ctx, >+ module, >+ req, >+ objectclass, >+ sd, >+ acl_user_token(module), > GUID_DRS_USER_CHANGE_PASSWORD, > SEC_ADS_CONTROL_ACCESS, > sid); >@@ -1134,7 +1177,12 @@ static int acl_check_password_rights( > if (add_val_cnt == 1 && del_val_cnt == 0) { > pav->pwd_reset = true; > >- ret = acl_check_extended_right(tmp_ctx, sd, acl_user_token(module), >+ ret = acl_check_extended_right(tmp_ctx, >+ module, >+ req, >+ objectclass, >+ sd, >+ acl_user_token(module), > GUID_DRS_FORCE_CHANGE_PASSWORD, > SEC_ADS_CONTROL_ACCESS, > sid); >@@ -1689,6 +1737,9 @@ static int acl_check_reanimate_tombstone(TALLOC_CTX *mem_ctx, > struct ldb_result *acl_res; > struct security_descriptor *sd = NULL; > struct dom_sid *sid = NULL; >+ const struct dsdb_schema *schema = NULL; >+ const struct dsdb_class *objectclass = NULL; >+ struct ldb_context *ldb = ldb_module_get_ctx(module); > static const char *acl_attrs[] = { > "nTSecurityDescriptor", > "objectClass", >@@ -1709,10 +1760,20 @@ static int acl_check_reanimate_tombstone(TALLOC_CTX *mem_ctx, > > ret = dsdb_get_sd_from_ldb_message(mem_ctx, req, acl_res->msgs[0], &sd); > sid = samdb_result_dom_sid(mem_ctx, acl_res->msgs[0], "objectSid"); >+ schema = dsdb_get_schema(ldb, req); >+ if (!schema) { >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ objectclass = dsdb_get_structural_oc_from_msg(schema, acl_res->msgs[0]); > if (ret != LDB_SUCCESS || !sd) { > return ldb_operr(ldb_module_get_ctx(module)); > } >- return acl_check_extended_right(mem_ctx, sd, acl_user_token(module), >+ return acl_check_extended_right(mem_ctx, >+ module, >+ req, >+ objectclass, >+ sd, >+ acl_user_token(module), > GUID_DRS_REANIMATE_TOMBSTONE, > SEC_ADS_CONTROL_ACCESS, sid); > } >diff --git a/source4/dsdb/samdb/ldb_modules/acl_util.c b/source4/dsdb/samdb/ldb_modules/acl_util.c >index f917d99517a..08a95c1c310 100644 >--- a/source4/dsdb/samdb/ldb_modules/acl_util.c >+++ b/source4/dsdb/samdb/ldb_modules/acl_util.c >@@ -197,6 +197,9 @@ fail: > > /* checks for validated writes */ > int acl_check_extended_right(TALLOC_CTX *mem_ctx, >+ struct ldb_module *module, >+ struct ldb_request *req, >+ const struct dsdb_class *objectclass, > struct security_descriptor *sd, > struct security_token *token, > const char *ext_right, >@@ -208,6 +211,43 @@ int acl_check_extended_right(TALLOC_CTX *mem_ctx, > uint32_t access_granted; > struct object_tree *root = NULL; > TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); >+ static const char *no_attrs[] = { NULL }; >+ struct ldb_result *extended_rights_res = NULL; >+ struct ldb_dn *extended_rights_dn = NULL; >+ struct ldb_context *ldb = ldb_module_get_ctx(module); >+ int ret = 0; >+ >+ /* >+ * Find the extended right and check if applies to >+ * the objectclass of the object >+ */ >+ extended_rights_dn = samdb_extended_rights_dn(ldb, req); >+ if (!extended_rights_dn) { >+ ldb_set_errstring(ldb, >+ "access_check: CN=Extended-Rights dn could not be generated!"); >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ >+ /* Note: we are checking only the structural object class. */ >+ ret = dsdb_module_search(module, req, &extended_rights_res, >+ extended_rights_dn, LDB_SCOPE_ONELEVEL, >+ no_attrs, >+ DSDB_FLAG_NEXT_MODULE | >+ DSDB_FLAG_AS_SYSTEM, >+ req, >+ "(&(rightsGuid=%s)(appliesTo=%s))", >+ ext_right, >+ GUID_string(tmp_ctx, >+ &(objectclass->schemaIDGUID))); >+ >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } else if (extended_rights_res->count == 0 ) { >+ ldb_debug(ldb, LDB_DEBUG_TRACE, >+ "acl_check_extended_right: Could not find appliesTo for %s\n", >+ ext_right); >+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; >+ } > > GUID_from_string(ext_right, &right); > >diff --git a/source4/dsdb/samdb/ldb_modules/dirsync.c b/source4/dsdb/samdb/ldb_modules/dirsync.c >index 0aa3edac3dc..fa57af49e8f 100644 >--- a/source4/dsdb/samdb/ldb_modules/dirsync.c >+++ b/source4/dsdb/samdb/ldb_modules/dirsync.c >@@ -1066,7 +1066,9 @@ static int dirsync_ldb_search(struct ldb_module *module, struct ldb_request *req > if (!(dirsync_ctl->flags & LDAP_DIRSYNC_OBJECT_SECURITY)) { > struct dom_sid *sid; > struct security_descriptor *sd = NULL; >- const char *acl_attrs[] = { "nTSecurityDescriptor", "objectSid", NULL }; >+ const char *acl_attrs[] = { "nTSecurityDescriptor", "objectSid", "objectClass", NULL }; >+ const struct dsdb_schema *schema = NULL; >+ const struct dsdb_class *objectclass = NULL; > /* > * If we don't have the flag and if we have the "replicate directory change" granted > * then we upgrade ourself to system to not be blocked by the acl >@@ -1096,7 +1098,14 @@ static int dirsync_ldb_search(struct ldb_module *module, struct ldb_request *req > if (ret != LDB_SUCCESS) { > return ret; > } >- ret = acl_check_extended_right(dsc, sd, acl_user_token(module), GUID_DRS_GET_CHANGES, SEC_ADS_CONTROL_ACCESS, sid); >+ schema = dsdb_get_schema(ldb, req); >+ if (!schema) { >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ objectclass = dsdb_get_structural_oc_from_msg(schema, acl_res->msgs[0]); >+ ret = acl_check_extended_right(dsc, module, req, objectclass, >+ sd, acl_user_token(module), >+ GUID_DRS_GET_CHANGES, SEC_ADS_CONTROL_ACCESS, sid); > > if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { > return ret; >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 140cc22cc53..edf153d9fb9 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -2196,12 +2196,15 @@ static int samldb_check_user_account_control_objectclass_invariants( > return LDB_SUCCESS; > } > >-static int samldb_get_domain_secdesc(struct samldb_ctx *ac, >- struct security_descriptor **domain_sd) >+static int samldb_get_domain_secdesc_and_oc(struct samldb_ctx *ac, >+ struct security_descriptor **domain_sd, >+ const struct dsdb_class **objectclass) > { >- const char * const sd_attrs[] = {"ntSecurityDescriptor", NULL}; >+ const char * const sd_attrs[] = {"ntSecurityDescriptor", "objectClass", NULL}; > struct ldb_result *res; > struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module)); >+ const struct dsdb_schema *schema = NULL; >+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module); > int ret = dsdb_module_search_dn(ac->module, ac, &res, > domain_dn, > sd_attrs, >@@ -2214,6 +2217,11 @@ static int samldb_get_domain_secdesc(struct samldb_ctx *ac, > return ldb_module_operr(ac->module); > } > >+ schema = dsdb_get_schema(ldb, ac->req); >+ if (!schema) { >+ return ldb_module_operr(ac->module);; >+ } >+ *objectclass = dsdb_get_structural_oc_from_msg(schema, res->msgs[0]); > return dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(ac->module), > ac, res->msgs[0], domain_sd); > >@@ -2233,6 +2241,7 @@ static int samldb_check_user_account_control_acl(struct samldb_ctx *ac, > bool need_acl_check = false; > struct security_token *user_token; > struct security_descriptor *domain_sd; >+ const struct dsdb_class *objectclass = NULL; > const struct uac_to_guid { > uint32_t uac; > uint32_t priv_to_change_from; >@@ -2318,7 +2327,7 @@ static int samldb_check_user_account_control_acl(struct samldb_ctx *ac, > return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; > } > >- ret = samldb_get_domain_secdesc(ac, &domain_sd); >+ ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass); > if (ret != LDB_SUCCESS) { > return ret; > } >@@ -2349,7 +2358,11 @@ static int samldb_check_user_account_control_acl(struct samldb_ctx *ac, > ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; > } > } else if (map[i].guid) { >- ret = acl_check_extended_right(ac, domain_sd, >+ ret = acl_check_extended_right(ac, >+ ac->module, >+ ac->req, >+ objectclass, >+ domain_sd, > user_token, > map[i].guid, > SEC_ADS_CONTROL_ACCESS, >@@ -2689,12 +2702,11 @@ static int samldb_check_pwd_last_set_acl(struct samldb_ctx *ac, > { > struct ldb_context *ldb = ldb_module_get_ctx(ac->module); > int ret = 0; >- struct ldb_result *res = NULL; >- const char * const sd_attrs[] = {"ntSecurityDescriptor", NULL}; > struct security_token *user_token = NULL; > struct security_descriptor *domain_sd = NULL; > struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module)); > const char *operation = ""; >+ const struct dsdb_class *objectclass = NULL; > > if (dsdb_module_am_system(ac->module)) { > return LDB_SUCCESS; >@@ -2716,24 +2728,15 @@ static int samldb_check_pwd_last_set_acl(struct samldb_ctx *ac, > return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; > } > >- ret = dsdb_module_search_dn(ac->module, ac, &res, >- domain_dn, >- sd_attrs, >- DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, >- ac->req); >- if (ret != LDB_SUCCESS) { >- return ret; >- } >- if (res->count != 1) { >- return ldb_module_operr(ac->module); >- } >- >- ret = dsdb_get_sd_from_ldb_message(ldb, ac, res->msgs[0], &domain_sd); >+ ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass); > if (ret != LDB_SUCCESS) { > return ret; > } >- >- ret = acl_check_extended_right(ac, domain_sd, >+ ret = acl_check_extended_right(ac, >+ ac->module, >+ ac->req, >+ objectclass, >+ domain_sd, > user_token, > GUID_DRS_UNEXPIRE_PASSWORD, > SEC_ADS_CONTROL_ACCESS, >@@ -3763,16 +3766,21 @@ static int samldb_check_sensitive_attributes(struct samldb_ctx *ac) > el = ldb_msg_find_element(ac->msg, "msDS-SecondaryKrbTgtNumber"); > if (el) { > struct security_descriptor *domain_sd; >+ const struct dsdb_class *objectclass = NULL; > /* > * msDS-SecondaryKrbTgtNumber allows the creator to > * become an RODC, this is trusted as an RODC > * account > */ >- ret = samldb_get_domain_secdesc(ac, &domain_sd); >+ ret = samldb_get_domain_secdesc_and_oc(ac, &domain_sd, &objectclass); > if (ret != LDB_SUCCESS) { > return ret; > } >- ret = acl_check_extended_right(ac, domain_sd, >+ ret = acl_check_extended_right(ac, >+ ac->module, >+ ac->req, >+ objectclass, >+ domain_sd, > user_token, > GUID_DRS_DS_INSTALL_REPLICA, > SEC_ADS_CONTROL_ACCESS, >-- >2.25.1 > > >From a19979d63932d4d217c7a6621c94143459021bc5 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Mon, 4 Oct 2021 12:56:42 +1300 >Subject: [PATCH 082/190] CVE-2020-25722 pytests: add reverse lookup dict for > LDB error codes > >You can give ldb_err() it a number, an LdbError, or a sequence of >numbers, and it will return the corresponding strings. Examples: > >ldb_err(68) # "LDB_ERR_ENTRY_ALREADY_EXISTS" >LDB_ERR_LUT[68] # "LDB_ERR_ENTRY_ALREADY_EXISTS" > >expected = (ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS, > ldb.ERR_INVALID_CREDENTIALS) >try: > foo() >except ldb.LdbError as e: > self.fail(f"got {ldb_err(e)}, expected one of {ldb_err(expected)}") > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > python/samba/tests/__init__.py | 16 ++++++++++++++++ > 1 file changed, 16 insertions(+) > >diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py >index 9ed8db7d337..1b43359e3e8 100644 >--- a/python/samba/tests/__init__.py >+++ b/python/samba/tests/__init__.py >@@ -61,6 +61,22 @@ BINDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), > > HEXDUMP_FILTER = bytearray([x if ((len(repr(chr(x))) == 3) and (x < 127)) else ord('.') for x in range(256)]) > >+LDB_ERR_LUT = {v: k for k,v in vars(ldb).items() if k.startswith('ERR_')} >+ >+def ldb_err(v): >+ if isinstance(v, ldb.LdbError): >+ v = v.args[0] >+ >+ if v in LDB_ERR_LUT: >+ return LDB_ERR_LUT[v] >+ >+ try: >+ return f"[{', '.join(LDB_ERR_LUT.get(x, x) for x in v)}]" >+ except TypeError as e: >+ print(e) >+ return v >+ >+ > def DynamicTestCase(cls): > cls.setUpDynamicTestCases() > return cls >-- >2.25.1 > > >From 904099c527da0ed43e900aabb1dd089a0670e857 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Sun, 24 Oct 2021 15:18:05 +1300 >Subject: [PATCH 083/190] CVE-2020-25722 pytest: assertRaisesLdbError invents a > message if you're lazy > >This makes it easier to convert tests that don't have good messages. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > python/samba/tests/__init__.py | 2 ++ > 1 file changed, 2 insertions(+) > >diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py >index 1b43359e3e8..ab366c7ed30 100644 >--- a/python/samba/tests/__init__.py >+++ b/python/samba/tests/__init__.py >@@ -208,6 +208,8 @@ class TestCase(unittest.TestCase): > > def assertRaisesLdbError(self, errcode, message, f, *args, **kwargs): > """Assert a function raises a particular LdbError.""" >+ if message is None: >+ message = f"{f.__name__}(*{args}, **{kwargs})" > try: > f(*args, **kwargs) > except ldb.LdbError as e: >-- >2.25.1 > > >From 4ed3a31a2a036c9a0cd982993118fed85dc1fe59 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 11 Aug 2021 16:56:07 +1200 >Subject: [PATCH 084/190] CVE-2020-25722 s4/dsdb/cracknames: always free > tmp_ctx in spn_alias > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/dsdb/samdb/cracknames.c | 6 +++++- > 1 file changed, 5 insertions(+), 1 deletion(-) > >diff --git a/source4/dsdb/samdb/cracknames.c b/source4/dsdb/samdb/cracknames.c >index f298ef3df6f..0aefaa1e58e 100644 >--- a/source4/dsdb/samdb/cracknames.c >+++ b/source4/dsdb/samdb/cracknames.c >@@ -101,10 +101,12 @@ static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, stru > > service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services"); > if ( ! ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb_ctx))) { >+ talloc_free(tmp_ctx); > return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; > } > service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn); > if ( ! service_dn_str) { >+ talloc_free(tmp_ctx); > return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; > } > >@@ -113,13 +115,15 @@ static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, stru > > if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { > DEBUG(1, ("ldb_search: dn: %s not found: %s\n", service_dn_str, ldb_errstring(ldb_ctx))); >+ talloc_free(tmp_ctx); > return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; > } else if (ret == LDB_ERR_NO_SUCH_OBJECT) { > DEBUG(1, ("ldb_search: dn: %s not found\n", service_dn_str)); >+ talloc_free(tmp_ctx); > return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; > } else if (res->count != 1) { >- talloc_free(res); > DEBUG(1, ("ldb_search: dn: %s not found\n", service_dn_str)); >+ talloc_free(tmp_ctx); > return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; > } > >-- >2.25.1 > > >From 72f284d48b245e2545d26f7c35397f22149def93 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Tue, 10 Aug 2021 23:02:36 +0000 >Subject: [PATCH 085/190] CVE-2020-25722 s4/cracknames: lookup_spn_alias > doesn't need krb5 context > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/dsdb/samdb/cracknames.c | 7 +++---- > 1 file changed, 3 insertions(+), 4 deletions(-) > >diff --git a/source4/dsdb/samdb/cracknames.c b/source4/dsdb/samdb/cracknames.c >index 0aefaa1e58e..7313a3247c7 100644 >--- a/source4/dsdb/samdb/cracknames.c >+++ b/source4/dsdb/samdb/cracknames.c >@@ -74,9 +74,9 @@ static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_con > > info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; > return WERR_OK; >-} >+} > >-static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, struct ldb_context *ldb_ctx, >+static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(struct ldb_context *ldb_ctx, > TALLOC_CTX *mem_ctx, > const char *alias_from, > char **alias_to) >@@ -221,8 +221,7 @@ static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_c > dns_name = (const char *)component->data; > > /* MAP it */ >- namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context, >- sam_ctx, mem_ctx, >+ namestatus = LDB_lookup_spn_alias(sam_ctx, mem_ctx, > service, &new_service); > > if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) { >-- >2.25.1 > > >From bf3aa8755a6a0df46582a372159f1f3eac342654 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 28 Jul 2021 05:38:50 +0000 >Subject: [PATCH 086/190] CVE-2020-25722 samba-tool spn: accept -H for database > url > >Following the convention and making testing easier > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > python/samba/netcmd/spn.py | 33 ++++++++++++++++++++++----------- > 1 file changed, 22 insertions(+), 11 deletions(-) > >diff --git a/python/samba/netcmd/spn.py b/python/samba/netcmd/spn.py >index f0069460e3e..46e9c59272a 100644 >--- a/python/samba/netcmd/spn.py >+++ b/python/samba/netcmd/spn.py >@@ -18,7 +18,6 @@ > > import samba.getopt as options > import ldb >-from samba import provision > from samba.samdb import SamDB > from samba.auth import system_session > from samba.netcmd.common import _get_user_realm_domain >@@ -40,14 +39,20 @@ class cmd_spn_list(Command): > "credopts": options.CredentialsOptions, > "versionopts": options.VersionOptions, > } >+ takes_options = [ >+ Option("-H", "--URL", help="LDB URL for database or target server", >+ type=str, metavar="URL", dest="H"), >+ ] > > takes_args = ["user"] > >- def run(self, user, credopts=None, sambaopts=None, versionopts=None): >+ def run(self, user, H=None, >+ credopts=None, >+ sambaopts=None, >+ versionopts=None): > lp = sambaopts.get_loadparm() > creds = credopts.get_credentials(lp) >- paths = provision.provision_paths_from_lp(lp, lp.get("realm")) >- sam = SamDB(paths.samdb, session_info=system_session(), >+ sam = SamDB(H, session_info=system_session(), > credentials=creds, lp=lp) > # TODO once I understand how, use the domain info to naildown > # to the correct domain >@@ -82,17 +87,20 @@ class cmd_spn_add(Command): > "versionopts": options.VersionOptions, > } > takes_options = [ >+ Option("-H", "--URL", help="LDB URL for database or target server", >+ type=str, metavar="URL", dest="H"), > Option("--force", help="Force the addition of the spn" > " even it exists already", action="store_true"), >- ] >+ ] > takes_args = ["name", "user"] > >- def run(self, name, user, force=False, credopts=None, sambaopts=None, >+ def run(self, name, user, H=None, force=False, >+ credopts=None, >+ sambaopts=None, > versionopts=None): > lp = sambaopts.get_loadparm() > creds = credopts.get_credentials(lp) >- paths = provision.provision_paths_from_lp(lp, lp.get("realm")) >- sam = SamDB(paths.samdb, session_info=system_session(), >+ sam = SamDB(H, session_info=system_session(), > credentials=creds, lp=lp) > res = sam.search( > expression="servicePrincipalName=%s" % ldb.binary_encode(name), >@@ -141,15 +149,18 @@ class cmd_spn_delete(Command): > "credopts": options.CredentialsOptions, > "versionopts": options.VersionOptions, > } >+ takes_options = [ >+ Option("-H", "--URL", help="LDB URL for database or target server", >+ type=str, metavar="URL", dest="H"), >+ ] > > takes_args = ["name", "user?"] > >- def run(self, name, user=None, credopts=None, sambaopts=None, >+ def run(self, name, user=None, H=None, credopts=None, sambaopts=None, > versionopts=None): > lp = sambaopts.get_loadparm() > creds = credopts.get_credentials(lp) >- paths = provision.provision_paths_from_lp(lp, lp.get("realm")) >- sam = SamDB(paths.samdb, session_info=system_session(), >+ sam = SamDB(H, session_info=system_session(), > credentials=creds, lp=lp) > res = sam.search( > expression="servicePrincipalName=%s" % ldb.binary_encode(name), >-- >2.25.1 > > >From db6ba7ed372d21c5df3630d1bebb4bf54cc6ff08 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Fri, 27 Aug 2021 11:36:42 +1200 >Subject: [PATCH 087/190] CVE-2020-25722 samba-tool spn add: remove --force > option > >This did not actually *force* the creation of a duplicate SPN, it just >ignored the client-side check for the existing copy. Soon we are going >to enforce SPN uniqueness on the server side, and this --force will not >work. This will make the --force test fail, and if that tests fail, so >will others that depend the duplicate values. So we remove those tests. > >It is wrong-headed to try to make duplicate SPNs in any case, which is >probably why there is no sign of anyone ever having used this option. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > python/samba/netcmd/spn.py | 6 ++---- > source4/setup/tests/blackbox_spn.sh | 5 +---- > 2 files changed, 3 insertions(+), 8 deletions(-) > >diff --git a/python/samba/netcmd/spn.py b/python/samba/netcmd/spn.py >index 46e9c59272a..2676ff34fac 100644 >--- a/python/samba/netcmd/spn.py >+++ b/python/samba/netcmd/spn.py >@@ -89,12 +89,10 @@ class cmd_spn_add(Command): > takes_options = [ > Option("-H", "--URL", help="LDB URL for database or target server", > type=str, metavar="URL", dest="H"), >- Option("--force", help="Force the addition of the spn" >- " even it exists already", action="store_true"), > ] > takes_args = ["name", "user"] > >- def run(self, name, user, H=None, force=False, >+ def run(self, name, user, H=None, > credopts=None, > sambaopts=None, > versionopts=None): >@@ -105,7 +103,7 @@ class cmd_spn_add(Command): > res = sam.search( > expression="servicePrincipalName=%s" % ldb.binary_encode(name), > scope=ldb.SCOPE_SUBTREE) >- if len(res) != 0 and not force: >+ if len(res) != 0: > raise CommandError("Service principal %s already" > " affected to another user" % name) > >diff --git a/source4/setup/tests/blackbox_spn.sh b/source4/setup/tests/blackbox_spn.sh >index 429ace9494f..764ded4c88b 100755 >--- a/source4/setup/tests/blackbox_spn.sh >+++ b/source4/setup/tests/blackbox_spn.sh >@@ -22,11 +22,8 @@ testit "addspn" $PYTHON $samba_tool spn add FOO/bar Administrator $CONFIG > testit "delspn" $PYTHON $samba_tool spn delete FOO/bar $CONFIG > testit "readdspn" $PYTHON $samba_tool spn add FOO/bar Administrator $CONFIG > testit_expect_failure "failexistingspn" $PYTHON $samba_tool spn add FOO/bar Guest $CONFIG >-testit "existingspnforce" $PYTHON $samba_tool spn add --force FOO/bar Guest $CONFIG > testit_expect_failure "faildelspnnotgooduser" $PYTHON $samba_tool spn delete FOO/bar krbtgt $CONFIG >-testit_expect_failure "faildelspnmoreoneuser" $PYTHON $samba_tool spn delete FOO/bar $CONFIG >-testit "deluserspn" $PYTHON $samba_tool spn delete FOO/bar Guest $CONFIG >-testit "dellastuserspn" $PYTHON $samba_tool spn delete FOO/bar $CONFIG >+testit "deluserspn" $PYTHON $samba_tool spn delete FOO/bar $CONFIG > testit_expect_failure "faildelspn" $PYTHON $samba_tool spn delete FOO/bar $CONFIG > testit_expect_failure "failaddspn" $PYTHON $samba_tool spn add FOO/bar nonexistinguser $CONFIG > >-- >2.25.1 > > >From adb28ff91a4ed5e0e7025ed2c60fc6b8e7e7fa16 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 1 Sep 2021 18:35:02 +1200 >Subject: [PATCH 088/190] CVE-2020-25722 tests: blackbox samba-tool spn > non-admin test > >It is soon going to be impossible to add duplicate SPNs (short of >going behind DSDB's back on the local filesystem). Our test of adding >SPNs on non-admin users doubled as the test for adding a duplicate (using >--force). As --force is gone, we add these tests on Guest after the SPN >on Administrator is gone. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/setup/tests/blackbox_spn.sh | 2 ++ > 1 file changed, 2 insertions(+) > >diff --git a/source4/setup/tests/blackbox_spn.sh b/source4/setup/tests/blackbox_spn.sh >index 764ded4c88b..8f0258d0db8 100755 >--- a/source4/setup/tests/blackbox_spn.sh >+++ b/source4/setup/tests/blackbox_spn.sh >@@ -24,6 +24,8 @@ testit "readdspn" $PYTHON $samba_tool spn add FOO/bar Administrator $CONFIG > testit_expect_failure "failexistingspn" $PYTHON $samba_tool spn add FOO/bar Guest $CONFIG > testit_expect_failure "faildelspnnotgooduser" $PYTHON $samba_tool spn delete FOO/bar krbtgt $CONFIG > testit "deluserspn" $PYTHON $samba_tool spn delete FOO/bar $CONFIG >+testit "readd_spn_guest" $PYTHON $samba_tool spn add FOO/bar Guest $CONFIG >+testit "deluserspn_guest" $PYTHON $samba_tool spn delete FOO/bar Guest $CONFIG > testit_expect_failure "faildelspn" $PYTHON $samba_tool spn delete FOO/bar $CONFIG > testit_expect_failure "failaddspn" $PYTHON $samba_tool spn add FOO/bar nonexistinguser $CONFIG > >-- >2.25.1 > > >From 401956d478d416edcd5516fb9b4a8d487b8e227a Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Thu, 28 Oct 2021 09:45:36 +1300 >Subject: [PATCH 089/190] CVE-2020-25722 s4/provision: add host/ SPNs at the > start > >There are two reasons for this. Firstly, leaving SPNs unclaimed is >dangerous, as someone else could grab them first. Secondly, in some >circumstances (self join) we try to add a DNS/ SPN a little bit later >in provision. Under the rules we are introducing for CVE-2020-25722, >this will make our later attempts to add HOST/ fail. > >This causes a few errors in samba4.blackbox.dbcheck.* tests, which >assert that revivified old domains match stored reference versions. >Now they don't, because they have servicePrincipalNames. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > selftest/knownfail.d/cve-2020-25722-provision | 4 ++++ > source4/setup/provision_self_join.ldif | 9 +++++++-- > 2 files changed, 11 insertions(+), 2 deletions(-) > create mode 100644 selftest/knownfail.d/cve-2020-25722-provision > >diff --git a/selftest/knownfail.d/cve-2020-25722-provision b/selftest/knownfail.d/cve-2020-25722-provision >new file mode 100644 >index 00000000000..7fd4b4b3763 >--- /dev/null >+++ b/selftest/knownfail.d/cve-2020-25722-provision >@@ -0,0 +1,4 @@ >+samba4.blackbox.dbcheck.release-4-0-0 >+samba4.blackbox.dbcheck.release-4-0-0.quick >+samba4.blackbox.upgradeprovision.release-4-0-0 >+samba4.blackbox.functionalprep.check_databases_same >diff --git a/source4/setup/provision_self_join.ldif b/source4/setup/provision_self_join.ldif >index f77ac5710ec..92bf4d9cf8f 100644 >--- a/source4/setup/provision_self_join.ldif >+++ b/source4/setup/provision_self_join.ldif >@@ -15,11 +15,16 @@ localPolicyFlags: 0 > operatingSystem: Samba > operatingSystemVersion: ${SAMBA_VERSION_STRING} > sAMAccountName: ${NETBIOSNAME}$ >-# The "servicePrincipalName" updates are now handled by the "samba_spnupdate" >-# script > userAccountControl: 532480 > clearTextPassword:: ${MACHINEPASS_B64} > objectSid: ${DOMAINSID}-${DCRID} >+# While some "servicePrincipalName" updates might be handled by the >+# "samba_spnupdate" script, we need to get the basics in here before >+# we add any others. >+servicePrincipalName: HOST/${DNSNAME} >+servicePrincipalName: HOST/${NETBIOSNAME} >+servicePrincipalName: HOST/${DNSNAME}/${DNSNAME} >+ > > dn: CN=RID Set,CN=${NETBIOSNAME},OU=Domain Controllers,${DOMAINDN} > objectClass: rIDSet >-- >2.25.1 > > >From 09627acd26d2870c3ee18634c10362d754037dcc Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Thu, 28 Oct 2021 13:07:01 +1300 >Subject: [PATCH 090/190] CVE-2020-25722 blackbox/upgrades tests: ignore SPN > for ldapcmp > >We need to have the SPNs there before someone else nabs them, which >makes the re-provisioned old releases different from the reference >versions that we keep for this comparison. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > selftest/knownfail.d/cve-2020-25722-provision | 4 ---- > source4/setup/tests/blackbox_upgradeprovision.sh | 8 ++++---- > testprogs/blackbox/dbcheck-oldrelease.sh | 4 ++-- > testprogs/blackbox/functionalprep.sh | 2 +- > testprogs/blackbox/upgradeprovision-oldrelease.sh | 4 ++-- > 5 files changed, 9 insertions(+), 13 deletions(-) > delete mode 100644 selftest/knownfail.d/cve-2020-25722-provision > >diff --git a/selftest/knownfail.d/cve-2020-25722-provision b/selftest/knownfail.d/cve-2020-25722-provision >deleted file mode 100644 >index 7fd4b4b3763..00000000000 >--- a/selftest/knownfail.d/cve-2020-25722-provision >+++ /dev/null >@@ -1,4 +0,0 @@ >-samba4.blackbox.dbcheck.release-4-0-0 >-samba4.blackbox.dbcheck.release-4-0-0.quick >-samba4.blackbox.upgradeprovision.release-4-0-0 >-samba4.blackbox.functionalprep.check_databases_same >diff --git a/source4/setup/tests/blackbox_upgradeprovision.sh b/source4/setup/tests/blackbox_upgradeprovision.sh >index e53e7031cd2..58f8af7672e 100755 >--- a/source4/setup/tests/blackbox_upgradeprovision.sh >+++ b/source4/setup/tests/blackbox_upgradeprovision.sh >@@ -42,19 +42,19 @@ upgradeprovision_full() { > # really doesn't change anything. > > ldapcmp() { >- $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX/upgradeprovision/private/sam.ldb tdb://$PREFIX/upgradeprovision_reference/private/sam.ldb --two --skip-missing-dn >+ $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX/upgradeprovision/private/sam.ldb tdb://$PREFIX/upgradeprovision_reference/private/sam.ldb --two --skip-missing-dn --filter=servicePrincipalName > } > > ldapcmp_full() { >- $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX/upgradeprovision_full/private/sam.ldb tdb://$PREFIX/upgradeprovision_reference/private/sam.ldb --two --skip-missing-dn >+ $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX/upgradeprovision_full/private/sam.ldb tdb://$PREFIX/upgradeprovision_reference/private/sam.ldb --two --skip-missing-dn --filter=servicePrincipalName > } > > ldapcmp_sd() { >- $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX/upgradeprovision/private/sam.ldb tdb://$PREFIX/upgradeprovision_reference/private/sam.ldb --two --sd --skip-missing-dn >+ $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX/upgradeprovision/private/sam.ldb tdb://$PREFIX/upgradeprovision_reference/private/sam.ldb --two --sd --skip-missing-dn --filter=servicePrincipalName > } > > ldapcmp_full_sd() { >- $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX/upgradeprovision_full/private/sam.ldb tdb://$PREFIX/upgradeprovision_reference/private/sam.ldb --two --sd --skip-missing-dn >+ $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX/upgradeprovision_full/private/sam.ldb tdb://$PREFIX/upgradeprovision_reference/private/sam.ldb --two --sd --skip-missing-dn --filter=servicePrincipalName > } > > testit "upgradeprovision" upgradeprovision >diff --git a/testprogs/blackbox/dbcheck-oldrelease.sh b/testprogs/blackbox/dbcheck-oldrelease.sh >index 0866627c42b..1c558202bc5 100755 >--- a/testprogs/blackbox/dbcheck-oldrelease.sh >+++ b/testprogs/blackbox/dbcheck-oldrelease.sh >@@ -483,13 +483,13 @@ referenceprovision() { > > ldapcmp() { > if [ x$RELEASE = x"release-4-0-0" ]; then >- $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX_ABS/${RELEASE}_reference/private/sam.ldb tdb://$PREFIX_ABS/${RELEASE}/private/sam.ldb --two --skip-missing-dn --filter=dnsRecord,displayName,msDS-SupportedEncryptionTypes >+ $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX_ABS/${RELEASE}_reference/private/sam.ldb tdb://$PREFIX_ABS/${RELEASE}/private/sam.ldb --two --skip-missing-dn --filter=dnsRecord,displayName,msDS-SupportedEncryptionTypes,servicePrincipalName > fi > } > > ldapcmp_sd() { > if [ x$RELEASE = x"release-4-0-0" ]; then >- $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX_ABS/${RELEASE}_reference/private/sam.ldb tdb://$PREFIX_ABS/${RELEASE}/private/sam.ldb --two --sd --skip-missing-dn >+ $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX_ABS/${RELEASE}_reference/private/sam.ldb tdb://$PREFIX_ABS/${RELEASE}/private/sam.ldb --two --sd --skip-missing-dn --filter=servicePrincipalName > fi > } > >diff --git a/testprogs/blackbox/functionalprep.sh b/testprogs/blackbox/functionalprep.sh >index a5ac4b8bc7f..e9ab0854cff 100755 >--- a/testprogs/blackbox/functionalprep.sh >+++ b/testprogs/blackbox/functionalprep.sh >@@ -72,7 +72,7 @@ provision_2012r2() { > ldapcmp_ignore() { > # At some point we will need to ignore, but right now, it should be perfect > IGNORE_ATTRS=$1 >- $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX_ABS/$2/private/sam.ldb tdb://$PREFIX_ABS/$3/private/sam.ldb --two --skip-missing-dn --filter msDS-SupportedEncryptionTypes >+ $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX_ABS/$2/private/sam.ldb tdb://$PREFIX_ABS/$3/private/sam.ldb --two --skip-missing-dn --filter msDS-SupportedEncryptionTypes,servicePrincipalName > } > > ldapcmp() { >diff --git a/testprogs/blackbox/upgradeprovision-oldrelease.sh b/testprogs/blackbox/upgradeprovision-oldrelease.sh >index b02aef9f91f..c6251796878 100755 >--- a/testprogs/blackbox/upgradeprovision-oldrelease.sh >+++ b/testprogs/blackbox/upgradeprovision-oldrelease.sh >@@ -182,12 +182,12 @@ referenceprovision() { > > ldapcmp() { > if [ x$RELEASE != x"alpha13" ]; then >- $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX_ABS/${RELEASE}_upgrade_reference/private/sam.ldb tdb://$PREFIX_ABS/${RELEASE}_upgrade/private/sam.ldb --two --skip-missing-dn --filter=dnsRecord,displayName,msDS-SupportedEncryptionTypes >+ $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX_ABS/${RELEASE}_upgrade_reference/private/sam.ldb tdb://$PREFIX_ABS/${RELEASE}_upgrade/private/sam.ldb --two --skip-missing-dn --filter=dnsRecord,displayName,msDS-SupportedEncryptionTypes,servicePrincipalName > fi > } > > ldapcmp_full() { >- $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX_ABS/${RELEASE}_upgrade_reference/private/sam.ldb tdb://$PREFIX_ABS/${RELEASE}_upgrade_full/private/sam.ldb --two --filter=dNSProperty,dnsRecord,cn,displayName,versionNumber,systemFlags,msDS-HasInstantiatedNCs --skip-missing-dn >+ $PYTHON $BINDIR/samba-tool ldapcmp tdb://$PREFIX_ABS/${RELEASE}_upgrade_reference/private/sam.ldb tdb://$PREFIX_ABS/${RELEASE}_upgrade_full/private/sam.ldb --two --filter=dNSProperty,dnsRecord,cn,displayName,versionNumber,systemFlags,msDS-HasInstantiatedNCs,servicePrincipalName --skip-missing-dn > } > > ldapcmp_sd() { >-- >2.25.1 > > >From 8b56ea48faa902f5d2a442113845fa2bc5d94d93 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Mon, 13 Sep 2021 14:15:09 +1200 >Subject: [PATCH 091/190] CVE-2020-25722 pytest: test > sAMAccountName/userPrincipalName over ldap > >Because the sam account name + the dns host name is used as the >default user principal name, we need to check for collisions between >these. Fixes are coming in upcoming patches. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > python/samba/tests/ldap_upn_sam_account.py | 510 +++++++++++++++++++++ > selftest/knownfail.d/ldap_upn_sam_account | 16 + > source4/selftest/tests.py | 9 + > 3 files changed, 535 insertions(+) > create mode 100644 python/samba/tests/ldap_upn_sam_account.py > create mode 100644 selftest/knownfail.d/ldap_upn_sam_account > >diff --git a/python/samba/tests/ldap_upn_sam_account.py b/python/samba/tests/ldap_upn_sam_account.py >new file mode 100644 >index 00000000000..cc1cce9b6c3 >--- /dev/null >+++ b/python/samba/tests/ldap_upn_sam_account.py >@@ -0,0 +1,510 @@ >+# Unix SMB/CIFS implementation. >+# >+# Copyright 2021 (C) Catalyst IT Ltd >+# >+# 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 os >+import sys >+from samba.samdb import SamDB >+from samba.auth import system_session >+import ldb >+from samba.tests.subunitrun import SubunitOptions, TestProgram >+from samba.tests import TestCase, ldb_err >+from samba.tests import DynamicTestCase >+import samba.getopt as options >+import optparse >+from samba.colour import c_RED, c_GREEN, c_DARK_YELLOW >+import re >+import pprint >+from samba.dsdb import ( >+ UF_SERVER_TRUST_ACCOUNT, >+ UF_TRUSTED_FOR_DELEGATION, >+) >+ >+ >+# bad sAMAccountName characters from [MS-SAMR] >+# "3.1.1.6 Attribute Constraints for Originating Updates" >+BAD_SAM_CHARS = (''.join(chr(x) for x in range(0, 32)) + >+ '"/\\[]:|<>+=;?,*') >+ >+# 0x7f is *said* to be bad, but turns out to be fine. >+ALLEGED_BAD_SAM_CHARS = chr(127) >+ >+LATIN1_BAD_CHARS = set([chr(x) for x in range(129, 160)] + >+ list("ªºÿ") + >+ [chr(x) for x in range(0xc0, 0xc6)] + >+ [chr(x) for x in range(0xc7, 0xd7)] + >+ [chr(x) for x in range(0xd8, 0xde)] + >+ [chr(x) for x in range(0xe0, 0xe6)] + >+ [chr(x) for x in range(0xe7, 0xf7)] + >+ [chr(x) for x in range(0xf8, 0xfe)]) >+ >+ >+LATIN_EXTENDED_A_NO_CLASH = {306, 307, 330, 331, 338, 339, 358, 359, 383} >+ >+#XXX does '\x00' just truncate the string though? >+#XXX elsewhere we see "[\\\"|,/:<>+=;?*']" with "'" >+ >+ >+## UPN limits >+# max length 1024 UTF-8 bytes, following "rfc822" >+# for o365 sync https://docs.microsoft.com/en-us/microsoft-365/enterprise/prepare-for-directory-synchronization?view=o365-worldwide >+# max length is 113 [64 before @] "@" [48 after @] >+# invalid chars: '\\%&*+/=?{}|<>();:,[]"' >+# allowed chars: A â Z, a - z, 0 â 9, ' . - _ ! # ^ ~ >+# "Letters with diacritical marks, such as umlauts, accents, and tildes, are invalid characters." >+# >+# "@" can't be first >+# "The username cannot end with a period (.), an ampersand (&), a space, or an at sign (@)." >+# >+ >+# per RFC 822, «"a b" @ example.org» is >+ >+ >+ok = True >+bad = False >+report = 'report' >+exists = ldb.ERR_ENTRY_ALREADY_EXISTS >+ >+ >+if sys.stdout.isatty(): >+ c_doc = c_DARK_YELLOW >+else: >+ c_doc = lambda x: x >+ >+ >+def get_samdb(): >+ return SamDB(url=f"ldap://{SERVER}", >+ lp=LP, >+ session_info=system_session(), >+ credentials=CREDS) >+ >+ >+def format(s): >+ if type(s) is str: >+ s = s.format(realm=REALM.upper(), >+ lrealm=REALM.lower(), >+ other_realm=(REALM + ".another.example.net")) >+ return s >+ >+ >+class LdapUpnSamTestBase(TestCase): >+ """Make sure we can't add userPrincipalNames or sAMAccountNames that >+ implicitly collide. >+ """ >+ _disabled = False >+ >+ @classmethod >+ def setUpDynamicTestCases(cls): >+ if getattr(cls, '_disabled', False): >+ return >+ for doc, *rows in cls.cases: >+ name = re.sub(r'\W+', '_', doc) >+ cls.generate_dynamic_test("test_upn_sam", name, rows, doc) >+ >+ def setup_objects(self, rows): >+ objects = set(r[0] for r in rows) >+ for name in objects: >+ if ':' in name: >+ objtype, name = name.split(':', 1) >+ else: >+ objtype = 'user' >+ getattr(self, f'add_{objtype}')(name) >+ self.addCleanup(self.remove_object, name) >+ >+ def _test_upn_sam_with_args(self, rows, doc): >+ self.setup_objects(rows) >+ cdoc = c_doc(doc) >+ >+ for i, row in enumerate(rows): >+ if len(row) == 4: >+ obj, data, expected, op = row >+ else: >+ obj, data, expected = row >+ op = ldb.FLAG_MOD_REPLACE >+ >+ dn, dnsname = self.objects[obj] >+ sam, upn = None, None >+ if isinstance(data, dict): >+ sam = data.get('sam') >+ upn = data.get('upn') >+ elif isinstance(data, str): >+ if '@' in data: >+ upn = data >+ else: >+ sam = data >+ else: # bytes >+ if b'@' in data: >+ upn = data >+ else: >+ sam = data >+ >+ m = {"dn": dn} >+ >+ if upn is not None: >+ m["userPrincipalName"] = format(upn) >+ >+ if sam is not None: >+ m["sAMAccountName"] = format(sam) >+ >+ msg = ldb.Message.from_dict(self.samdb, m, op) >+ >+ if expected is bad: >+ try: >+ self.samdb.modify(msg) >+ except ldb.LdbError as e: >+ print(f"row {i+1} of '{cdoc}' failed as expected with " >+ f"{ldb_err(e)}\n") >+ continue >+ self.fail(f"row {i+1} of '{cdoc}' should have failed:\n" >+ f"{pprint.pformat(m)} on {obj}") >+ elif expected is ok: >+ try: >+ self.samdb.modify(msg) >+ except ldb.LdbError as e: >+ raise AssertionError( >+ f"row {i+1} of '{cdoc}' failed with {ldb_err(e)}:\n" >+ f"{pprint.pformat(m)} on {obj}") from None >+ elif expected is report: >+ try: >+ self.samdb.modify(msg) >+ print(f"row {i+1} of '{cdoc}' SUCCEEDED:\n" >+ f"{pprint.pformat(m)} on {obj}") >+ except ldb.LdbError as e: >+ print(f"row {i+1} of '{cdoc}' FAILED " >+ f"with {ldb_err(e)}:\n" >+ f"{pprint.pformat(m)} on {obj}") >+ >+ else: >+ try: >+ self.samdb.modify(msg) >+ except ldb.LdbError as e: >+ if hasattr(expected, '__contains__'): >+ if e.args[0] in expected: >+ continue >+ >+ if e.args[0] == expected: >+ continue >+ >+ self.fail(f"row {i+1} of '{cdoc}' " >+ f"should have failed with {ldb_err(expected)} " >+ f"but instead failed with {ldb_err(e)}:\n" >+ f"{pprint.pformat(m)} on {obj}") >+ self.fail(f"row {i+1} of '{cdoc}' " >+ f"should have failed with {ldb_err(expected)}:\n" >+ f"{pprint.pformat(m)} on {obj}") >+ >+ def add_dc(self, name): >+ dn = f"CN={name},OU=Domain Controllers,{self.base_dn}" >+ dnsname = f"{name}.{REALM}".lower() >+ self.samdb.add({ >+ "dn": dn, >+ "objectclass": "computer", >+ "userAccountControl": str(UF_SERVER_TRUST_ACCOUNT | >+ UF_TRUSTED_FOR_DELEGATION), >+ "dnsHostName": dnsname, >+ "carLicense": self.id() >+ }) >+ self.objects[name] = (dn, dnsname) >+ >+ def add_user(self, name): >+ dn = f"CN={name},{self.ou}" >+ self.samdb.add({ >+ "dn": dn, >+ "name": name, >+ "objectclass": "user", >+ "carLicense": self.id() >+ }) >+ self.objects[name] = (dn, None) >+ >+ def remove_object(self, name): >+ dn, dnsname = self.objects.pop(name) >+ self.samdb.delete(dn) >+ >+ def setUp(self): >+ super().setUp() >+ self.samdb = get_samdb() >+ self.base_dn = self.samdb.get_default_basedn() >+ self.short_id = self.id().rsplit('.', 1)[1][:63] >+ self.objects = {} >+ self.ou = f"OU={ self.short_id },{ self.base_dn }" >+ self.addCleanup(self.samdb.delete, self.ou, ["tree_delete:1"]) >+ self.samdb.add({"dn": self.ou, "objectclass": "organizationalUnit"}) >+ >+ >+@DynamicTestCase >+class LdapUpnSamTest(LdapUpnSamTestBase): >+ cases = [ >+ # The structure is >+ # ( «documentation/message that becomes test name», >+ # («short object id», «upn or sam or mapping», «expected»), >+ # («short object id», «upn or sam or mapping», «expected»), >+ # ..., >+ # ) >+ # >+ # where the first item is a one line string explaining the >+ # test, and subsequent items describe database modifications, >+ # to be applied in series. >+ # >+ # First is a short ID, which maps to an object DN. Second is >+ # either a string or a dictionary. >+ # >+ # * If a string, if it contains '@', it is a UPN, otherwise a >+ # samaccountname. >+ # >+ # * If a dictionary, it is a mapping of some of ['sam', 'upn'] >+ # to strings (in this way, you can add two attributes in one >+ # mesage, or attempt a samaccountname with '@'). >+ # >+ # expected can be «ok», «bad» (mapped to True and False, >+ # respectively), or a specific LDB error code, if that exact >+ # exception is wanted. >+ ("add good UPN", >+ ('A', 'a@{realm}', ok), >+ ), >+ ("add the same upn to different objects", >+ ('A', 'a@{realm}', ok), >+ ('B', 'a@{realm}', ldb.ERR_CONSTRAINT_VIOLATION), >+ ('B', 'a@{lrealm}', ldb.ERR_CONSTRAINT_VIOLATION), # lowercase realm >+ ), >+ ("replace UPN with itself", >+ ('A', 'a@{realm}', ok), >+ ('A', 'a@{realm}', ok), >+ ('A', 'a@{lrealm}', ok), >+ ), >+ ("replace SAM with itself", >+ ('A', 'a', ok), >+ ('A', 'a', ok), >+ ), >+ ("replace UPN realm", >+ ('A', 'a@{realm}', ok), >+ ('A', 'a@{other_realm}', ok), >+ ), >+ ("matching SAM and UPN", >+ ('A', 'a', ok), >+ ('A', 'a@{realm}', ok), >+ ), >+ ("matching SAM and UPN, other realm", >+ ('A', 'a', ok), >+ ('A', 'a@{other_realm}', ok), >+ ), >+ ("matching SAM and UPN, single message", >+ ('A', {'sam': 'a', 'upn': 'a@{realm}'}, ok), >+ ('A', {'sam': 'a', 'upn': 'a@{other_realm}'}, ok), >+ ), >+ ("different objects, different realms", >+ ('A', 'a@{realm}', ok), >+ ('B', 'a@{other_realm}', ok), >+ ), >+ ("different objects, same UPN, different case", >+ ('A', 'a@{realm}', ok), >+ ('B', 'A@{realm}', ldb.ERR_CONSTRAINT_VIOLATION), >+ ), >+ ("different objects, SAM after UPN", >+ ('A', 'a@{realm}', ok), >+ ('B', 'a', ldb.ERR_CONSTRAINT_VIOLATION), >+ ), >+ ("different objects, SAM before UPN", >+ ('A', 'a', ok), >+ ('B', 'a@{realm}', exists), >+ ), >+ ("different objects, SAM account clash", >+ ('A', 'a', ok), >+ ('B', 'a', exists), >+ ), >+ ("different objects, SAM account clash, different case", >+ ('A', 'a', ok), >+ ('B', 'A', exists), >+ ), >+ ("two way clash", >+ ('A', {'sam': 'x', 'upn': 'y@{realm}'}, ok), >+ # The sam account raises EXISTS while the UPN raises >+ # CONSTRAINT_VIOLATION. We don't really care in which order >+ # they are checked, so either error is ok. >+ ('B', {'sam': 'y', 'upn': 'x@{realm}'}, >+ (exists, ldb.ERR_CONSTRAINT_VIOLATION)), >+ ), >+ ("two way clash, other realm", >+ ('A', {'sam': 'x', 'upn': 'y@{other_realm}'}, ok), >+ ('B', {'sam': 'y', 'upn': 'x@{other_realm}'}, ok), >+ ), >+ # UPN versions of bad sam account names >+ ("UPN clash on other realm", >+ ('A', 'a@x.x', ok), >+ ('B', 'a@x.x', ldb.ERR_CONSTRAINT_VIOLATION), >+ ), >+ ("UPN same but for trailing spaces", >+ ('A', 'a@{realm}', ok), >+ ('B', 'a @{realm}', ok), >+ ), >+ # UPN has no at >+ ("UPN has no at", >+ ('A', {'upn': 'noat'}, ok), >+ ('B', {'upn': 'noat'}, ldb.ERR_CONSTRAINT_VIOLATION), >+ ('C', {'upn': 'NOAT'}, ldb.ERR_CONSTRAINT_VIOLATION), >+ ), >+ # UPN has non-ascii at, followed by real at. >+ ("UPN with non-ascii at vs real at", >+ ('A', {'upn': 'smallat﹫{realm}'}, ok), >+ ('B', {'upn': 'smallat@{realm}'}, ok), >+ ('C', {'upn': 'tagat\U000e0040{realm}'}, ok), >+ ('D', {'upn': 'tagat@{realm}'}, ok), >+ ), >+ ("UPN with unicode at vs real at, real at first", >+ ('B', {'upn': 'smallat@{realm}'}, ok), >+ ('A', {'upn': 'smallat﹫{realm}'}, ok), >+ ('D', {'upn': 'tagat@{realm}'}, ok), >+ ('C', {'upn': 'tagat\U000e0040{realm}'}, ok), >+ ), >+ ("UPN username too long", >+ # SPN soft limit 20; hard limit 256, overall UPN 1024 >+ ('A', 'a' * 25 + '@b.c', ok), >+ ('A', 'a' * 65 + '@b.c', ok), # Azure AD limit is 64 >+ ('A', 'a' * 257 + '@b.c', ok), # 256 is sam account name limit >+ ), >+ ("sam account name 20 long", >+ # SPN soft limit 20 >+ ('A', 'a' * 20, ok), >+ ), >+ ("UPN has two at signs", >+ ('A', 'a@{realm}', ok), >+ ('A', 'a@{realm}@{realm}', ok), >+ ('A', 'a@a.b', ok), >+ ('A', 'a@a@a.b', ok), >+ ), >+ ("SAM has at signs clashing upn second, non-realm", >+ ('A', {'sam': 'a@a.b'}, ok), >+ ('B', 'a@a.b@a.b', ok), # UPN won't clash with SAM, because realm >+ ), >+ ("SAM has at signs clashing upn second", >+ ('A', {'sam': 'a@{realm}'}, ok), >+ ('B', 'a@{realm}@{realm}', bad), # UPN would clashes with SAM >+ ), >+ ("SAM has at signs clashing upn first", >+ ('B', 'a@{realm}@{realm}', ok), >+ ('A', {'sam': 'a@{realm}'}, bad), >+ ), >+ ("spaces around at", >+ ('A', 'a name @ {realm}', ok), >+ ('B', 'a name @ {realm}', ldb.ERR_CONSTRAINT_VIOLATION), >+ ('B', 'a name @{realm}', ok), # because realm looks different >+ ('C', 'a name@{realm}', ok), >+ ('D', 'a name', ldb.ERR_CONSTRAINT_VIOLATION), >+ ('D', 'a name ', (exists, ldb.ERR_CONSTRAINT_VIOLATION)), # matches B >+ ), >+ ("SAM starts with at", >+ ('A', {'sam': '@{realm}'}, ok), >+ ('B', {'sam': '@a'}, ok), >+ ('C', {'sam': '@{realm}'}, exists), >+ ('C', {'sam': '@a'}, exists), >+ ('C', {'upn': '@{realm}@{realm}'}, bad), >+ ('C', {'upn': '@a@{realm}'}, bad), >+ ), >+ ("UPN starts with at", >+ ('A', {'upn': '@{realm}'}, ok), >+ ('B', {'upn': '@a@{realm}'}, ok), >+ ('C', {'upn': '@{realm}'}, bad), >+ ('C', {'sam': '@a'}, bad), >+ ), >+ ("SAM ends with at", >+ ('A', {'sam': '{realm}@'}, ok), >+ ('B', {'sam': 'a@'}, ok), >+ ('C', {'sam': '{realm}@'}, exists), >+ ('C', {'sam': 'a@'}, exists), >+ ('C', {'upn': 'a@@{realm}'}, bad), >+ ('C', {'upn': '{realm}@@{realm}'}, bad), >+ ), >+ ("UPN ends with at", >+ ('A', {'upn': '{realm}@'}, ok), >+ ('B', {'upn': '@a@{realm}@'}, ok), >+ ('C', {'upn': '{realm}@'}, bad), >+ ('C', {'sam': '@a@{realm}'}, ok), # not like B, because other realm >+ ), >+ ] >+ >+ >+@DynamicTestCase >+class LdapUpnSamSambaOnlyTest(LdapUpnSamTestBase): >+ # We don't run these ones outside of selftest, where we are >+ # probably testing against Windows and these are known failures. >+ _disabled = 'SAMBA_SELFTEST' not in os.environ >+ cases = [ >+ ("sam account name too long", >+ # SPN soft limit 20 >+ ('A', 'a' * 19, ok), >+ ('A', 'a' * 20, ok), >+ ('A', 'a' * 65, ok), >+ ('A', 'a' * 255, ok), >+ ('A', 'a' * 256, ok), >+ ('A', 'a' * 257, ldb.ERR_INVALID_ATTRIBUTE_SYNTAX), >+ ), >+ ("UPN username too long", >+ ('A', 'a' * 254 + '@' + 'b.c' * 257, >+ ldb.ERR_INVALID_ATTRIBUTE_SYNTAX), # 1024 is alleged UPN limit >+ ), >+ ("UPN same but for internal spaces", >+ ('A', 'a b@x.x', ok), >+ ('B', 'a b@x.x', ldb.ERR_CONSTRAINT_VIOLATION), >+ ), >+ ("SAM contains delete", >+ # forbidden according to documentation, but works in practice on Windows >+ ('A', 'a\x7f', ldb.ERR_CONSTRAINT_VIOLATION), >+ ('A', 'a\x7f'.encode(), ldb.ERR_CONSTRAINT_VIOLATION), >+ ('A', 'a\x7fb', ldb.ERR_CONSTRAINT_VIOLATION), >+ ('A', 'a\x7fb'.encode(), ldb.ERR_CONSTRAINT_VIOLATION), >+ ('A', '\x7fb', ldb.ERR_CONSTRAINT_VIOLATION), >+ ('A', '\x7fb'.encode(), ldb.ERR_CONSTRAINT_VIOLATION), >+ ), >+ # The wide at symbol ('ï¼ ' U+FF20) does not count as '@' for Samba >+ # so it will look like a string with no @s. >+ ("UPN with unicode wide at vs real at", >+ ('A', {'upn': 'wideatï¼ {realm}'}, ok), >+ ('B', {'upn': 'wideat@{realm}'}, ok), >+ ), >+ ("UPN with real at vs wide at", >+ ('B', {'upn': 'wideat@{realm}'}, ok), >+ ('A', {'upn': 'wideatï¼ {realm}'}, ok) >+ ), >+ ] >+ >+ >+def main(): >+ global LP, CREDS, SERVER, REALM >+ >+ parser = optparse.OptionParser( >+ "python3 ldap_upn_sam_account_name.py <server> [options]") >+ sambaopts = options.SambaOptions(parser) >+ parser.add_option_group(sambaopts) >+ >+ # use command line creds if available >+ credopts = options.CredentialsOptions(parser) >+ parser.add_option_group(credopts) >+ subunitopts = SubunitOptions(parser) >+ parser.add_option_group(subunitopts) >+ >+ opts, args = parser.parse_args() >+ if len(args) != 1: >+ parser.print_usage() >+ sys.exit(1) >+ >+ LP = sambaopts.get_loadparm() >+ CREDS = credopts.get_credentials(LP) >+ SERVER = args[0] >+ REALM = CREDS.get_realm() >+ >+ TestProgram(module=__name__, opts=subunitopts) >+ >+main() >diff --git a/selftest/knownfail.d/ldap_upn_sam_account b/selftest/knownfail.d/ldap_upn_sam_account >new file mode 100644 >index 00000000000..c4d494968b2 >--- /dev/null >+++ b/selftest/knownfail.d/ldap_upn_sam_account >@@ -0,0 +1,16 @@ >+samba.tests.ldap_upn_sam_account.+LdapUpnSamSambaOnlyTest.test_upn_sam_SAM_contains_delete >+samba.tests.ldap_upn_sam_account.+LdapUpnSamSambaOnlyTest.test_upn_sam_UPN_same_but_for_internal_spaces >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_SAM_ends_with_at >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_SAM_has_at_signs_clashing_upn_first >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_SAM_has_at_signs_clashing_upn_second >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_SAM_starts_with_at >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_UPN_clash_on_other_realm >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_UPN_ends_with_at >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_UPN_has_no_at >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_UPN_starts_with_at >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_add_the_same_upn_to_different_objects >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_different_objects_SAM_after_UPN >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_different_objects_SAM_before_UPN >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_different_objects_same_UPN_different_case >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_spaces_around_at >+samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_two_way_clash >diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py >index 2e9603e8f34..5dd61828899 100755 >--- a/source4/selftest/tests.py >+++ b/source4/selftest/tests.py >@@ -1181,6 +1181,15 @@ planoldpythontestsuite("ad_dc", > extra_args=['-U"$USERNAME%$PASSWORD"'], > environ={'TEST_ENV': 'ad_dc'}) > >+plantestsuite_loadlist("samba.tests.ldap_upn_sam_account", "ad_dc", >+ [python, >+ f"{srcdir()}/python/samba/tests/ldap_upn_sam_account.py", >+ '$SERVER', >+ '-U"$USERNAME%$PASSWORD"', >+ '--workgroup=$DOMAIN', >+ '$LOADLIST', '$LISTOPT']) >+ >+ > plantestsuite_loadlist("samba4.tokengroups.krb5.python(ad_dc_default)", "ad_dc_default:local", [python, os.path.join(DSDB_PYTEST_DIR, "token_group.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '-k', 'yes', '$LOADLIST', '$LISTOPT']) > plantestsuite_loadlist("samba4.tokengroups.ntlm.python(ad_dc_default)", "ad_dc_default:local", [python, os.path.join(DSDB_PYTEST_DIR, "token_group.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '-k', 'no', '$LOADLIST', '$LISTOPT']) > plantestsuite("samba4.sam.python(fl2008r2dc)", "fl2008r2dc", [python, os.path.join(DSDB_PYTEST_DIR, "sam.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN']) >-- >2.25.1 > > >From d9d87e090c09c91d23c95a8150e28dd09854f26f Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Fri, 6 Aug 2021 12:03:18 +1200 >Subject: [PATCH 092/190] CVE-2020-25722 pytest: test setting > servicePrincipalName over ldap > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > python/samba/tests/ldap_spn.py | 917 +++++++++++++++++++++++++++++++++ > selftest/knownfail.d/ldap_spn | 26 + > source4/selftest/tests.py | 10 +- > 3 files changed, 952 insertions(+), 1 deletion(-) > create mode 100644 python/samba/tests/ldap_spn.py > create mode 100644 selftest/knownfail.d/ldap_spn > >diff --git a/python/samba/tests/ldap_spn.py b/python/samba/tests/ldap_spn.py >new file mode 100644 >index 00000000000..8a398ffaa49 >--- /dev/null >+++ b/python/samba/tests/ldap_spn.py >@@ -0,0 +1,917 @@ >+# Unix SMB/CIFS implementation. >+# >+# Copyright 2021 (C) Catalyst IT Ltd >+# >+# 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 >+import pprint >+import re >+from samba.samdb import SamDB >+from samba.auth import system_session >+import ldb >+from samba.sd_utils import SDUtils >+from samba.credentials import DONT_USE_KERBEROS, Credentials >+from samba.gensec import FEATURE_SEAL >+from samba.tests.subunitrun import SubunitOptions, TestProgram >+from samba.tests import TestCase, ldb_err >+from samba.tests import DynamicTestCase >+import samba.getopt as options >+import optparse >+from samba.colour import c_RED, c_GREEN, c_DARK_YELLOW >+from samba.dsdb import ( >+ UF_SERVER_TRUST_ACCOUNT, >+ UF_TRUSTED_FOR_DELEGATION, >+) >+ >+ >+SPN_GUID = 'f3a64788-5306-11d1-a9c5-0000f80367c1' >+ >+RELEVANT_ATTRS = {'dNSHostName', >+ 'servicePrincipalName', >+ 'sAMAccountName', >+ 'dn'} >+ >+ok = True >+bad = False >+report = 'report' >+ >+operr = ldb.ERR_OPERATIONS_ERROR >+denied = ldb.ERR_INSUFFICIENT_ACCESS_RIGHTS >+constraint = ldb.ERR_CONSTRAINT_VIOLATION >+exists = ldb.ERR_ENTRY_ALREADY_EXISTS >+ >+add = ldb.FLAG_MOD_ADD >+replace = ldb.FLAG_MOD_REPLACE >+delete = ldb.FLAG_MOD_DELETE >+ >+try: >+ breakpoint >+except NameError: >+ # for python <= 3.6 >+ def breakpoint(): >+ import pdb >+ pdb.set_trace() >+ >+ >+def init(): >+ # This needs to happen before the class definition, and we put it >+ # in a function to keep the namespace clean. >+ global LP, CREDS, SERVER, REALM, COLOUR_TEXT, subunitopts, FILTER >+ >+ parser = optparse.OptionParser( >+ "python3 ldap_spn.py <server> [options]") >+ sambaopts = options.SambaOptions(parser) >+ parser.add_option_group(sambaopts) >+ >+ # use command line creds if available >+ credopts = options.CredentialsOptions(parser) >+ parser.add_option_group(credopts) >+ subunitopts = SubunitOptions(parser) >+ parser.add_option_group(subunitopts) >+ >+ parser.add_option('--colour', action="store_true", >+ help="use colour text", >+ default=sys.stdout.isatty()) >+ >+ parser.add_option('--filter', help="only run tests matching this regex") >+ >+ opts, args = parser.parse_args() >+ if len(args) != 1: >+ parser.print_usage() >+ sys.exit(1) >+ >+ LP = sambaopts.get_loadparm() >+ CREDS = credopts.get_credentials(LP) >+ SERVER = args[0] >+ REALM = CREDS.get_realm() >+ COLOUR_TEXT = opts.colour >+ FILTER = opts.filter >+ >+ >+init() >+ >+ >+def colour_text(x, state=None): >+ if not COLOUR_TEXT: >+ return x >+ if state == 'error': >+ return c_RED(x) >+ if state == 'pass': >+ return c_GREEN(x) >+ >+ return c_DARK_YELLOW(x) >+ >+ >+def get_samdb(creds=None): >+ if creds is None: >+ creds = CREDS >+ session = system_session() >+ else: >+ session = None >+ >+ return SamDB(url=f"ldap://{SERVER}", >+ lp=LP, >+ session_info=session, >+ credentials=creds) >+ >+ >+def add_unpriv_user(samdb, ou, username, >+ writeable_objects=None, >+ password="samba123@"): >+ creds = Credentials() >+ creds.set_username(username) >+ creds.set_password(password) >+ creds.set_domain(CREDS.get_domain()) >+ creds.set_realm(CREDS.get_realm()) >+ creds.set_workstation(CREDS.get_workstation()) >+ creds.set_gensec_features(CREDS.get_gensec_features() | FEATURE_SEAL) >+ creds.set_kerberos_state(DONT_USE_KERBEROS) >+ dnstr = f"CN={username},{ou}" >+ >+ # like, WTF, samdb.newuser(), this is what you make us do. >+ short_ou = ou.split(',', 1)[0] >+ >+ samdb.newuser(username, password, userou=short_ou) >+ >+ if writeable_objects: >+ sd_utils = SDUtils(samdb) >+ sid = sd_utils.get_object_sid(dnstr) >+ for obj in writeable_objects: >+ mod = f"(OA;CI;WP;{ SPN_GUID };;{ sid })" >+ sd_utils.dacl_add_ace(obj, mod) >+ >+ unpriv_samdb = get_samdb(creds=creds) >+ return unpriv_samdb >+ >+ >+class LdapSpnTestBase(TestCase): >+ _disabled = False >+ >+ @classmethod >+ def setUpDynamicTestCases(cls): >+ if getattr(cls, '_disabled', False): >+ return >+ for doc, *rows in cls.cases: >+ if FILTER: >+ if not re.search(FILTER, doc): >+ continue >+ name = re.sub(r'\W+', '_', doc) >+ cls.generate_dynamic_test("test_spn", name, rows, doc) >+ >+ def setup_objects(self, rows): >+ objects = set(r[0] for r in rows) >+ for name in objects: >+ if ':' in name: >+ objtype, name = name.split(':', 1) >+ else: >+ objtype = 'dc' >+ getattr(self, f'add_{objtype}')(name) >+ >+ def setup_users(self, rows): >+ # When you are adding an SPN that aliases (or would be aliased >+ # by) another SPN on another object, you need to have write >+ # permission on that other object too. >+ # >+ # To test this negatively and positively, we need to have >+ # users with various combinations of write permission, which >+ # means fiddling with SDs on the objects. >+ # >+ # The syntax is: >+ # '' : user with no special permissions >+ # '*' : admin user >+ # 'A' : user can write to A only >+ # 'A,C' : user can write to A and C >+ # 'C,A' : same, but makes another user >+ self.userdbs = { >+ '*': self.samdb >+ } >+ >+ permissions = set(r[2] for r in rows) >+ for p in permissions: >+ if p == '*': >+ continue >+ if p == '': >+ user = 'nobody' >+ writeable_objects = None >+ else: >+ user = 'writes_' + p.replace(",", '_') >+ writeable_objects = [self.objects[x][0] for x in p.split(',')] >+ >+ self.userdbs[p] = add_unpriv_user(self.samdb, self.ou, user, >+ writeable_objects) >+ >+ def _test_spn_with_args(self, rows, doc): >+ cdoc = colour_text(doc) >+ edoc = colour_text(doc, 'error') >+ pdoc = colour_text(doc, 'pass') >+ >+ if COLOUR_TEXT: >+ sys.stderr.flush() >+ print('\n', c_DARK_YELLOW('#' * 10), f'starting «{cdoc}»\n') >+ sys.stdout.flush() >+ >+ self.samdb = get_samdb() >+ self.base_dn = self.samdb.get_default_basedn() >+ self.short_id = self.id().rsplit('.', 1)[1][:63] >+ self.objects = {} >+ self.ou = f"OU={ self.short_id },{ self.base_dn }" >+ self.addCleanup(self.samdb.delete, self.ou, ["tree_delete:1"]) >+ self.samdb.create_ou(self.ou) >+ >+ self.setup_objects(rows) >+ self.setup_users(rows) >+ >+ for i, row in enumerate(rows): >+ if len(row) == 5: >+ obj, data, rights, expected, op = row >+ else: >+ obj, data, rights, expected = row >+ op = ldb.FLAG_MOD_REPLACE >+ >+ # We use this DB with possibly restricted rights for this row >+ samdb = self.userdbs[rights] >+ >+ if ':' in obj: >+ objtype, obj = obj.split(':', 1) >+ else: >+ objtype = 'dc' >+ >+ dn, dnsname = self.objects[obj] >+ m = {"dn": dn} >+ >+ if isinstance(data, dict): >+ m.update(data) >+ else: >+ m['servicePrincipalName'] = data >+ >+ # for python's sake (and our sanity) we try to ensure we >+ # have consistent canonical case in our attributes >+ keys = set(m.keys()) >+ if not keys.issubset(RELEVANT_ATTRS): >+ raise ValueError(f"unexpected attr {keys - RELEVANT_ATTRS}. " >+ "Casefold typo?") >+ >+ for k in ('dNSHostName', 'servicePrincipalName'): >+ if isinstance(m.get(k), str): >+ m[k] = m[k].format(dnsname=f"x.{REALM}") >+ >+ msg = ldb.Message.from_dict(samdb, m, op) >+ >+ if expected is bad: >+ try: >+ samdb.modify(msg) >+ except ldb.LdbError as e: >+ print(f"row {i+1} of '{pdoc}' failed as expected with " >+ f"{ldb_err(e)}\n") >+ continue >+ self.fail(f"row {i+1}: " >+ f"{rights} {pprint.pformat(m)} on {objtype} {obj} " >+ f"should fail ({edoc})") >+ >+ elif expected is ok: >+ try: >+ samdb.modify(msg) >+ except ldb.LdbError as e: >+ self.fail(f"row {i+1} of {edoc} failed with {ldb_err(e)}:\n" >+ f"{rights} {pprint.pformat(m)} on {objtype} {obj}") >+ >+ elif expected is report: >+ try: >+ self.samdb.modify(msg) >+ print(f"row {i+1} " >+ f"of '{cdoc}' {colour_text('SUCCEEDED', 'pass')}:\n" >+ f"{pprint.pformat(m)} on {obj}") >+ except ldb.LdbError as e: >+ print(f"row {i+1} " >+ f"of '{cdoc}' {colour_text('FAILED', 'error')} " >+ f"with {ldb_err(e)}:\n{pprint.pformat(m)} on {obj}") >+ >+ elif expected is breakpoint: >+ try: >+ breakpoint() >+ samdb.modify(msg) >+ except ldb.LdbError as e: >+ print(f"row {i+1} of '{pdoc}' FAILED with {ldb_err(e)}\n") >+ >+ else: # an ldb error number >+ try: >+ samdb.modify(msg) >+ except ldb.LdbError as e: >+ if e.args[0] == expected: >+ continue >+ self.fail(f"row {i+1} of '{edoc}' " >+ f"should have failed with {ldb_err(expected)}:\n" >+ f"not {ldb_err(e)}:\n" >+ f"{rights} {pprint.pformat(m)} on {objtype} {obj}") >+ self.fail(f"row {i+1} of '{edoc}' " >+ f"should have failed with {ldb_err(expected)}:\n" >+ f"{rights} {pprint.pformat(m)} on {objtype} {obj}") >+ >+ def add_dc(self, name): >+ dn = f"CN={name},OU=Domain Controllers,{self.base_dn}" >+ dnsname = f"{name}.{REALM}".lower() >+ self.samdb.add({ >+ "dn": dn, >+ "objectclass": "computer", >+ "userAccountControl": str(UF_SERVER_TRUST_ACCOUNT | >+ UF_TRUSTED_FOR_DELEGATION), >+ "dnsHostName": dnsname, >+ "carLicense": self.id() >+ }) >+ self.addCleanup(self.remove_object, name) >+ self.objects[name] = (dn, dnsname) >+ >+ def add_user(self, name): >+ dn = f"CN={name},{self.ou}" >+ self.samdb.add({ >+ "dn": dn, >+ "name": name, >+ "samAccountName": name, >+ "objectclass": "user", >+ "carLicense": self.id() >+ }) >+ self.addCleanup(self.remove_object, name) >+ self.objects[name] = (dn, None) >+ >+ def remove_object(self, name): >+ dn, dnsname = self.objects.pop(name) >+ self.samdb.delete(dn) >+ >+ >+@DynamicTestCase >+class LdapSpnTest(LdapSpnTestBase): >+ """Make sure we can't add clashing servicePrincipalNames. >+ >+ This would be possible using sPNMappings aliases â for example, if >+ the mapping maps host/ to cifs/, we should not be able to add >+ different addresses for each. >+ """ >+ >+ # default sPNMappings: host=alerter, appmgmt, cisvc, clipsrv, >+ # browser, dhcp, dnscache, replicator, eventlog, eventsystem, >+ # policyagent, oakley, dmserver, dns, mcsvc, fax, msiserver, ias, >+ # messenger, netlogon, netman, netdde, netddedsm, nmagent, >+ # plugplay, protectedstorage, rasman, rpclocator, rpc, rpcss, >+ # remoteaccess, rsvp, samss, scardsvr, scesrv, seclogon, scm, >+ # dcom, cifs, spooler, snmp, schedule, tapisrv, trksvr, trkwks, >+ # ups, time, wins, www, http, w3svc, iisadmin, msdtc >+ # >+ # I think in practice this is rarely if ever changed or added to. >+ >+ cases = [ >+ ("add one as admin", >+ ('A', 'host/{dnsname}', '*', ok), >+ ), >+ ("add one as rightful user", >+ ('A', 'host/{dnsname}', 'A', ok), >+ ), >+ ("attempt to add one as nobody", >+ ('A', 'host/{dnsname}', '', denied), >+ ), >+ >+ ("add and replace as admin", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', 'host/x.{dnsname}', '*', ok), >+ ), >+ ("replace as rightful user", >+ ('A', 'host/{dnsname}', 'A', ok), >+ ('A', 'host/x.{dnsname}', 'A', ok), >+ ), >+ ("attempt to replace one as nobody", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', 'host/x.{dnsname}', '', denied), >+ ), >+ >+ ("add second as admin", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', 'host/x.{dnsname}', '*', ok, add), >+ ), >+ ("add second as rightful user", >+ ('A', 'host/{dnsname}', 'A', ok), >+ ('A', 'host/x.{dnsname}', 'A', ok, add), >+ ), >+ ("attempt to add second as nobody", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', 'host/x.{dnsname}', '', denied, add), >+ ), >+ >+ ("add the same one twice, simple duplicate error", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', '*', bad, add), >+ ), >+ ("simple duplicate attributes, as non-admin", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', 'A', bad, add), >+ ), >+ >+ ("add the same one twice, identical duplicate", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', '*', bad, add), >+ ), >+ >+ ("add a conflict, host first, as nobody", >+ ('A', 'host/z.{dnsname}', '*', ok), >+ ('B', 'cifs/z.{dnsname}', '', denied), >+ ), >+ >+ ("add a conflict, service first, as nobody", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('B', 'host/{dnsname}', '', denied), >+ ), >+ >+ >+ ("three way conflict, host first, as admin", >+ ('A', 'host/z.{dnsname}', '*', ok), >+ ('B', 'cifs/z.{dnsname}', '*', ok), >+ ('C', 'www/z.{dnsname}', '*', ok), >+ ), >+ ("three way conflict, host first, with sufficient rights", >+ ('A', 'host/z.{dnsname}', 'A', ok), >+ ('B', 'cifs/z.{dnsname}', 'B,A', ok), >+ ('C', 'www/z.{dnsname}', 'C,A', ok), >+ ), >+ ("three way conflict, host first, adding duplicate", >+ ('A', 'host/z.{dnsname}', 'A', ok), >+ ('B', 'cifs/z.{dnsname}', 'B,A', ok), >+ ('C', 'cifs/z.{dnsname}', 'C,A', bad), >+ ), >+ ("three way conflict, host first, adding duplicate, full rights", >+ ('A', 'host/z.{dnsname}', 'A', ok), >+ ('B', 'cifs/z.{dnsname}', 'B,A', ok), >+ ('C', 'cifs/z.{dnsname}', 'C,B,A', bad), >+ ), >+ >+ ("three way conflict, host first, with other write rights", >+ ('A', 'host/z.{dnsname}', '*', ok), >+ ('B', 'cifs/z.{dnsname}', 'A,B', ok), >+ ('C', 'cifs/z.{dnsname}', 'A,B', bad), >+ >+ ), >+ ("three way conflict, host first, as nobody", >+ ('A', 'host/z.{dnsname}', '*', ok), >+ ('B', 'cifs/z.{dnsname}', '*', ok), >+ ('C', 'www/z.{dnsname}', '', denied), >+ ), >+ >+ ("three way conflict, services first, as admin", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('B', 'www/{dnsname}', '*', ok), >+ ('C', 'host/{dnsname}', '*', constraint), >+ ), >+ ("three way conflict, services first, with service write rights", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('B', 'www/{dnsname}', '*', ok), >+ ('C', 'host/{dnsname}', 'A,B', bad), >+ ), >+ >+ ("three way conflict, service first, as nobody", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('B', 'www/{dnsname}', '*', ok), >+ ('C', 'host/{dnsname}', '', denied), >+ ), >+ ("replace host before specific", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ), >+ ("replace host after specific, as nobody", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', '', denied), >+ ), >+ >+ ("non-conflict host before specific", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', 'cifs/{dnsname}', '*', ok, add), >+ ), >+ ("non-conflict host after specific", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', '*', ok, add), >+ ), >+ ("non-conflict host before specific, non-admin", >+ ('A', 'host/{dnsname}', 'A', ok), >+ ('A', 'cifs/{dnsname}', 'A', ok, add), >+ ), >+ ("non-conflict host after specific, as nobody", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', '', denied, add), >+ ), >+ >+ ("add a conflict, host first on user, as admin", >+ ('user:C', 'host/{dnsname}', '*', ok), >+ ('B', 'cifs/{dnsname}', '*', ok), >+ ), >+ ("add a conflict, host first on user, host rights", >+ ('user:C', 'host/{dnsname}', '*', ok), >+ ('B', 'cifs/{dnsname}', 'C', denied), >+ ), >+ ("add a conflict, host first on user, both rights", >+ ('user:C', 'host/{dnsname}', '*', ok), >+ ('B', 'cifs/{dnsname}', 'B,C', ok), >+ ), >+ ("add a conflict, host first both on user", >+ ('user:C', 'host/{dnsname}', '*', ok), >+ ('user:D', 'www/{dnsname}', '*', ok), >+ ), >+ ("add a conflict, host first both on user, host rights", >+ ('user:C', 'host/{dnsname}', '*', ok), >+ ('user:D', 'www/{dnsname}', 'C', denied), >+ ), >+ ("add a conflict, host first both on user, both rights", >+ ('user:C', 'host/{dnsname}', '*', ok), >+ ('user:D', 'www/{dnsname}', 'C,D', ok), >+ ), >+ ("add a conflict, host first both on user, as nobody", >+ ('user:C', 'host/{dnsname}', '*', ok), >+ ('user:D', 'www/{dnsname}', '', denied), >+ ), >+ ("add a conflict, host first, with both write rights", >+ ('A', 'host/z.{dnsname}', '*', ok), >+ ('B', 'cifs/z.{dnsname}', 'A,B', ok), >+ ), >+ >+ ("add a conflict, host first, second on user, as admin", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('user:D', 'cifs/{dnsname}', '*', ok), >+ ), >+ ("add a conflict, host first, second on user, with rights", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('user:D', 'cifs/{dnsname}', 'A,D', ok), >+ ), >+ >+ ("nonsense SPNs, part 1, as admin", >+ ('A', 'a-b-c/{dnsname}', '*', ok), >+ ('A', 'rrrrrrrrrrrrr /{dnsname}', '*', ok), >+ ), >+ ("nonsense SPNs, part 1, as user", >+ ('A', 'a-b-c/{dnsname}', 'A', ok), >+ ('A', 'rrrrrrrrrrrrr /{dnsname}', 'A', ok), >+ ), >+ ("nonsense SPNs, part 1, as nobody", >+ ('A', 'a-b-c/{dnsname}', '', denied), >+ ('A', 'rrrrrrrrrrrrr /{dnsname}', '', denied), >+ ), >+ >+ ("add a conflict, using port", >+ ('A', 'dns/{dnsname}', '*', ok), >+ ('B', 'dns/{dnsname}:53', '*', ok), >+ ), >+ ("add a conflict, using port, port first", >+ ('user:C', 'dns/{dnsname}:53', '*', ok), >+ ('user:D', 'dns/{dnsname}', '*', ok), >+ ), >+ ("three part spns", >+ ('A', {'dNSHostName': '{dnsname}'}, '*', ok), >+ ('A', 'cifs/{dnsname}/DomainDNSZones.{dnsname}', '*', ok), >+ ('B', 'cifs/{dnsname}/DomainDNSZones.{dnsname}', '*', constraint), >+ ('A', {'dNSHostName': 'y.{dnsname}'}, '*', ok), >+ ('B', 'cifs/{dnsname}/DomainDNSZones.{dnsname}', '*', ok), >+ ('B', 'cifs/y.{dnsname}/DomainDNSZones.{dnsname}', '*', constraint), >+ ), >+ ("three part nonsense spns", >+ ('A', {'dNSHostName': 'bean'}, '*', ok), >+ ('A', 'cifs/bean/DomainDNSZones.bean', '*', ok), >+ ('B', 'cifs/bean/DomainDNSZones.bean', '*', constraint), >+ ('A', {'dNSHostName': 'y.bean'}, '*', ok), >+ ('B', 'cifs/bean/DomainDNSZones.bean', '*', ok), >+ ('B', 'cifs/y.bean/DomainDNSZones.bean', '*', constraint), >+ ('C', 'host/bean/bean', '*', ok), >+ ), >+ >+ ("one part spns (no slashes)", >+ ('A', '{dnsname}', '*', constraint), >+ ('B', 'cifs', '*', constraint), >+ ('B', 'cifs/', '*', ok), >+ ('B', ' ', '*', constraint), >+ ('user:C', 'host', '*', constraint), >+ ), >+ >+ ("dodgy spns", >+ # These tests pass on Windows. An SPN must have one or two >+ # slashes, with at least one character before the first one, >+ # UNLESS the first slash is followed by a good enough service >+ # name (e.g. "/host/x.y" rather than "sdfsd/x.y"). >+ ('A', '\\/{dnsname}', '*', ok), >+ ('B', 'cifs/\\\\{dnsname}', '*', ok), >+ ('B', r'cifs/\\\{dnsname}', '*', ok), >+ ('B', r'cifs/\\\{dnsname}/', '*', ok), >+ ('A', r'cÄ«fs/\\\{dnsname}/', '*', constraint), # 'Ä«' maps to 'i' >+ # on the next two, full-width solidus (U+FF0F) does not work >+ # as '/'. >+ ('A', 'cifsï¼sfic', '*', constraint, add), >+ ('A', r'cifsï¼\\\{dnsname}', '*', constraint, add), >+ ('B', '\n', '*', constraint), >+ ('B', '\n/\n', '*', ok), >+ ('B', '\n/\n/\n', '*', ok), >+ ('B', '\n/\n/\n/\n', '*', constraint), >+ ('B', ' /* and so on */ ', '*', ok, add), >+ ('B', r'¯\_(ã)_/¯', '*', ok, add), # ¯\_(ã)_/¯ >+ # 㤠is hiragana for katakana ã, so the next one fails for >+ # something analogous to casefold reasons. >+ ('A', r'¯\_(ã¤)_/¯', '*', constraint), >+ ('A', r'¯\_(ã¡)_/¯', '*', constraint), # circled ã >+ ('B', '//', '*', constraint), # all can't be empty, >+ ('B', ' //', '*', ok), # service can be space >+ ('B', '/host/{dnsname}', '*', ok), # or empty if others aren't >+ ('B', '/host/x.y.z', '*', ok), >+ ('B', '/ /x.y.z', '*', ok), >+ ('B', ' / / ', '*', ok), >+ ('user:C', b'host/', '*', ok), >+ ('user:C', ' /host', '*', ok), # service is ' ' (space) >+ ('B', ' /host', '*', constraint), # already on C >+ ('B', ' /HÅST', '*', constraint), # Å equiv to O >+ ('B', ' /ħÃÅt', '*', constraint), # maps to ' /host' >+ ('B', ' /H0ST', '*', ok), # 0 is zero >+ ('B', ' /ÐoST', '*', ok), # Cyrillic Ð (~N) >+ ('B', ' /host', '*', ok), # two space >+ ('B', '\u00a0/host', '*', ok), # non-breaking space >+ ('B', ' 2/HÅST/â·[ ][]¨(', '*', ok), >+ ('B', ' (//)', '*', ok, add), >+ ('B', ' ///', '*', constraint), >+ ('B', r' /\//', '*', constraint), # escape doesn't help >+ ('B', ' /\\//', '*', constraint), # double escape doesn't help >+ ('B', r'\//', '*', ok), >+ ('A', r'\\/\\/', '*', ok), >+ ('B', '|//|', '*', ok, add), >+ ('B', r'\/\/\\', '*', ok, add), >+ >+ ('A', ':', '*', constraint), >+ ('A', ':/:', '*', ok), >+ ('A', ':/:80', '*', ok), # port number syntax is not special >+ ('A', ':/:( ã', '*', ok), >+ ('A', ':/:/:', '*', ok), >+ ('B', b'cifs/\x11\xaa\xbb\xcc\\example.com', '*', ok), >+ ('A', b':/\xcc\xcc\xcc\xcc', '*', ok), >+ ('A', b':/b\x00/b/b/b', '*', ok), # string handlng truncates at \x00 >+ ('A', b'a@b/a@b/a@b', '*', ok), >+ ('A', b'a/a@b/a@b', '*', ok), >+ ), >+ ("empty part spns (consecutive slashes)", >+ ('A', 'cifs//{dnsname}', '*', ok), >+ ('B', 'cifs//{dnsname}', '*', bad), # should clash with line 1 >+ ('B', 'cifs/zzzy.{dnsname}/', '*', ok), >+ ('B', '/host/zzzy.{dnsname}', '*', ok), >+ ), >+ ("too many spn parts", >+ ('A', 'cifs/{dnsname}/{dnsname}/{dnsname}', '*', bad), >+ ('A', {'dNSHostName': 'y.{dnsname}'}, '*', ok), >+ ('B', 'cifs/{dnsname}/{dnsname}/', '*', bad), >+ ('B', 'cifs/y.{dnsname}/{dnsname}/toop', '*', bad), >+ ('B', 'host/{dnsname}/a/b/c', '*', bad), >+ ), >+ ("add a conflict, host first, as admin", >+ ('A', 'host/z.{dnsname}', '*', ok), >+ ('B', 'cifs/z.{dnsname}', '*', ok), >+ ), >+ ("add a conflict, host first, with host write rights", >+ ('A', 'host/z.{dnsname}', '*', ok), >+ ('B', 'cifs/z.{dnsname}', 'A', denied), >+ ), >+ ("add a conflict, service first, with service write rights", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('B', 'host/{dnsname}', 'A', denied), >+ ), >+ ("adding dNSHostName after cifs with no old dNSHostName", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', {'dNSHostName': 'y.{dnsname}'}, '*', ok), >+ ('B', 'cifs/{dnsname}', '*', constraint), >+ ('B', 'cifs/y.{dnsname}', '*', ok), >+ ('B', 'host/y.{dnsname}', '*', ok), >+ ), >+ ("changing dNSHostName after cifs", >+ ('A', {'dNSHostName': '{dnsname}'}, '*', ok), >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', {'dNSHostName': 'y.{dnsname}'}, '*', ok), >+ ('B', 'cifs/{dnsname}', '*', ok), >+ ('B', 'cifs/y.{dnsname}', '*', bad), >+ ('B', 'host/y.{dnsname}', '*', bad), >+ ), >+ ] >+ >+ >+@DynamicTestCase >+class LdapSpnSambaOnlyTest(LdapSpnTestBase): >+ # We don't run these ones outside of selftest, where we are >+ # probably testing against Windows and these are known failures. >+ _disabled = 'SAMBA_SELFTEST' not in os.environ >+ cases = [ >+ ("add a conflict, host first, with service write rights", >+ ('A', 'host/z.{dnsname}', '*', ok), >+ ('B', 'cifs/z.{dnsname}', 'B', denied), >+ ), >+ ("add a conflict, service first, with host write rights", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('B', 'host/{dnsname}', 'B', constraint), >+ ), >+ ("add a conflict, service first, as admin", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('B', 'host/{dnsname}', '*', constraint), >+ ), >+ ("add a conflict, service first, with both write rights", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('B', 'host/{dnsname}', 'A,B', constraint), >+ ), >+ ("add a conflict, host first both on user, service rights", >+ ('user:C', 'host/{dnsname}', '*', ok), >+ ('user:D', 'www/{dnsname}', 'D', denied), >+ ), >+ >+ ("changing dNSHostName after host", >+ ('A', {'dNSHostName': '{dnsname}'}, '*', ok), >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', {'dNSHostName': 'y.{dnsname}'}, '*', ok), >+ ('B', 'cifs/{dnsname}', 'B', ok), # no clash with A >+ ('B', 'cifs/y.{dnsname}', 'B', bad), # should clash with A >+ ('B', 'host/y.{dnsname}', '*', bad), >+ ), >+ >+ ("mystery dnsname clash, host first", >+ ('user:C', 'host/heeble.example.net', '*', ok), >+ ('user:D', 'www/heeble.example.net', '*', ok), >+ ), >+ ("mystery dnsname clash, www first", >+ ('user:D', 'www/heeble.example.net', '*', ok), >+ ('user:C', 'host/heeble.example.net', '*', constraint), >+ ), >+ ("replace as admin", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ), >+ ("replace as non-admin with rights", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', 'A', ok), >+ ('A', 'cifs/{dnsname}', 'A', ok), >+ ), >+ ("replace vial delete as non-admin with rights", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', 'A', ok), >+ ('A', 'host/{dnsname}', 'A', ok, delete), >+ ('A', 'cifs/{dnsname}', 'A', ok, add), >+ ), >+ ("replace as non-admin without rights", >+ ('B', 'cifs/b', '*', ok), >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', 'B', denied), >+ ('A', 'cifs/{dnsname}', 'B', denied), >+ ), >+ ("replace as nobody", >+ ('B', 'cifs/b', '*', ok), >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', '', denied), >+ ('A', 'cifs/{dnsname}', '', denied), >+ ), >+ ("accumulate and delete as admin", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', '*', ok, add), >+ ('A', 'www/{dnsname}', '*', ok, add), >+ ('A', 'www/...', '*', ok, add), >+ ('A', 'host/...', '*', ok, add), >+ ('A', 'www/{dnsname}', '*', ok, delete), >+ ('A', 'host/{dnsname}', '*', ok, delete), >+ ('A', 'host/{dnsname}', '*', ok, add), >+ ('A', 'www/{dnsname}', '*', ok, add), >+ ('A', 'host/...', '*', ok, delete), >+ ), >+ ("accumulate and delete with user rights", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', 'host/{dnsname}', 'A', ok, add), >+ ('A', 'www/{dnsname}', 'A', ok, add), >+ ('A', 'www/...', 'A', ok, add), >+ ('A', 'host/...', 'A', ok, add), >+ ('A', 'www/{dnsname}', 'A', ok, delete), >+ ('A', 'host/{dnsname}', 'A', ok, delete), >+ ('A', 'host/{dnsname}', 'A', ok, add), >+ ('A', 'www/{dnsname}', 'A', ok, add), >+ ('A', 'host/...', 'A', ok, delete), >+ ), >+ ("three way conflict, host first, with partial write rights", >+ ('A', 'host/z.{dnsname}', 'A', ok), >+ ('B', 'cifs/z.{dnsname}', 'B', denied), >+ ('C', 'www/z.{dnsname}', 'C', denied), >+ ), >+ ("three way conflict, host first, with partial write rights 2", >+ ('A', 'host/z.{dnsname}', 'A', ok), >+ ('B', 'cifs/z.{dnsname}', 'B', bad), >+ ('C', 'www/z.{dnsname}', 'C,A', ok), >+ ), >+ >+ ("three way conflict sandwich, sufficient rights", >+ ('B', 'host/{dnsname}', 'B', ok), >+ ('A', 'cifs/{dnsname}', 'A,B', ok), >+ # the replaces don't fail even though they appear to affect A >+ # and B, because they are effectively no-ops, leaving >+ # everything as it was before. >+ ('A', 'cifs/{dnsname}', 'A', ok), >+ ('B', 'host/{dnsname}', 'B', ok), >+ ('C', 'www/{dnsname}', 'A,B,C', ok), >+ ('C', 'www/{dnsname}', 'B,C', ok), >+ # because B already has host/, C doesn't matter >+ ('B', 'host/{dnsname}', 'A,B', ok), >+ # removing host (via replace) frees others, needs B only >+ ('B', 'ldap/{dnsname}', 'B', ok), >+ ('C', 'www/{dnsname}', 'C', ok), >+ ('A', 'cifs/{dnsname}', 'A', ok), >+ >+ # re-adding host is now impossible while A and C have {dnsname} spns >+ ('B', 'host/{dnsname}', '*', bad), >+ ('B', 'host/{dnsname}', 'A,B,C', bad), >+ # so let's remove those... (not needing B rights) >+ ('C', 'www/{dnsname}', 'C', ok, delete), >+ ('A', 'cifs/{dnsname}', 'A', ok, delete), >+ # and now we can add host/ again >+ ('B', 'host/{dnsname}', 'B', ok), >+ ('C', 'www/{dnsname}', 'B,C', ok, add), >+ ('A', 'cifs/{dnsname}', 'A,B', ok), >+ ), >+ ("three way conflict, service first, with all write rights", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('B', 'www/{dnsname}', 'A,B,C', ok), >+ ('C', 'host/{dnsname}', 'A,B,C', bad), >+ ), >+ ("three way conflict, service first, just sufficient rights", >+ ('A', 'cifs/{dnsname}', 'A', ok), >+ ('B', 'www/{dnsname}', 'B', ok), >+ ('C', 'host/{dnsname}', 'A,B,C', bad), >+ ), >+ >+ ("three way conflict, service first, with host write rights", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('B', 'www/{dnsname}', '*', ok), >+ ('C', 'host/{dnsname}', 'C', bad), >+ ), >+ ("three way conflict, service first, with both write rights", >+ ('A', 'cifs/{dnsname}', '*', ok), >+ ('A', 'cifs/{dnsname}', '*', ok, delete), >+ ('A', 'www/{dnsname}', 'A,B,C', ok), >+ ('B', 'host/{dnsname}', 'A,B', bad), >+ ('A', 'www/{dnsname}', 'A', ok, delete), >+ ('B', 'host/{dnsname}', 'A,B', ok), >+ ('C', 'cifs/{dnsname}', 'C', bad), >+ ('C', 'cifs/{dnsname}', 'B,C', ok), >+ ), >+ ("three way conflict, services first, with partial rights", >+ ('A', 'cifs/{dnsname}', 'A,C', ok), >+ ('B', 'www/{dnsname}', '*', ok), >+ ('C', 'host/{dnsname}', 'A,C', bad), >+ ), >+ ] >+ >+ >+@DynamicTestCase >+class LdapSpnAmbitiousTest(LdapSpnTestBase): >+ _disabled = True >+ cases = [ >+ ("add a conflict with port, host first both on user", >+ ('user:C', 'host/{dnsname}', '*', ok), >+ ('user:D', 'www/{dnsname}:80', '*', bad), >+ ), >+ # see https://bugzilla.samba.org/show_bug.cgi?id=8929 >+ ("add the same one twice, case-insensitive duplicate", >+ ('A', 'host/{dnsname}', '*', ok), >+ ('A', 'Host/{dnsname}', '*', bad, add), >+ ), >+ ("special SPN", >+ # should fail because we don't have all the DSA infrastructure >+ ('A', ("E3514235-4B06-11D1-AB04-00C04FC2DCD2/" >+ "75b84f00-a81b-4a19-8ef2-8e483cccff11/" >+ "{dnsname}"), '*', constraint) >+ ), >+ ("single part SPNs matching sAMAccountName", >+ # setting them both together is allegedly a MacOS behaviour, >+ # but all we get from Windows is a mysterious NO_SUCH_OBJECT. >+ ('user:A', {'sAMAccountName': 'A', >+ 'servicePrincipalName': 'A'}, '*', ldb.ERR_NO_SUCH_OBJECT), >+ ('user:B', {'sAMAccountName': 'B'}, '*', ok), >+ ('user:B', {'servicePrincipalName': 'B'}, '*', constraint), >+ ('user:C', {'servicePrincipalName': 'C'}, '*', constraint), >+ ('user:C', {'sAMAccountName': 'C'}, '*', ok), >+ ), >+ ("three part spns with dnsHostName", >+ ('A', {'dNSHostName': '{dnsname}'}, '*', ok), >+ ('A', 'cifs/{dnsname}/DomainDNSZones.{dnsname}', '*', ok), >+ ('A', {'dNSHostName': 'y.{dnsname}'}, '*', ok), >+ ('B', 'cifs/{dnsname}/DomainDNSZones.{dnsname}', '*', ok), >+ ('B', 'cifs/y.{dnsname}/DomainDNSZones.{dnsname}', '*', constraint), >+ ('C', 'host/{y.dnsname}/{y.dnsname}', '*', constraint), >+ ('A', 'host/y.{dnsname}/{dnsname}', '*', constraint), >+ ), >+ ] >+ >+ >+def main(): >+ TestProgram(module=__name__, opts=subunitopts) >+ >+main() >diff --git a/selftest/knownfail.d/ldap_spn b/selftest/knownfail.d/ldap_spn >new file mode 100644 >index 00000000000..dc768728658 >--- /dev/null >+++ b/selftest/knownfail.d/ldap_spn >@@ -0,0 +1,26 @@ >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_host_first_both_on_user_service_rights >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_host_first_with_service_write_rights >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_service_first_as_admin >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_service_first_with_both_write_rights >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_service_first_with_host_write_rights >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_changing_dNSHostName_after_host >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_mystery_dnsname_clash_www_first >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_host_first_with_partial_write_rights >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_host_first_with_partial_write_rights_2 >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_sandwich_sufficient_rights >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_service_first_just_sufficient_rights >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_service_first_with_all_write_rights >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_service_first_with_both_write_rights >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_service_first_with_host_write_rights >+samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_services_first_with_partial_rights >+samba.tests.ldap_spn.+LdapSpnTest.test_spn_adding_dNSHostName_after_cifs_with_no_old_dNSHostName >+samba.tests.ldap_spn.+LdapSpnTest.test_spn_changing_dNSHostName_after_cifs >+samba.tests.ldap_spn.+LdapSpnTest.test_spn_dodgy_spns >+samba.tests.ldap_spn.+LdapSpnTest.test_spn_empty_part_spns_consecutive_slashes_ >+samba.tests.ldap_spn.+LdapSpnTest.test_spn_one_part_spns_no_slashes_ >+samba.tests.ldap_spn.+LdapSpnTest.test_spn_three_part_nonsense_spns >+samba.tests.ldap_spn.+LdapSpnTest.test_spn_three_part_spns >+samba.tests.ldap_spn.+LdapSpnTest.test_spn_three_way_conflict_host_first_adding_duplicate >+samba.tests.ldap_spn.+LdapSpnTest.test_spn_three_way_conflict_host_first_adding_duplicate_full_rights >+samba.tests.ldap_spn.+LdapSpnTest.test_spn_three_way_conflict_services_first_as_admin >+samba.tests.ldap_spn.+LdapSpnTest.test_spn_too_many_spn_parts >diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py >index 5dd61828899..deba427f3b9 100755 >--- a/source4/selftest/tests.py >+++ b/source4/selftest/tests.py >@@ -1181,7 +1181,15 @@ planoldpythontestsuite("ad_dc", > extra_args=['-U"$USERNAME%$PASSWORD"'], > environ={'TEST_ENV': 'ad_dc'}) > >-plantestsuite_loadlist("samba.tests.ldap_upn_sam_account", "ad_dc", >+plantestsuite_loadlist("samba.tests.ldap_spn", "ad_dc", >+ [python, >+ f"{srcdir()}/python/samba/tests/ldap_spn.py", >+ '$SERVER', >+ '-U"$USERNAME%$PASSWORD"', >+ '--workgroup=$DOMAIN', >+ '$LOADLIST', '$LISTOPT']) >+ >+plantestsuite_loadlist("samba.tests.ldap_upn_sam_account", "ad_dc_ntvfs", > [python, > f"{srcdir()}/python/samba/tests/ldap_upn_sam_account.py", > '$SERVER', >-- >2.25.1 > > >From 89c570006787fbc14b150726de1273c19f947684 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Thu, 12 Aug 2021 21:53:16 +1200 >Subject: [PATCH 093/190] CVE-2020-25722 s4/cracknames: add comment pointing to > samldb spn handling > >These need to stay a little bit in sync. The reverse comment is there. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/cracknames.c | 6 ++++++ > 1 file changed, 6 insertions(+) > >diff --git a/source4/dsdb/samdb/cracknames.c b/source4/dsdb/samdb/cracknames.c >index 7313a3247c7..4852a0ef9bd 100644 >--- a/source4/dsdb/samdb/cracknames.c >+++ b/source4/dsdb/samdb/cracknames.c >@@ -81,6 +81,12 @@ static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(struct ldb_context *ldb_ct > const char *alias_from, > char **alias_to) > { >+ /* >+ * Some of the logic of this function is mirrored in find_spn_alias() >+ * in source4/dsdb.samdb/ldb_modules/samldb.c. If you change this to >+ * not return the first matched alias, you will need to rethink that >+ * function too. >+ */ > unsigned int i; > int ret; > struct ldb_result *res; >-- >2.25.1 > > >From 9a46d11638c23a52af23964d33669a90afaf7639 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Fri, 22 Oct 2021 14:12:25 +1300 >Subject: [PATCH 094/190] CVE-2020-25722 s4/dsdb/samldb: add > samldb_get_single_valued_attr() helper > >This takes a string of logic out of samldb_unique_attr_check() that we >are going to need in other places, and that would be very tedious to >repeat. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 49 +++++++++++++++++++++++++ > 1 file changed, 49 insertions(+) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index edf153d9fb9..690719ce423 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -164,6 +164,55 @@ static int samldb_next_step(struct samldb_ctx *ac) > } > } > >+static int samldb_get_single_valued_attr(struct ldb_context *ldb, >+ struct samldb_ctx *ac, >+ const char *attr, >+ const char **value) >+{ >+ /* >+ * The steps we end up going through to get and check a single valued >+ * attribute. >+ */ >+ struct ldb_message_element *el = NULL; >+ >+ *value = NULL; >+ >+ el = dsdb_get_single_valued_attr(ac->msg, attr, >+ ac->req->operation); >+ if (el == NULL) { >+ /* we are not affected */ >+ return LDB_SUCCESS; >+ } >+ >+ if (el->num_values > 1) { >+ ldb_asprintf_errstring( >+ ldb, >+ "samldb: %s has %u values, should be single-valued!", >+ attr, el->num_values); >+ return LDB_ERR_CONSTRAINT_VIOLATION; >+ } else if (el->num_values == 0) { >+ ldb_asprintf_errstring( >+ ldb, >+ "samldb: new value for %s " >+ "not provided for mandatory, single-valued attribute!", >+ attr); >+ return LDB_ERR_OBJECT_CLASS_VIOLATION; >+ } >+ >+ >+ if (el->values[0].length == 0) { >+ ldb_asprintf_errstring( >+ ldb, >+ "samldb: %s is of zero length, should have a value!", >+ attr); >+ return LDB_ERR_OBJECT_CLASS_VIOLATION; >+ } >+ >+ *value = (char *)el->values[0].data; >+ >+ return LDB_SUCCESS; >+} >+ > static int samldb_unique_attr_check(struct samldb_ctx *ac, const char *attr, > const char *attr_conflict, > struct ldb_dn *base_dn) >-- >2.25.1 > > >From 4185b800fd821fc43334d5fcb648e5a8e80f323f Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Fri, 22 Oct 2021 13:16:30 +1300 >Subject: [PATCH 095/190] CVE-2020-25722 s4/dsdb/samldb: unique_attr_check uses > samldb_get_single_valued_attr() > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 36 +++++++------------------ > 1 file changed, 10 insertions(+), 26 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 690719ce423..8f22fb45fd1 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -219,37 +219,21 @@ static int samldb_unique_attr_check(struct samldb_ctx *ac, const char *attr, > { > struct ldb_context *ldb = ldb_module_get_ctx(ac->module); > const char * const no_attrs[] = { NULL }; >- struct ldb_result *res; >- const char *enc_str; >- struct ldb_message_element *el; >+ struct ldb_result *res = NULL; >+ const char *str = NULL; >+ const char *enc_str = NULL; > int ret; > >- el = dsdb_get_single_valued_attr(ac->msg, attr, >- ac->req->operation); >- if (el == NULL) { >- /* we are not affected */ >- return LDB_SUCCESS; >- } >- >- if (el->num_values > 1) { >- ldb_asprintf_errstring(ldb, >- "samldb: %s has %u values, should be single-valued!", >- attr, el->num_values); >- return LDB_ERR_CONSTRAINT_VIOLATION; >- } else if (el->num_values == 0) { >- ldb_asprintf_errstring(ldb, >- "samldb: new value for %s not provided for mandatory, single-valued attribute!", >- attr); >- return LDB_ERR_OBJECT_CLASS_VIOLATION; >+ ret = samldb_get_single_valued_attr(ldb, ac, attr, &str); >+ if (ret != LDB_SUCCESS) { >+ return ret; > } >- if (el->values[0].length == 0) { >- ldb_asprintf_errstring(ldb, >- "samldb: %s is of zero length, should have a value!", >- attr); >- return LDB_ERR_OBJECT_CLASS_VIOLATION; >+ if (str == NULL) { >+ /* the attribute wasn't found */ >+ return LDB_SUCCESS; > } >- enc_str = ldb_binary_encode(ac, el->values[0]); > >+ enc_str = ldb_binary_encode_string(ac, str); > if (enc_str == NULL) { > return ldb_module_oom(ac->module); > } >-- >2.25.1 > > >From 5d3914eec5247e5208ebae6f72f1d9ba520cfc43 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Fri, 22 Oct 2021 13:17:34 +1300 >Subject: [PATCH 096/190] CVE-2020-25722 s4/dsdb/samldb: check for clashes in > UPNs/samaccountnames > >We already know duplicate sAMAccountNames and UserPrincipalNames are bad, >but we also have to check against the values these imply in each other. > >For example, imagine users with SAM account names "Alice" and "Bob" in >the realm "example.com". If they do not have explicit UPNs, by the logic >of MS-ADTS 5.1.1.1.1 they use the implict UPNs "alice@example.com" and >"bob@example.com", respectively. If Bob's UPN gets set to >"alice@example.com", it will clash with Alice's implicit one. > >Therefore we refuse to allow a UPN that implies an existing SAM account >name and vice versa. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > selftest/knownfail.d/ldap_upn_sam_account | 15 -- > source4/dsdb/samdb/ldb_modules/samldb.c | 206 +++++++++++++++++++++- > 2 files changed, 203 insertions(+), 18 deletions(-) > >diff --git a/selftest/knownfail.d/ldap_upn_sam_account b/selftest/knownfail.d/ldap_upn_sam_account >index c4d494968b2..e53d566816e 100644 >--- a/selftest/knownfail.d/ldap_upn_sam_account >+++ b/selftest/knownfail.d/ldap_upn_sam_account >@@ -1,16 +1 @@ > samba.tests.ldap_upn_sam_account.+LdapUpnSamSambaOnlyTest.test_upn_sam_SAM_contains_delete >-samba.tests.ldap_upn_sam_account.+LdapUpnSamSambaOnlyTest.test_upn_sam_UPN_same_but_for_internal_spaces >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_SAM_ends_with_at >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_SAM_has_at_signs_clashing_upn_first >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_SAM_has_at_signs_clashing_upn_second >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_SAM_starts_with_at >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_UPN_clash_on_other_realm >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_UPN_ends_with_at >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_UPN_has_no_at >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_UPN_starts_with_at >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_add_the_same_upn_to_different_objects >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_different_objects_SAM_after_UPN >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_different_objects_SAM_before_UPN >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_different_objects_same_UPN_different_case >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_spaces_around_at >-samba.tests.ldap_upn_sam_account.+LdapUpnSamTest.test_upn_sam_two_way_clash >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 8f22fb45fd1..af32e942c82 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -238,8 +238,9 @@ static int samldb_unique_attr_check(struct samldb_ctx *ac, const char *attr, > return ldb_module_oom(ac->module); > } > >- /* Make sure that attr (eg) "sAMAccountName" is only used once */ >- >+ /* >+ * No other object should have the attribute with this value. >+ */ > if (attr_conflict != NULL) { > ret = dsdb_module_search(ac->module, ac, &res, > base_dn, >@@ -273,6 +274,193 @@ static int samldb_unique_attr_check(struct samldb_ctx *ac, const char *attr, > return LDB_SUCCESS; > } > >+ >+ >+static inline int samldb_sam_account_upn_clash_sub_search( >+ struct samldb_ctx *ac, >+ TALLOC_CTX *mem_ctx, >+ struct ldb_dn *base_dn, >+ const char *attr, >+ const char *value, >+ const char *err_msg >+ ) >+{ >+ /* >+ * A very specific helper function for samldb_sam_account_upn_clash(), >+ * where we end up doing this same thing several times in a row. >+ */ >+ const char * const no_attrs[] = { NULL }; >+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module); >+ struct ldb_result *res = NULL; >+ int ret; >+ char *enc_value = ldb_binary_encode_string(ac, value); >+ if (enc_value == NULL) { >+ return ldb_module_oom(ac->module); >+ } >+ ret = dsdb_module_search(ac->module, mem_ctx, &res, >+ base_dn, >+ LDB_SCOPE_SUBTREE, no_attrs, >+ DSDB_FLAG_NEXT_MODULE, ac->req, >+ "(%s=%s)", >+ attr, enc_value); >+ talloc_free(enc_value); >+ >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } else if (res->count > 1) { >+ return ldb_operr(ldb); >+ } else if (res->count == 1) { >+ if (ldb_dn_compare(res->msgs[0]->dn, ac->msg->dn) != 0){ >+ ldb_asprintf_errstring(ldb, >+ "samldb: %s '%s' " >+ "is already in use %s", >+ attr, value, err_msg); >+ /* different errors for different attrs */ >+ if (strcasecmp("userPrincipalName", attr) == 0) { >+ return LDB_ERR_CONSTRAINT_VIOLATION; >+ } >+ return LDB_ERR_ENTRY_ALREADY_EXISTS; >+ } >+ } >+ return LDB_SUCCESS; >+} >+ >+static int samldb_sam_account_upn_clash(struct samldb_ctx *ac) >+{ >+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module); >+ int ret; >+ struct ldb_dn *base_dn = ldb_get_default_basedn(ldb); >+ TALLOC_CTX *tmp_ctx = NULL; >+ const char *real_sam = NULL; >+ const char *real_upn = NULL; >+ char *implied_sam = NULL; >+ char *implied_upn = NULL; >+ const char *realm = NULL; >+ >+ ret = samldb_get_single_valued_attr(ldb, ac, >+ "sAMAccountName", >+ &real_sam); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ ret = samldb_get_single_valued_attr(ldb, ac, >+ "userPrincipalName", >+ &real_upn); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ if (real_upn == NULL && real_sam == NULL) { >+ /* Not changing these things, so we're done */ >+ return LDB_SUCCESS; >+ } >+ >+ tmp_ctx = talloc_new(ac); >+ realm = samdb_dn_to_dns_domain(tmp_ctx, base_dn); >+ if (realm == NULL) { >+ talloc_free(tmp_ctx); >+ return ldb_operr(ldb); >+ } >+ >+ if (real_upn != NULL) { >+ /* >+ * note we take the last @ in the upn because the first (i.e. >+ * sAMAccountName equivalent) part can contain @. >+ * >+ * It is also OK (per Windows) for a UPN to have zero @s. >+ */ >+ char *at = NULL; >+ char *upn_realm = NULL; >+ implied_sam = talloc_strdup(tmp_ctx, real_upn); >+ if (implied_sam == NULL) { >+ talloc_free(tmp_ctx); >+ return ldb_module_oom(ac->module); >+ } >+ >+ at = strrchr(implied_sam, '@'); >+ if (at == NULL) { >+ /* >+ * there is no @ in this UPN, so we treat the whole >+ * thing as a sAMAccountName for the purposes of a >+ * clash. >+ */ >+ DBG_INFO("samldb: userPrincipalName '%s' contains " >+ "no '@' character\n", implied_sam); >+ } else { >+ /* >+ * Now, this upn only implies a sAMAccountName if the >+ * realm is our realm. So we need to compare the tail >+ * of the upn to the realm. >+ */ >+ *at = '\0'; >+ upn_realm = at + 1; >+ if (strcasecmp(upn_realm, realm) != 0) { >+ /* implied_sam is not the implied >+ * sAMAccountName after all, because it is >+ * from a different realm. */ >+ TALLOC_FREE(implied_sam); >+ } >+ } >+ } >+ >+ if (real_sam != NULL) { >+ implied_upn = talloc_asprintf(tmp_ctx, "%s@%s", >+ real_sam, realm); >+ if (implied_upn == NULL) { >+ talloc_free(tmp_ctx); >+ return ldb_module_oom(ac->module); >+ } >+ } >+ >+ /* >+ * Now we have all of the actual and implied names, in which to search >+ * for conflicts. >+ */ >+ if (real_sam != NULL) { >+ ret = samldb_sam_account_upn_clash_sub_search( >+ ac, tmp_ctx, base_dn, "sAMAccountName", >+ real_sam, ""); >+ >+ if (ret != LDB_SUCCESS) { >+ talloc_free(tmp_ctx); >+ return ret; >+ } >+ } >+ if (implied_upn != NULL) { >+ ret = samldb_sam_account_upn_clash_sub_search( >+ ac, tmp_ctx, base_dn, "userPrincipalName", implied_upn, >+ "(implied by sAMAccountName)"); >+ >+ if (ret != LDB_SUCCESS) { >+ talloc_free(tmp_ctx); >+ return ret; >+ } >+ } >+ if (real_upn != NULL) { >+ ret = samldb_sam_account_upn_clash_sub_search( >+ ac, tmp_ctx, base_dn, "userPrincipalName", >+ real_upn, ""); >+ >+ if (ret != LDB_SUCCESS) { >+ talloc_free(tmp_ctx); >+ return ret; >+ } >+ } >+ if (implied_sam != NULL) { >+ ret = samldb_sam_account_upn_clash_sub_search( >+ ac, tmp_ctx, base_dn, "sAMAccountName", implied_sam, >+ "(implied by userPrincipalName)"); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(tmp_ctx); >+ return ret; >+ } >+ } >+ >+ talloc_free(tmp_ctx); >+ return LDB_SUCCESS; >+} >+ >+ >+/* This is run during an add or modify */ > static int samldb_sam_accountname_valid_check(struct samldb_ctx *ac) > { > int ret = 0; >@@ -306,7 +494,11 @@ static int samldb_sam_accountname_valid_check(struct samldb_ctx *ac) > } else if (ret == LDB_ERR_OBJECT_CLASS_VIOLATION) { > ret = LDB_ERR_CONSTRAINT_VIOLATION; > } >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } > >+ ret = samldb_sam_account_upn_clash(ac); > if (ret != LDB_SUCCESS) { > return ret; > } >@@ -4180,7 +4372,6 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req) > if (ret != LDB_SUCCESS) { > return ret; > } >- > user_account_control > = ldb_msg_find_attr_as_uint(res->msgs[0], > "userAccountControl", >@@ -4204,6 +4395,15 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req) > } > } > >+ el = ldb_msg_find_element(ac->msg, "userPrincipalName"); >+ if (el != NULL) { >+ ret = samldb_sam_account_upn_clash(ac); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(ac); >+ return ret; >+ } >+ } >+ > el = ldb_msg_find_element(ac->msg, "ldapDisplayName"); > if (el != NULL) { > ret = samldb_schema_ldapdisplayname_valid_check(ac); >-- >2.25.1 > > >From abbb478237d27e5af6ee581003250a7315956bea Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Fri, 22 Oct 2021 15:27:25 +1300 >Subject: [PATCH 097/190] CVE-2020-25722 s4/dsdb/samldb: check sAMAccountName > for illegal characters > >This only for the real account name, not the account name implicit in >a UPN. It doesn't matter if a UPN implies an illegal sAMAccountName, >since that is not going to conflict with a real one. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > selftest/knownfail.d/ldap_upn_sam_account | 1 - > source4/dsdb/samdb/ldb_modules/samldb.c | 58 +++++++++++++++++++++++ > 2 files changed, 58 insertions(+), 1 deletion(-) > delete mode 100644 selftest/knownfail.d/ldap_upn_sam_account > >diff --git a/selftest/knownfail.d/ldap_upn_sam_account b/selftest/knownfail.d/ldap_upn_sam_account >deleted file mode 100644 >index e53d566816e..00000000000 >--- a/selftest/knownfail.d/ldap_upn_sam_account >+++ /dev/null >@@ -1 +0,0 @@ >-samba.tests.ldap_upn_sam_account.+LdapUpnSamSambaOnlyTest.test_upn_sam_SAM_contains_delete >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index af32e942c82..b0628df1131 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -325,6 +325,59 @@ static inline int samldb_sam_account_upn_clash_sub_search( > return LDB_SUCCESS; > } > >+static int samaccountname_bad_chars_check(struct samldb_ctx *ac, >+ const char *name) >+{ >+ /* >+ * The rules here are based on >+ * >+ * https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx >+ * >+ * Windows considers UTF-8 sequences that map to "similar" characters >+ * (e.g. 'a', 'Ä') to be the same sAMAccountName, and we don't. Names >+ * that are not valid UTF-8 *are* allowed. >+ * >+ * Additionally, Samba collapses multiple spaces, and Windows doesn't. >+ */ >+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module); >+ size_t i; >+ >+ for (i = 0; name[i] != '\0'; i++) { >+ uint8_t c = name[i]; >+ char *p = NULL; >+ if (c < 32 || c == 127) { >+ ldb_asprintf_errstring( >+ ldb, >+ "samldb: sAMAccountName contains invalid " >+ "0x%.2x character\n", c); >+ return LDB_ERR_CONSTRAINT_VIOLATION; >+ } >+ p = strchr("\"[]:;|=+*?<>/\\,", c); >+ if (p != NULL) { >+ ldb_asprintf_errstring( >+ ldb, >+ "samldb: sAMAccountName contains invalid " >+ "'%c' character\n", c); >+ return LDB_ERR_CONSTRAINT_VIOLATION; >+ } >+ } >+ >+ if (i == 0) { >+ ldb_asprintf_errstring( >+ ldb, >+ "samldb: sAMAccountName is empty\n"); >+ return LDB_ERR_CONSTRAINT_VIOLATION; >+ } >+ >+ if (name[i - 1] == '.') { >+ ldb_asprintf_errstring( >+ ldb, >+ "samldb: sAMAccountName ends with '.'"); >+ return LDB_ERR_CONSTRAINT_VIOLATION; >+ } >+ return LDB_SUCCESS; >+} >+ > static int samldb_sam_account_upn_clash(struct samldb_ctx *ac) > { > struct ldb_context *ldb = ldb_module_get_ctx(ac->module); >@@ -424,6 +477,11 @@ static int samldb_sam_account_upn_clash(struct samldb_ctx *ac) > talloc_free(tmp_ctx); > return ret; > } >+ ret = samaccountname_bad_chars_check(ac, real_sam); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(tmp_ctx); >+ return ret; >+ } > } > if (implied_upn != NULL) { > ret = samldb_sam_account_upn_clash_sub_search( >-- >2.25.1 > > >From 19527958663896cecaf4a3d04f55b3e155dfa42f Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Fri, 22 Oct 2021 13:14:32 +1300 >Subject: [PATCH 098/190] CVE-2020-25722 s4/dsdb/samldb: check for SPN > uniqueness, including aliases > >Not only should it not be possible to add a servicePrincipalName that >is already present in the domain, it should not be possible to add one >that is implied by an entry in sPNMappings, unless the user is adding >an alias to another SPN and has rights to alter that one. > >For example, with the default sPNMappings, cifs/ is an alias pointing to >host/, meaning if there is no cifs/example.com SPN, the host/example.com >one will be used instead. A user can add the cifs/example.com SPN only >if they can also change the host/example.com one (because adding the >cifs/ effectively changes the host/). The reverse is refused in all cases, >unless they happen to be on the same object. That is, if there is a >cifs/example.com SPN, there is no way to add host/example.com elsewhere. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > selftest/knownfail.d/ldap_spn | 23 - > source4/dsdb/samdb/ldb_modules/samldb.c | 584 +++++++++++++++++++++++- > 2 files changed, 581 insertions(+), 26 deletions(-) > >diff --git a/selftest/knownfail.d/ldap_spn b/selftest/knownfail.d/ldap_spn >index dc768728658..b7eb6f30e7a 100644 >--- a/selftest/knownfail.d/ldap_spn >+++ b/selftest/knownfail.d/ldap_spn >@@ -1,26 +1,3 @@ >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_host_first_both_on_user_service_rights >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_host_first_with_service_write_rights >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_service_first_as_admin >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_service_first_with_both_write_rights >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_add_a_conflict_service_first_with_host_write_rights >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_changing_dNSHostName_after_host >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_mystery_dnsname_clash_www_first >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_host_first_with_partial_write_rights >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_host_first_with_partial_write_rights_2 >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_sandwich_sufficient_rights >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_service_first_just_sufficient_rights >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_service_first_with_all_write_rights >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_service_first_with_both_write_rights >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_service_first_with_host_write_rights >-samba.tests.ldap_spn.+LdapSpnSambaOnlyTest.test_spn_three_way_conflict_services_first_with_partial_rights >-samba.tests.ldap_spn.+LdapSpnTest.test_spn_adding_dNSHostName_after_cifs_with_no_old_dNSHostName >-samba.tests.ldap_spn.+LdapSpnTest.test_spn_changing_dNSHostName_after_cifs > samba.tests.ldap_spn.+LdapSpnTest.test_spn_dodgy_spns >-samba.tests.ldap_spn.+LdapSpnTest.test_spn_empty_part_spns_consecutive_slashes_ > samba.tests.ldap_spn.+LdapSpnTest.test_spn_one_part_spns_no_slashes_ >-samba.tests.ldap_spn.+LdapSpnTest.test_spn_three_part_nonsense_spns >-samba.tests.ldap_spn.+LdapSpnTest.test_spn_three_part_spns >-samba.tests.ldap_spn.+LdapSpnTest.test_spn_three_way_conflict_host_first_adding_duplicate >-samba.tests.ldap_spn.+LdapSpnTest.test_spn_three_way_conflict_host_first_adding_duplicate_full_rights >-samba.tests.ldap_spn.+LdapSpnTest.test_spn_three_way_conflict_services_first_as_admin > samba.tests.ldap_spn.+LdapSpnTest.test_spn_too_many_spn_parts >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index b0628df1131..02a74bf8710 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -3388,6 +3388,542 @@ static int samldb_description_check(struct samldb_ctx *ac, bool *modified) > return LDB_SUCCESS; > } > >+#define SPN_ALIAS_NONE 0 >+#define SPN_ALIAS_LINK 1 >+#define SPN_ALIAS_TARGET 2 >+ >+static int find_spn_aliases(struct ldb_context *ldb, >+ TALLOC_CTX *mem_ctx, >+ const char *service_class, >+ char ***aliases, >+ size_t *n_aliases, >+ int *direction) >+{ >+ /* >+ * If you change the way this works, you should also look at changing >+ * LDB_lookup_spn_alias() in source4/dsdb/samdb/cracknames.c, which >+ * does some of the same work. >+ * >+ * In particular, note that sPNMappings are resolved on a first come, >+ * first served basis. For example, if we have >+ * >+ * host=ldap,cifs >+ * foo=ldap >+ * cifs=host,alerter >+ * >+ * then 'ldap', 'cifs', and 'host' will resolve to 'host', and >+ * 'alerter' will resolve to 'cifs'. >+ * >+ * If this resolution method is made more complicated, then the >+ * cracknames function should also be changed. >+ */ >+ size_t i, j; >+ int ret; >+ bool ok; >+ struct ldb_result *res = NULL; >+ struct ldb_message_element *spnmappings = NULL; >+ TALLOC_CTX *tmp_ctx = NULL; >+ struct ldb_dn *service_dn = NULL; >+ >+ const char *attrs[] = { >+ "sPNMappings", >+ NULL >+ }; >+ >+ *direction = SPN_ALIAS_NONE; >+ >+ tmp_ctx = talloc_new(mem_ctx); >+ if (tmp_ctx == NULL) { >+ return ldb_oom(ldb); >+ } >+ >+ service_dn = ldb_dn_new( >+ tmp_ctx, ldb, >+ "CN=Directory Service,CN=Windows NT,CN=Services"); >+ if (service_dn == NULL) { >+ talloc_free(tmp_ctx); >+ return ldb_oom(ldb); >+ } >+ >+ ok = ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb)); >+ if (! ok) { >+ talloc_free(tmp_ctx); >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ >+ ret = ldb_search(ldb, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE, >+ attrs, "(objectClass=nTDSService)"); >+ >+ if (ret != LDB_SUCCESS || res->count != 1) { >+ DBG_WARNING("sPNMappings not found.\n"); >+ talloc_free(tmp_ctx); >+ return ret; >+ } >+ >+ spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings"); >+ if (spnmappings == NULL || spnmappings->num_values == 0) { >+ DBG_WARNING("no sPNMappings attribute\n"); >+ talloc_free(tmp_ctx); >+ return LDB_ERR_NO_SUCH_OBJECT; >+ } >+ *n_aliases = 0; >+ >+ for (i = 0; i < spnmappings->num_values; i++) { >+ char *p = NULL; >+ char *mapping = talloc_strndup( >+ tmp_ctx, >+ (char *)spnmappings->values[i].data, >+ spnmappings->values[i].length); >+ if (mapping == NULL) { >+ talloc_free(tmp_ctx); >+ return ldb_oom(ldb); >+ } >+ >+ p = strchr(mapping, '='); >+ if (p == NULL) { >+ talloc_free(tmp_ctx); >+ return LDB_ERR_ALIAS_PROBLEM; >+ } >+ p[0] = '\0'; >+ p++; >+ >+ if (strcasecmp(mapping, service_class) == 0) { >+ /* >+ * We need to return the reverse aliases for this one. >+ * >+ * typically, this means the service_class is "host" >+ * and the mapping is "host=alerter,appmgmt,cisvc,..", >+ * so we get "alerter", "appmgmt", etc in the list of >+ * aliases. >+ */ >+ >+ /* There is one more field than there are commas */ >+ size_t n = 1; >+ >+ for (j = 0; p[j] != '\0'; j++) { >+ if (p[j] == ',') { >+ n++; >+ p[j] = '\0'; >+ } >+ } >+ *aliases = talloc_array(mem_ctx, char*, n); >+ if (*aliases == NULL) { >+ talloc_free(tmp_ctx); >+ return ldb_oom(ldb); >+ } >+ *n_aliases = n; >+ talloc_steal(mem_ctx, mapping); >+ for (j = 0; j < n; j++) { >+ (*aliases)[j] = p; >+ p += strlen(p) + 1; >+ } >+ talloc_free(tmp_ctx); >+ *direction = SPN_ALIAS_LINK; >+ return LDB_SUCCESS; >+ } >+ /* >+ * We need to look along the list to see if service_class is >+ * there; if so, we return a list of one item (probably "host"). >+ */ >+ do { >+ char *str = p; >+ p = strchr(p, ','); >+ if (p != NULL) { >+ p[0] = '\0'; >+ p++; >+ } >+ if (strcasecmp(str, service_class) == 0) { >+ *aliases = talloc_array(mem_ctx, char*, 1); >+ if (*aliases == NULL) { >+ talloc_free(tmp_ctx); >+ return ldb_oom(ldb); >+ } >+ *n_aliases = 1; >+ (*aliases)[0] = mapping; >+ talloc_steal(mem_ctx, mapping); >+ talloc_free(tmp_ctx); >+ *direction = SPN_ALIAS_TARGET; >+ return LDB_SUCCESS; >+ } >+ } while (p != NULL); >+ } >+ DBG_INFO("no sPNMappings alias for '%s'\n", service_class); >+ talloc_free(tmp_ctx); >+ *aliases = NULL; >+ *n_aliases = 0; >+ return LDB_SUCCESS; >+} >+ >+ >+static int get_spn_dn(struct ldb_context *ldb, >+ TALLOC_CTX *tmp_ctx, >+ const char *candidate, >+ struct ldb_dn **dn) >+{ >+ int ret; >+ const char *empty_attrs[] = { NULL }; >+ struct ldb_message *msg = NULL; >+ struct ldb_dn *base_dn = ldb_get_default_basedn(ldb); >+ >+ const char *enc_candidate = NULL; >+ >+ *dn = NULL; >+ >+ enc_candidate = ldb_binary_encode_string(tmp_ctx, candidate); >+ if (enc_candidate == NULL) { >+ return ldb_operr(ldb); >+ } >+ >+ ret = dsdb_search_one(ldb, >+ tmp_ctx, >+ &msg, >+ base_dn, >+ LDB_SCOPE_SUBTREE, >+ empty_attrs, >+ 0, >+ "(servicePrincipalName=%s)", >+ enc_candidate); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ *dn = msg->dn; >+ return LDB_SUCCESS; >+} >+ >+ >+static int check_spn_write_rights(struct ldb_context *ldb, >+ TALLOC_CTX *mem_ctx, >+ const char *spn, >+ struct ldb_dn *dn) >+{ >+ int ret; >+ struct ldb_message *msg = NULL; >+ struct ldb_message_element *del_el = NULL; >+ struct ldb_message_element *add_el = NULL; >+ struct ldb_val val = { >+ .data = discard_const_p(uint8_t, spn), >+ .length = strlen(spn) >+ }; >+ >+ msg = ldb_msg_new(mem_ctx); >+ if (msg == NULL) { >+ return ldb_oom(ldb); >+ } >+ msg->dn = dn; >+ >+ ret = ldb_msg_add_empty(msg, >+ "servicePrincipalName", >+ LDB_FLAG_MOD_DELETE, >+ &del_el); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(msg); >+ return ret; >+ } >+ ret = ldb_msg_add_empty(msg, >+ "servicePrincipalName", >+ LDB_FLAG_MOD_ADD, >+ &add_el); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(msg); >+ return ret; >+ } >+ >+ del_el->values = talloc_array(msg->elements, struct ldb_val, 1); >+ if (del_el->values == NULL) { >+ talloc_free(msg); >+ return ret; >+ } >+ >+ add_el->values = talloc_array(msg->elements, struct ldb_val, 1); >+ if (add_el->values == NULL) { >+ talloc_free(msg); >+ return ret; >+ } >+ >+ del_el->values[0] = val; >+ del_el->num_values = 1; >+ add_el->values[0] = val; >+ add_el->num_values = 1; >+ ret = ldb_modify(ldb, msg); >+ if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) { >+ DBG_ERR("hmm I think we're OK, but not sure\n"); >+ } else if (ret != LDB_SUCCESS) { >+ DBG_ERR("SPN write rights check failed with %d\n", ret); >+ talloc_free(msg); >+ return ret; >+ } >+ talloc_free(msg); >+ return LDB_SUCCESS; >+} >+ >+ >+static int check_spn_alias_collision(struct ldb_context *ldb, >+ TALLOC_CTX *mem_ctx, >+ const char *spn, >+ struct ldb_dn *target_dn) >+{ >+ int ret; >+ char *service_class = NULL; >+ char *spn_tail = NULL; >+ char *p = NULL; >+ char **aliases = NULL; >+ size_t n_aliases = 0; >+ size_t i, len; >+ TALLOC_CTX *tmp_ctx = NULL; >+ const char *target_dnstr = ldb_dn_get_linearized(target_dn); >+ int link_direction; >+ >+ tmp_ctx = talloc_new(mem_ctx); >+ if (tmp_ctx == NULL) { >+ return ldb_oom(ldb); >+ } >+ >+ /* >+ * "dns/example.com/xxx" gives >+ * service_class = "dns" >+ * spn_tail = "example.com/xxx" >+ */ >+ p = strchr(spn, '/'); >+ if (p == NULL) { >+ /* bad SPN */ >+ talloc_free(tmp_ctx); >+ return ldb_error(ldb, >+ LDB_ERR_OPERATIONS_ERROR, >+ "malformed servicePrincipalName"); >+ } >+ len = p - spn; >+ >+ service_class = talloc_strndup(tmp_ctx, spn, len); >+ if (service_class == NULL) { >+ talloc_free(tmp_ctx); >+ return ldb_oom(ldb); >+ } >+ spn_tail = p + 1; >+ >+ ret = find_spn_aliases(ldb, >+ tmp_ctx, >+ service_class, >+ &aliases, >+ &n_aliases, >+ &link_direction); >+ if (ret != LDB_SUCCESS) { >+ talloc_free(tmp_ctx); >+ return ret; >+ } >+ >+ /* >+ * we have the list of aliases, and now we need to combined them with >+ * spn_tail and see if we can find the SPN. >+ */ >+ for (i = 0; i < n_aliases; i++) { >+ struct ldb_dn *colliding_dn = NULL; >+ const char *colliding_dnstr = NULL; >+ >+ char *candidate = talloc_asprintf(tmp_ctx, >+ "%s/%s", >+ aliases[i], >+ spn_tail); >+ if (candidate == NULL) { >+ talloc_free(tmp_ctx); >+ return ldb_oom(ldb); >+ } >+ >+ ret = get_spn_dn(ldb, tmp_ctx, candidate, &colliding_dn); >+ if (ret == LDB_ERR_NO_SUCH_OBJECT) { >+ DBG_DEBUG("SPN alias '%s' not found (good)\n", >+ candidate); >+ talloc_free(candidate); >+ continue; >+ } >+ if (ret != LDB_SUCCESS) { >+ DBG_ERR("SPN '%s' search error %d\n", candidate, ret); >+ talloc_free(tmp_ctx); >+ return ret; >+ } >+ >+ target_dnstr = ldb_dn_get_linearized(target_dn); >+ /* >+ * We have found an existing SPN that matches the alias. That >+ * is OK only if it is on the object we are trying to add to, >+ * or if the SPN on the other side is a more generic alias for >+ * this one and we also have rights to modify it. >+ * >+ * That is, we can put "host/X" and "cifs/X" on the same >+ * object, but not on different objects, unless we put the >+ * host/X on first, and could also change that object when we >+ * add cifs/X. It is forbidden to add the objects in the other >+ * order. >+ * >+ * The rationale for this is that adding "cifs/X" effectively >+ * changes "host/X" by diverting traffic. If "host/X" can be >+ * added after "cifs/X", a sneaky person could get "cifs/X" in >+ * first, making "host/X" have less effect than intended. >+ * >+ * Note: we also can't have "host/X" and "Host/X" on the same >+ * object, but that is not relevant here. >+ */ >+ >+ ret = ldb_dn_compare(colliding_dn, target_dn); >+ if (ret != 0) { >+ colliding_dnstr = ldb_dn_get_linearized(colliding_dn); >+ DBG_ERR("trying to add SPN '%s' on '%s' when '%s' is " >+ "on '%s'\n", >+ spn, >+ target_dnstr, >+ candidate, >+ colliding_dnstr); >+ >+ if (link_direction == SPN_ALIAS_LINK) { >+ /* we don't allow host/X if there is a >+ * cifs/X */ >+ talloc_free(tmp_ctx); >+ return LDB_ERR_CONSTRAINT_VIOLATION; >+ } >+ ret = check_spn_write_rights(ldb, >+ tmp_ctx, >+ candidate, >+ colliding_dn); >+ if (ret != LDB_SUCCESS) { >+ DBG_ERR("SPN '%s' is on '%s' so '%s' can't be " >+ "added to '%s'\n", >+ candidate, >+ colliding_dnstr, >+ spn, >+ target_dnstr); >+ talloc_free(tmp_ctx); >+ return ldb_error( >+ ldb, >+ LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS, >+ "SPN conflict"); >+ } >+ } else { >+ DBG_INFO("SPNs '%s' and '%s' alias both on '%s'\n", >+ candidate, spn, target_dnstr); >+ } >+ talloc_free(candidate); >+ } >+ >+ talloc_free(tmp_ctx); >+ return LDB_SUCCESS; >+} >+ >+static int check_spn_direct_collision(struct ldb_context *ldb, >+ TALLOC_CTX *mem_ctx, >+ const char *spn, >+ struct ldb_dn *target_dn) >+{ >+ int ret; >+ TALLOC_CTX *tmp_ctx = NULL; >+ struct ldb_dn *colliding_dn = NULL; >+ const char *target_dnstr = NULL; >+ const char *colliding_dnstr = NULL; >+ >+ tmp_ctx = talloc_new(mem_ctx); >+ if (tmp_ctx == NULL) { >+ return ldb_oom(ldb); >+ } >+ >+ ret = get_spn_dn(ldb, tmp_ctx, spn, &colliding_dn); >+ if (ret == LDB_ERR_NO_SUCH_OBJECT) { >+ DBG_DEBUG("SPN '%s' not found (good)\n", spn); >+ talloc_free(tmp_ctx); >+ return LDB_SUCCESS; >+ } >+ if (ret != LDB_SUCCESS) { >+ DBG_ERR("SPN '%s' search error %d\n", spn, ret); >+ talloc_free(tmp_ctx); >+ if (ret == LDB_ERR_COMPARE_TRUE) { >+ /* >+ * COMPARE_TRUE has special meaning here and we don't >+ * want to return it by mistake. >+ */ >+ ret = LDB_ERR_OPERATIONS_ERROR; >+ } >+ return ret; >+ } >+ /* >+ * We have found this exact SPN. This is mostly harmless (depend on >+ * ADD vs REPLACE) when the spn is being put on the object that >+ * already has, so we let it through to succeed or fail as some other >+ * module sees fit. >+ */ >+ target_dnstr = ldb_dn_get_linearized(target_dn); >+ ret = ldb_dn_compare(colliding_dn, target_dn); >+ if (ret != 0) { >+ colliding_dnstr = ldb_dn_get_linearized(colliding_dn); >+ DBG_ERR("SPN '%s' is on '%s' so it can't be " >+ "added to '%s'\n", >+ spn, >+ colliding_dnstr, >+ target_dnstr); >+ talloc_free(tmp_ctx); >+ return ldb_error(ldb, >+ LDB_ERR_CONSTRAINT_VIOLATION, >+ "SPN conflict"); >+ } >+ >+ DBG_INFO("SPN '%s' is already on '%s'\n", >+ spn, target_dnstr); >+ talloc_free(tmp_ctx); >+ return LDB_ERR_COMPARE_TRUE; >+} >+ >+ >+/* Check that "servicePrincipalName" changes do not introduce a collision >+ * globally. */ >+static int samldb_spn_uniqueness_check(struct samldb_ctx *ac, >+ struct ldb_message_element *spn_el) >+{ >+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module); >+ int ret; >+ const char *spn = NULL; >+ size_t i; >+ TALLOC_CTX *tmp_ctx = talloc_new(ac->msg); >+ if (tmp_ctx == NULL) { >+ return ldb_oom(ldb); >+ } >+ >+ for (i = 0; i < spn_el->num_values; i++) { >+ spn = (char *)spn_el->values[i].data; >+ >+ ret = check_spn_direct_collision(ldb, >+ tmp_ctx, >+ spn, >+ ac->msg->dn); >+ if (ret == LDB_ERR_COMPARE_TRUE) { >+ DBG_INFO("SPN %s re-added to the same object\n", spn); >+ talloc_free(tmp_ctx); >+ return LDB_SUCCESS; >+ } >+ if (ret != LDB_SUCCESS) { >+ DBG_ERR("SPN %s failed direct uniqueness check\n", spn); >+ talloc_free(tmp_ctx); >+ return ret; >+ } >+ >+ ret = check_spn_alias_collision(ldb, >+ tmp_ctx, >+ spn, >+ ac->msg->dn); >+ >+ if (ret == LDB_ERR_NO_SUCH_OBJECT) { >+ /* we have no sPNMappings, hence no aliases */ >+ break; >+ } >+ if (ret != LDB_SUCCESS) { >+ DBG_ERR("SPN %s failed alias uniqueness check\n", spn); >+ talloc_free(tmp_ctx); >+ return ret; >+ } >+ DBG_INFO("SPN %s seems to be unique\n", spn); >+ } >+ >+ talloc_free(tmp_ctx); >+ return LDB_SUCCESS; >+} >+ >+ >+ > /* This trigger adapts the "servicePrincipalName" attributes if the > * "dNSHostName" and/or "sAMAccountName" attribute change(s) */ > static int samldb_service_principal_names_change(struct samldb_ctx *ac) >@@ -3503,8 +4039,14 @@ static int samldb_service_principal_names_change(struct samldb_ctx *ac) > return LDB_SUCCESS; > } > >- /* Potential "servicePrincipalName" changes in the same request have to >- * be handled before the update (Windows behaviour). */ >+ /* >+ * Potential "servicePrincipalName" changes in the same request have >+ * to be handled before the update (Windows behaviour). >+ * >+ * We extract the SPN changes into a new message and run it through >+ * the stack from this module, so that it subjects them to the SPN >+ * checks we have here. >+ */ > el = ldb_msg_find_element(ac->msg, "servicePrincipalName"); > if (el != NULL) { > msg = ldb_msg_new(ac->msg); >@@ -3526,7 +4068,7 @@ static int samldb_service_principal_names_change(struct samldb_ctx *ac) > } while (el != NULL); > > ret = dsdb_module_modify(ac->module, msg, >- DSDB_FLAG_NEXT_MODULE, ac->req); >+ DSDB_FLAG_OWN_MODULE, ac->req); > if (ret != LDB_SUCCESS) { > return ret; > } >@@ -4260,6 +4802,19 @@ static int samldb_add(struct ldb_module *module, struct ldb_request *req) > return samldb_fill_object(ac); > } > >+ >+ el = ldb_msg_find_element(ac->msg, "servicePrincipalName"); >+ if ((el != NULL)) { >+ /* >+ * We need to check whether the SPN collides with an existing >+ * one (anywhere) including via aliases. >+ */ >+ ret = samldb_spn_uniqueness_check(ac, el); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ } >+ > if (samdb_find_attribute(ldb, ac->msg, > "objectclass", "subnet") != NULL) { > ret = samldb_verify_subnet(ac, ac->msg->dn); >@@ -4510,12 +5065,35 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req) > el2 = ldb_msg_find_element(ac->msg, "sAMAccountName"); > if ((el != NULL) || (el2 != NULL)) { > modified = true; >+ /* >+ * samldb_service_principal_names_change() might add SPN >+ * changes to the request, so this must come before the SPN >+ * uniqueness check below. >+ * >+ * Note we ALSO have to do the SPN uniqueness check inside >+ * samldb_service_principal_names_change(), because it does a >+ * subrequest to do requested SPN modifications *before* its >+ * automatic ones are added. >+ */ > ret = samldb_service_principal_names_change(ac); > if (ret != LDB_SUCCESS) { > return ret; > } > } > >+ el = ldb_msg_find_element(ac->msg, "servicePrincipalName"); >+ if ((el != NULL)) { >+ /* >+ * We need to check whether the SPN collides with an existing >+ * one (anywhere) including via aliases. >+ */ >+ modified = true; >+ ret = samldb_spn_uniqueness_check(ac, el); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ } >+ > el = ldb_msg_find_element(ac->msg, "fSMORoleOwner"); > if (el != NULL) { > ret = samldb_fsmo_role_owner_check(ac); >-- >2.25.1 > > >From a29c6625ab2fe9751ae04aac9d2e420fb817b012 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Fri, 22 Oct 2021 16:03:18 +1300 >Subject: [PATCH 099/190] CVE-2020-25722 s4/dsdb/samldb: reject SPN with too > few/many components > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14564 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > selftest/knownfail.d/ldap_spn | 2 -- > source4/dsdb/samdb/ldb_modules/samldb.c | 38 +++++++++++++++++++++++++ > 2 files changed, 38 insertions(+), 2 deletions(-) > >diff --git a/selftest/knownfail.d/ldap_spn b/selftest/knownfail.d/ldap_spn >index b7eb6f30e7a..63f9fe02ef7 100644 >--- a/selftest/knownfail.d/ldap_spn >+++ b/selftest/knownfail.d/ldap_spn >@@ -1,3 +1 @@ > samba.tests.ldap_spn.+LdapSpnTest.test_spn_dodgy_spns >-samba.tests.ldap_spn.+LdapSpnTest.test_spn_one_part_spns_no_slashes_ >-samba.tests.ldap_spn.+LdapSpnTest.test_spn_too_many_spn_parts >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 02a74bf8710..b56ddf49537 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -3869,6 +3869,37 @@ static int check_spn_direct_collision(struct ldb_context *ldb, > } > > >+static int count_spn_components(struct ldb_val val) >+{ >+ /* >+ * a 3 part servicePrincipalName has two slashes, like >+ * ldap/example.com/DomainDNSZones.example.com. >+ * >+ * In krb5_parse_name_flags() we don't count "\/" as a slash (i.e. >+ * escaped by a backslash), but this is not the behaviour of Windows >+ * on setting a servicePrincipalName -- slashes are counted regardless >+ * of backslashes. >+ * >+ * Accordingly, here we ignore backslashes. This will reject >+ * multi-slash SPNs that krb5_parse_name_flags() would accept, and >+ * allow ones in the form "a\/b" that it won't parse. >+ */ >+ size_t i; >+ int slashes = 0; >+ for (i = 0; i < val.length; i++) { >+ char c = val.data[i]; >+ if (c == '/') { >+ slashes++; >+ if (slashes == 3) { >+ /* at this point we don't care */ >+ return 4; >+ } >+ } >+ } >+ return slashes + 1; >+} >+ >+ > /* Check that "servicePrincipalName" changes do not introduce a collision > * globally. */ > static int samldb_spn_uniqueness_check(struct samldb_ctx *ac, >@@ -3884,8 +3915,15 @@ static int samldb_spn_uniqueness_check(struct samldb_ctx *ac, > } > > for (i = 0; i < spn_el->num_values; i++) { >+ int n_components; > spn = (char *)spn_el->values[i].data; > >+ n_components = count_spn_components(spn_el->values[i]); >+ if (n_components > 3 || n_components < 2) { >+ talloc_free(tmp_ctx); >+ return LDB_ERR_CONSTRAINT_VIOLATION; >+ } >+ > ret = check_spn_direct_collision(ldb, > tmp_ctx, > spn, >-- >2.25.1 > > >From b70d1b5be68396df0e1f0129279d60ab3a3f3819 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:09:21 +1300 >Subject: [PATCH 100/190] CVE-2020-25722 s4/dsdb modules: add > dsdb_get_expected_new_values() > >This function collects a superset of all the new values for the specified >attribute that could result from an ldb add or modify message. > >In most cases -- where there is a single add or modify -- the exact set >of added values is returned, and this is done reasonably efficiently >using the existing element. Where it gets complicated is when there are >multiple elements for the same attribute in a message. Anything added >before a replace or delete will be included in these results but may not >end up in the database if the message runs its course. Examples: > > sequence result >1. ADD the element is returned (exact) >2. REPLACE the element is returned (exact) >3. ADD, ADD both elements are concatenated together (exact) >4. ADD, REPLACE both elements are concatenated together (superset) >5. REPLACE, ADD both elements are concatenated together (exact) >6. ADD, DEL, ADD adds are concatenated together (superset) >7. REPLACE, REPLACE both concatenated (superset) >8. DEL, ADD last element is returned (exact) > >Why this? In the past we have treated dsdb_get_single_valued_attr() as if >it returned the complete set of possible database changes, when in fact it >only returned the last non-delete. That is, it could have missed values >in examples 3-7 above. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/util.c | 121 ++++++++++++++++++++++++++ > 1 file changed, 121 insertions(+) > >diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c >index 0e8b72233f4..b10177775a8 100644 >--- a/source4/dsdb/samdb/ldb_modules/util.c >+++ b/source4/dsdb/samdb/ldb_modules/util.c >@@ -1443,6 +1443,127 @@ void dsdb_req_chain_debug(struct ldb_request *req, int level) > talloc_free(s); > } > >+/* >+ * Get all the values that *might* be added by an ldb message, as a composite >+ * ldb element. >+ * >+ * This is useful when we need to check all the possible values against some >+ * criteria. >+ * >+ * In cases where a modify message mixes multiple ADDs, DELETEs, and REPLACES, >+ * the returned element might contain more values than would actually end up >+ * in the database if the message was run to its conclusion. >+ * >+ * If the operation is not LDB_ADD or LDB_MODIFY, an operations error is >+ * returned. >+ * >+ * The returned element might not be new, and should not be modified or freed >+ * before the message is finished. >+ */ >+ >+int dsdb_get_expected_new_values(TALLOC_CTX *mem_ctx, >+ const struct ldb_message *msg, >+ const char *attr_name, >+ struct ldb_message_element **el, >+ enum ldb_request_type operation) >+{ >+ unsigned int i; >+ unsigned int el_count = 0; >+ unsigned int val_count = 0; >+ struct ldb_val *v = NULL; >+ struct ldb_message_element *_el = NULL; >+ *el = NULL; >+ >+ if (operation != LDB_ADD && operation != LDB_MODIFY) { >+ DBG_ERR("inapplicable operation type: %d\n", operation); >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ >+ /* count the adding or replacing elements */ >+ for (i = 0; i < msg->num_elements; i++) { >+ if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) { >+ unsigned int tmp; >+ if ((operation == LDB_MODIFY) && >+ (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) >+ == LDB_FLAG_MOD_DELETE)) { >+ continue; >+ } >+ el_count++; >+ tmp = val_count + msg->elements[i].num_values; >+ if (unlikely(tmp < val_count)) { >+ DBG_ERR("too many values for one element!"); >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ val_count = tmp; >+ } >+ } >+ if (el_count == 0) { >+ /* nothing to see here */ >+ return LDB_SUCCESS; >+ } >+ >+ if (el_count == 1 || val_count == 0) { >+ /* >+ * There is one effective element, which we can return as-is, >+ * OR there are only elements with zero values -- any of which >+ * will do. >+ */ >+ for (i = 0; i < msg->num_elements; i++) { >+ if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) { >+ if ((operation == LDB_MODIFY) && >+ (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) >+ == LDB_FLAG_MOD_DELETE)) { >+ continue; >+ } >+ *el = &msg->elements[i]; >+ return LDB_SUCCESS; >+ } >+ } >+ } >+ >+ _el = talloc_zero(mem_ctx, struct ldb_message_element); >+ if (_el == NULL) { >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ _el->name = attr_name; >+ >+ if (val_count == 0) { >+ /* >+ * Seems unlikely, but sometimes we might be adding zero >+ * values in multiple separate elements. The talloc zero has >+ * already set the expected values = NULL, num_values = 0. >+ */ >+ *el = _el; >+ return LDB_SUCCESS; >+ } >+ >+ _el->values = talloc_array(_el, struct ldb_val, val_count); >+ if (_el->values == NULL) { >+ talloc_free(_el); >+ return LDB_ERR_OPERATIONS_ERROR; >+ } >+ _el->num_values = val_count; >+ >+ v = _el->values; >+ >+ for (i = 0; i < val_count; i++) { >+ if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) { >+ if ((operation == LDB_MODIFY) && >+ (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) >+ == LDB_FLAG_MOD_DELETE)) { >+ continue; >+ } >+ memcpy(v, >+ msg->elements[i].values, >+ msg->elements[i].num_values); >+ v += msg->elements[i].num_values; >+ } >+ } >+ >+ *el = _el; >+ return LDB_SUCCESS; >+} >+ > /* > * Gets back a single-valued attribute by the rules of the DSDB triggers when > * performing a modify operation. >-- >2.25.1 > > >From f6e298a5b1d416aefb82682a6737991de6dba75b Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:10:44 +1300 >Subject: [PATCH 101/190] CVE-2020-25722 s4/dsdb/samldb: > samldb_get_single_valued_attr() check all values > >using dsdb_get_expected_new_values(). > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 12 ++++++++++-- > 1 file changed, 10 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index b56ddf49537..f1d176820f9 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -174,11 +174,19 @@ static int samldb_get_single_valued_attr(struct ldb_context *ldb, > * attribute. > */ > struct ldb_message_element *el = NULL; >+ int ret; > > *value = NULL; > >- el = dsdb_get_single_valued_attr(ac->msg, attr, >- ac->req->operation); >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ attr, >+ &el, >+ ac->req->operation); >+ >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } > if (el == NULL) { > /* we are not affected */ > return LDB_SUCCESS; >-- >2.25.1 > > >From 2a85a20f79ece5a7f06ca4ffb6160619d1028f90 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Fri, 22 Oct 2021 14:52:49 +1300 >Subject: [PATCH 102/190] CVE-2020-25722 s4/dsdb/samldb: > samldb_sam_accountname_valid_check() check all values > >Using dsdb_get_expected_new_values(). > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 13 +++++++++++-- > 1 file changed, 11 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index f1d176820f9..b5b72968f0f 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -533,8 +533,17 @@ static int samldb_sam_accountname_valid_check(struct samldb_ctx *ac) > bool is_admin; > struct security_token *user_token = NULL; > struct ldb_context *ldb = ldb_module_get_ctx(ac->module); >- struct ldb_message_element *el = dsdb_get_single_valued_attr(ac->msg, "samAccountName", >- ac->req->operation); >+ struct ldb_message_element *el = NULL; >+ >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ "samAccountName", >+ &el, >+ ac->req->operation); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ > if (el == NULL || el->num_values == 0) { > ldb_asprintf_errstring(ldb, > "%08X: samldb: 'samAccountName' can't be deleted/empty!", >-- >2.25.1 > > >From d3d0177a68ea29bc4380bc133cb70b0b4705feaf Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:12:49 +1300 >Subject: [PATCH 103/190] CVE-2020-25722 s4/dsdb/samldb: > samldb_schema_add_handle_linkid() checks all values > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 11 +++++++++-- > 1 file changed, 9 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index b5b72968f0f..98bc69f5489 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -742,8 +742,15 @@ static int samldb_schema_add_handle_linkid(struct samldb_ctx *ac) > schema = dsdb_get_schema(ldb, ac); > schema_dn = ldb_get_schema_basedn(ldb); > >- el = dsdb_get_single_valued_attr(ac->msg, "linkID", >- ac->req->operation); >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ "linkID", >+ &el, >+ ac->req->operation); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ > if (el == NULL) { > return LDB_SUCCESS; > } >-- >2.25.1 > > >From e8e8612f0289f1eef4f14ebfd4a9d56c5a69dad3 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:13:35 +1300 >Subject: [PATCH 104/190] CVE-2020-25722 s4/dsdb/samldb: > samldb_schema_add_handle_mapiid() checks all values > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 11 +++++++++-- > 1 file changed, 9 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 98bc69f5489..08d1e57df10 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -910,8 +910,15 @@ static int samldb_schema_add_handle_mapiid(struct samldb_ctx *ac) > schema = dsdb_get_schema(ldb, ac); > schema_dn = ldb_get_schema_basedn(ldb); > >- el = dsdb_get_single_valued_attr(ac->msg, "mAPIID", >- ac->req->operation); >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ "mAPIID", >+ &el, >+ ac->req->operation); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ > if (el == NULL) { > return LDB_SUCCESS; > } >-- >2.25.1 > > >From facb5d7dae28305fd3bf14662f2da44bdee96e10 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:14:05 +1300 >Subject: [PATCH 105/190] CVE-2020-25722 s4/dsdb/samldb: > samldb_prim_group_change() checks all values > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 11 +++++++++-- > 1 file changed, 9 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 08d1e57df10..819af114e08 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -2124,8 +2124,15 @@ static int samldb_prim_group_change(struct samldb_ctx *ac) > int ret; > const char * const noattrs[] = { NULL }; > >- el = dsdb_get_single_valued_attr(ac->msg, "primaryGroupID", >- ac->req->operation); >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ "primaryGroupID", >+ &el, >+ ac->req->operation); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ > if (el == NULL) { > /* we are not affected */ > return LDB_SUCCESS; >-- >2.25.1 > > >From 5f524fab321581ef8daed65efd9181c52bbfcf0e Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:15:00 +1300 >Subject: [PATCH 106/190] CVE-2020-25722 s4/dsdb/samldb: > samldb_user_account_control_change() checks all values > >There is another call to dsdb_get_expected_new_values() in this function >that we change in the next commit. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 11 +++++++++-- > 1 file changed, 9 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 819af114e08..55a12de586c 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -2812,8 +2812,15 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) > bool old_is_critical = false; > bool new_is_critical = false; > >- el = dsdb_get_single_valued_attr(ac->msg, "userAccountControl", >- ac->req->operation); >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ "userAccountControl", >+ &el, >+ ac->req->operation); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ > if (el == NULL || el->num_values == 0) { > ldb_asprintf_errstring(ldb, > "%08X: samldb: 'userAccountControl' can't be deleted!", >-- >2.25.1 > > >From 27b52295eb78aa6010b6bf8525757790890a3728 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:15:43 +1300 >Subject: [PATCH 107/190] CVE-2020-25722 s4/dsdb/samldb > _user_account_control_change() always add final value > >dsdb_get_single_valued_attr() was finding the last non-delete element for >userAccountControl and changing its value to the computed value. >Unfortunately, the last non-delete element might not be the last element, >and a subsequent delete might remove it. > >Instead we just add a replace on the end. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 9 ++++++--- > 1 file changed, 6 insertions(+), 3 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 55a12de586c..7a60aa3f908 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -3013,9 +3013,12 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) > return ldb_module_oom(ac->module); > } > >- /* Overwrite "userAccountControl" correctly */ >- el = dsdb_get_single_valued_attr(ac->msg, "userAccountControl", >- ac->req->operation); >+ ret = ldb_msg_add_empty(ac->msg, >+ "userAccountControl", >+ LDB_FLAG_MOD_REPLACE, >+ &el); >+ el->values = talloc(ac->msg, struct ldb_val); >+ el->num_values = 1; > el->values[0].data = (uint8_t *) tempstr; > el->values[0].length = strlen(tempstr); > } else { >-- >2.25.1 > > >From 3b6b8e861bd45e7bee92594455c74fe1fea6f998 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:16:34 +1300 >Subject: [PATCH 108/190] CVE-2020-25722 s4/dsdb/samldb: > samldb_pwd_last_set_change() checks all values > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 11 +++++++++-- > 1 file changed, 9 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 7a60aa3f908..06385dfcebe 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -3107,8 +3107,15 @@ static int samldb_pwd_last_set_change(struct samldb_ctx *ac) > NULL > }; > >- el = dsdb_get_single_valued_attr(ac->msg, "pwdLastSet", >- ac->req->operation); >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ "pwdLastSet", >+ &el, >+ ac->req->operation); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ > if (el == NULL || el->num_values == 0) { > ldb_asprintf_errstring(ldb, > "%08X: samldb: 'pwdLastSet' can't be deleted!", >-- >2.25.1 > > >From 84c59072af8cb4d401f14e120718e14be9e2eb90 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:17:31 +1300 >Subject: [PATCH 109/190] CVE-2020-25722 s4/dsdb/samldb: samldb_lockout_time() > checks all values > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 11 +++++++++-- > 1 file changed, 9 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 06385dfcebe..5b9a1d5864c 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -3169,8 +3169,15 @@ static int samldb_lockout_time(struct samldb_ctx *ac) > struct ldb_message *tmp_msg; > int ret; > >- el = dsdb_get_single_valued_attr(ac->msg, "lockoutTime", >- ac->req->operation); >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ "lockoutTime", >+ &el, >+ ac->req->operation); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ > if (el == NULL || el->num_values == 0) { > ldb_asprintf_errstring(ldb, > "%08X: samldb: 'lockoutTime' can't be deleted!", >-- >2.25.1 > > >From 750824c9edae6a7b474e335f41140dc92d21d0ec Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:17:50 +1300 >Subject: [PATCH 110/190] CVE-2020-25722 s4/dsdb/samldb: > samldb_group_type_change() checks all values > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 11 +++++++++-- > 1 file changed, 9 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 5b9a1d5864c..6a5eb473990 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -3226,8 +3226,15 @@ static int samldb_group_type_change(struct samldb_ctx *ac) > struct ldb_result *res; > const char * const attrs[] = { "groupType", NULL }; > >- el = dsdb_get_single_valued_attr(ac->msg, "groupType", >- ac->req->operation); >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ "groupType", >+ &el, >+ ac->req->operation); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ > if (el == NULL) { > /* we are not affected */ > return LDB_SUCCESS; >-- >2.25.1 > > >From 1680010be1c86d63e85f76458ed2ebb5f53e8854 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:18:10 +1300 >Subject: [PATCH 111/190] CVE-2020-25722 s4/dsdb/samldb: > samldb_service_principal_names_change checks values > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 20 ++++++++++++++++---- > 1 file changed, 16 insertions(+), 4 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 6a5eb473990..2b4770fc18f 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -4045,10 +4045,22 @@ static int samldb_service_principal_names_change(struct samldb_ctx *ac) > unsigned int i, j; > int ret; > >- el = dsdb_get_single_valued_attr(ac->msg, "dNSHostName", >- ac->req->operation); >- el2 = dsdb_get_single_valued_attr(ac->msg, "sAMAccountName", >- ac->req->operation); >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ "dNSHostName", >+ &el, >+ ac->req->operation); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ "sAMAccountName", >+ &el2, >+ ac->req->operation); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } > if ((el == NULL) && (el2 == NULL)) { > /* we are not affected */ > return LDB_SUCCESS; >-- >2.25.1 > > >From 109f53c7bb2d8160d7c3ff6617c76b2b61508dba Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:18:21 +1300 >Subject: [PATCH 112/190] CVE-2020-25722 s4/dsdb/samldb: > samldb_fsmo_role_owner_check checks values > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 10 ++++++++-- > 1 file changed, 8 insertions(+), 2 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 2b4770fc18f..620b165abfe 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -4300,9 +4300,15 @@ static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac) > struct ldb_dn *res_dn; > struct ldb_result *res; > int ret; >+ ret = dsdb_get_expected_new_values(ac, >+ ac->msg, >+ "fSMORoleOwner", >+ &el, >+ ac->req->operation); >+ if (ret != LDB_SUCCESS) { >+ return ret; >+ } > >- el = dsdb_get_single_valued_attr(ac->msg, "fSMORoleOwner", >- ac->req->operation); > if (el == NULL) { > /* we are not affected */ > return LDB_SUCCESS; >-- >2.25.1 > > >From d5d6f5928ff91a69c9cf654f72e416c37a403a45 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Thu, 21 Oct 2021 12:52:07 +1300 >Subject: [PATCH 113/190] CVE-2020-25722 s4/dsdb/samldb: > samldb_fsmo_role_owner_check() wants one value > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/samldb.c | 17 ++++++++++++----- > 1 file changed, 12 insertions(+), 5 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c >index 620b165abfe..c1c996d4c34 100644 >--- a/source4/dsdb/samdb/ldb_modules/samldb.c >+++ b/source4/dsdb/samdb/ldb_modules/samldb.c >@@ -4313,6 +4313,9 @@ static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac) > /* we are not affected */ > return LDB_SUCCESS; > } >+ if (el->num_values != 1) { >+ goto choose_error_code; >+ } > > /* Create a temporary message for fetching the "fSMORoleOwner" */ > tmp_msg = ldb_msg_new(ac->msg); >@@ -4329,11 +4332,7 @@ static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac) > if (res_dn == NULL) { > ldb_set_errstring(ldb, > "samldb: 'fSMORoleOwner' attributes have to reference 'nTDSDSA' entries!"); >- if (ac->req->operation == LDB_ADD) { >- return LDB_ERR_CONSTRAINT_VIOLATION; >- } else { >- return LDB_ERR_UNWILLING_TO_PERFORM; >- } >+ goto choose_error_code; > } > > /* Fetched DN has to reference a "nTDSDSA" entry */ >@@ -4353,6 +4352,14 @@ static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac) > talloc_free(res); > > return LDB_SUCCESS; >+ >+choose_error_code: >+ /* this is just how it is */ >+ if (ac->req->operation == LDB_ADD) { >+ return LDB_ERR_CONSTRAINT_VIOLATION; >+ } else { >+ return LDB_ERR_UNWILLING_TO_PERFORM; >+ } > } > > /* >-- >2.25.1 > > >From 2a984cf38218650510cac4e14b05cca23646b128 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:19:42 +1300 >Subject: [PATCH 114/190] CVE-2020-25722 s4/dsdb/pwd_hash: password_hash_bypass > gets all values > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > .../dsdb/samdb/ldb_modules/password_hash.c | 30 ++++++++++++------- > 1 file changed, 20 insertions(+), 10 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c >index 47af8c8733c..c285e2d0457 100644 >--- a/source4/dsdb/samdb/ldb_modules/password_hash.c >+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c >@@ -204,6 +204,7 @@ static int password_hash_bypass(struct ldb_module *module, struct ldb_request *r > struct ldb_message_element *nthe; > struct ldb_message_element *lmhe; > struct ldb_message_element *sce; >+ int ret; > > switch (request->operation) { > case LDB_ADD: >@@ -217,17 +218,26 @@ static int password_hash_bypass(struct ldb_module *module, struct ldb_request *r > } > > /* nobody must touch password histories and 'supplementalCredentials' */ >- nte = dsdb_get_single_valued_attr(msg, "unicodePwd", >- request->operation); >- lme = dsdb_get_single_valued_attr(msg, "dBCSPwd", >- request->operation); >- nthe = dsdb_get_single_valued_attr(msg, "ntPwdHistory", >- request->operation); >- lmhe = dsdb_get_single_valued_attr(msg, "lmPwdHistory", >- request->operation); >- sce = dsdb_get_single_valued_attr(msg, "supplementalCredentials", >- request->operation); > >+#define GET_VALUES(el, attr) do { \ >+ ret = dsdb_get_expected_new_values(request, \ >+ msg, \ >+ attr, \ >+ &el, \ >+ request->operation); \ >+ \ >+ if (ret != LDB_SUCCESS) { \ >+ return ret; \ >+ } \ >+} while(0) >+ >+ GET_VALUES(nte, "unicodePwd"); >+ GET_VALUES(lme, "dBCSPwd"); >+ GET_VALUES(nthe, "ntPwdHistory"); >+ GET_VALUES(lmhe, "lmPwdHistory"); >+ GET_VALUES(sce, "supplementalCredentials"); >+ >+#undef GET_VALUES > #define CHECK_HASH_ELEMENT(e, min, max) do {\ > if (e && e->num_values) { \ > unsigned int _count; \ >-- >2.25.1 > > >From bd04e76f03eecd8463dc5f60345ce68c3d637258 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Wed, 20 Oct 2021 17:20:54 +1300 >Subject: [PATCH 115/190] CVE-2020-25722 s4/dsdb/pwd_hash: rework pwdLastSet > bypass > >This tightens the logic a bit, in that a message with trailing DELETE >elements is no longer accepted when the bypass flag is set. In any case >this is an unlikely scenario as this is an internal flag set by a private >control in pdb_samba_dsdb_replace_by_sam(). > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > .../dsdb/samdb/ldb_modules/password_hash.c | 28 ++++++++++++------- > 1 file changed, 18 insertions(+), 10 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c >index c285e2d0457..348696a895c 100644 >--- a/source4/dsdb/samdb/ldb_modules/password_hash.c >+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c >@@ -2247,23 +2247,31 @@ static int setup_last_set_field(struct setup_password_fields_io *io) > } > > if (io->ac->pwd_last_set_bypass) { >- struct ldb_message_element *el1 = NULL; >- struct ldb_message_element *el2 = NULL; >- >+ struct ldb_message_element *el = NULL; >+ size_t i; >+ size_t count = 0; >+ /* >+ * This is a message from pdb_samba_dsdb_replace_by_sam() >+ * >+ * We want to ensure there is only one pwdLastSet element, and >+ * it isn't deleting. >+ */ > if (msg == NULL) { > return LDB_ERR_CONSTRAINT_VIOLATION; > } > >- el1 = dsdb_get_single_valued_attr(msg, "pwdLastSet", >- io->ac->req->operation); >- if (el1 == NULL) { >- return LDB_ERR_CONSTRAINT_VIOLATION; >+ for (i = 0; i < msg->num_elements; i++) { >+ if (ldb_attr_cmp(msg->elements[i].name, >+ "pwdLastSet") == 0) { >+ count++; >+ el = &msg->elements[i]; >+ } > } >- el2 = ldb_msg_find_element(msg, "pwdLastSet"); >- if (el2 == NULL) { >+ if (count != 1) { > return LDB_ERR_CONSTRAINT_VIOLATION; > } >- if (el1 != el2) { >+ >+ if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) { > return LDB_ERR_CONSTRAINT_VIOLATION; > } > >-- >2.25.1 > > >From 9cf0560d99c5afe1de09b4752094f7abdf4e6fc7 Mon Sep 17 00:00:00 2001 >From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >Date: Thu, 21 Oct 2021 13:49:28 +1300 >Subject: [PATCH 116/190] CVE-2020-25722 s4/dsdb/util: remove unused > dsdb_get_single_valued_attr() > >Nobody uses it now. It never really did what it said it did. Almost >every use was wrong. It was a trap. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876 > >Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> >--- > source4/dsdb/samdb/ldb_modules/util.c | 34 --------------------------- > 1 file changed, 34 deletions(-) > >diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c >index b10177775a8..405febf0b3d 100644 >--- a/source4/dsdb/samdb/ldb_modules/util.c >+++ b/source4/dsdb/samdb/ldb_modules/util.c >@@ -1564,40 +1564,6 @@ int dsdb_get_expected_new_values(TALLOC_CTX *mem_ctx, > return LDB_SUCCESS; > } > >-/* >- * Gets back a single-valued attribute by the rules of the DSDB triggers when >- * performing a modify operation. >- * >- * In order that the constraint checking by the "objectclass_attrs" LDB module >- * does work properly, the change request should remain similar or only be >- * enhanced (no other modifications as deletions, variations). >- */ >-struct ldb_message_element *dsdb_get_single_valued_attr(const struct ldb_message *msg, >- const char *attr_name, >- enum ldb_request_type operation) >-{ >- struct ldb_message_element *el = NULL; >- unsigned int i; >- >- /* We've to walk over all modification entries and consider the last >- * non-delete one which belongs to "attr_name". >- * >- * If "el" is NULL afterwards then that means there was no interesting >- * change entry. */ >- for (i = 0; i < msg->num_elements; i++) { >- if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) { >- if ((operation == LDB_MODIFY) && >- (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) >- == LDB_FLAG_MOD_DELETE)) { >- continue; >- } >- el = &msg->elements[i]; >- } >- } >- >- return el; >-} >- > /* > * This function determines the (last) structural or 88 object class of a passed > * "objectClass" attribute - per MS-ADTS 3.1.1.1.4 this is the last value. >-- >2.25.1 > > >From edc01437a4b4b1f2f6a03015d6fca44f58965c66 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 29 Oct 2021 12:20:49 +1300 >Subject: [PATCH 117/190] CVE-2020-25722 selftest: Adapt ldap.py tests to new > objectClass restrictions > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753 > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > selftest/knownfail.d/ldap | 1 + > source4/dsdb/tests/python/ldap.py | 36 +++++++++++++++++++------------ > 2 files changed, 23 insertions(+), 14 deletions(-) > >diff --git a/selftest/knownfail.d/ldap b/selftest/knownfail.d/ldap >index 0331d3687d4..545dc93db8e 100644 >--- a/selftest/knownfail.d/ldap >+++ b/selftest/knownfail.d/ldap >@@ -1,3 +1,4 @@ > # the attributes too long test returns the wrong error > ^samba4.ldap.python.+test_attribute_ranges_too_long > samba4.ldap.python\(ad_dc_default\).*__main__.BasicTests.test_ldapSearchNoAttributes >+^samba4.ldap.python.+test_objectclasses >diff --git a/source4/dsdb/tests/python/ldap.py b/source4/dsdb/tests/python/ldap.py >index ce02d887792..a50a5f7b8d6 100755 >--- a/source4/dsdb/tests/python/ldap.py >+++ b/source4/dsdb/tests/python/ldap.py >@@ -435,33 +435,41 @@ class BasicTests(samba.tests.TestCase): > (num, _) = e.args > self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > >- # Add a new top-most structural class "inetOrgPerson" and remove it >- # afterwards >+ # Try to add a new top-most structural class "inetOrgPerson" > m = Message() > m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > m["objectClass"] = MessageElement("inetOrgPerson", FLAG_MOD_ADD, > "objectClass") >- ldb.modify(m) >+ try: >+ ldb.modify(m) >+ self.fail() >+ except LdbError as e: >+ (num, _) = e.args >+ self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > >+ # Try to remove the structural class "user" > m = Message() > m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) >- m["objectClass"] = MessageElement("inetOrgPerson", FLAG_MOD_DELETE, >+ m["objectClass"] = MessageElement("user", FLAG_MOD_DELETE, > "objectClass") >- ldb.modify(m) >+ try: >+ ldb.modify(m) >+ self.fail() >+ except LdbError as e: >+ (num, _) = e.args >+ self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > >- # Replace top-most structural class to "inetOrgPerson" and reset it >- # back to "user" >+ # Try to replace top-most structural class to "inetOrgPerson" > m = Message() > m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) > m["objectClass"] = MessageElement("inetOrgPerson", FLAG_MOD_REPLACE, > "objectClass") >- ldb.modify(m) >- >- m = Message() >- m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn) >- m["objectClass"] = MessageElement("user", FLAG_MOD_REPLACE, >- "objectClass") >- ldb.modify(m) >+ try: >+ ldb.modify(m) >+ self.fail() >+ except LdbError as e: >+ (num, _) = e.args >+ self.assertEqual(num, ERR_OBJECT_CLASS_VIOLATION) > > # Add a new auxiliary object class "posixAccount" to "ldaptestuser" > m = Message() >-- >2.25.1 > > >From e4c748d067ac47963c0b79abc0a1fc0ccb59e2cf Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 20:56:10 +1300 >Subject: [PATCH 118/190] tests/krb5: Fix indentation > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 8 ++++---- > 1 file changed, 4 insertions(+), 4 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 5313dbc6045..480e3d8264d 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -667,7 +667,7 @@ class KdcTgsTests(KDCBaseTest): > creds = self._get_creds(replication_allowed=True, > revealed_to_rodc=True) > existing_rid = self._get_existing_rid(replication_allowed=True, >- revealed_to_rodc=True) >+ revealed_to_rodc=True) > tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid) > self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) > >@@ -675,7 +675,7 @@ class KdcTgsTests(KDCBaseTest): > creds = self._get_creds(replication_allowed=True, > revealed_to_rodc=True) > existing_rid = self._get_existing_rid(replication_allowed=True, >- revealed_to_rodc=True) >+ revealed_to_rodc=True) > tgt = self._get_tgt(creds, renewable=True, from_rodc=True, > new_rid=existing_rid) > self._renew_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >@@ -693,7 +693,7 @@ class KdcTgsTests(KDCBaseTest): > creds = self._get_creds(replication_allowed=True, > revealed_to_rodc=True) > existing_rid = self._get_existing_rid(replication_allowed=True, >- revealed_to_rodc=True) >+ revealed_to_rodc=True) > tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid) > self._s4u2self(tgt, creds, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) > >@@ -701,7 +701,7 @@ class KdcTgsTests(KDCBaseTest): > creds = self._get_creds(replication_allowed=True, > revealed_to_rodc=True) > existing_rid = self._get_existing_rid(replication_allowed=True, >- revealed_to_rodc=True) >+ revealed_to_rodc=True) > tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid) > self._user2user(tgt, creds, > expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >-- >2.25.1 > > >From 76e3384213c204cac55cfb7f59ac1ae0fcc37d87 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 20:33:38 +1300 >Subject: [PATCH 119/190] krb5pac.idl: Add PAC_ATTRIBUTES_INFO PAC buffer type > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > librpc/idl/krb5pac.idl | 14 +++++++++++++- > 1 file changed, 13 insertions(+), 1 deletion(-) > >diff --git a/librpc/idl/krb5pac.idl b/librpc/idl/krb5pac.idl >index ed488dee425..11e227026f6 100644 >--- a/librpc/idl/krb5pac.idl >+++ b/librpc/idl/krb5pac.idl >@@ -111,6 +111,16 @@ interface krb5pac > [switch_is(flags & PAC_UPN_DNS_FLAG_HAS_SAM_NAME_AND_SID)] PAC_UPN_DNS_INFO_EX ex; > } PAC_UPN_DNS_INFO; > >+ typedef [bitmap32bit] bitmap { >+ PAC_ATTRIBUTE_FLAG_PAC_WAS_REQUESTED = 0x00000001, >+ PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY = 0x00000002 >+ } PAC_ATTRIBUTE_INFO_FLAGS; >+ >+ typedef struct { >+ uint32 flags_length; /* length in bits */ >+ PAC_ATTRIBUTE_INFO_FLAGS flags; >+ } PAC_ATTRIBUTES_INFO; >+ > typedef [public] struct { > PAC_LOGON_INFO *info; > } PAC_LOGON_INFO_CTR; >@@ -130,7 +140,8 @@ interface krb5pac > PAC_TYPE_CLIENT_CLAIMS_INFO = 13, > PAC_TYPE_DEVICE_INFO = 14, > PAC_TYPE_DEVICE_CLAIMS_INFO = 15, >- PAC_TYPE_TICKET_CHECKSUM = 16 >+ PAC_TYPE_TICKET_CHECKSUM = 16, >+ PAC_TYPE_ATTRIBUTES_INFO = 17 > } PAC_TYPE; > > typedef struct { >@@ -147,6 +158,7 @@ interface krb5pac > PAC_CONSTRAINED_DELEGATION_CTR constrained_delegation; > [case(PAC_TYPE_UPN_DNS_INFO)] PAC_UPN_DNS_INFO upn_dns_info; > [case(PAC_TYPE_TICKET_CHECKSUM)] PAC_SIGNATURE_DATA ticket_checksum; >+ [case(PAC_TYPE_ATTRIBUTES_INFO)] PAC_ATTRIBUTES_INFO attributes_info; > /* when new PAC info types are added they are supposed to be done > in such a way that they are backwards compatible with existing > servers. This makes it safe to just use a [default] for >-- >2.25.1 > > >From 86205a8a721edea6d7f3a5424279aa9488d0e3f0 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 20:33:49 +1300 >Subject: [PATCH 120/190] krb5pac.idl: Add PAC_REQUESTER_SID PAC buffer type > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > librpc/idl/krb5pac.idl | 8 +++++++- > 1 file changed, 7 insertions(+), 1 deletion(-) > >diff --git a/librpc/idl/krb5pac.idl b/librpc/idl/krb5pac.idl >index 11e227026f6..bbe4a253e3a 100644 >--- a/librpc/idl/krb5pac.idl >+++ b/librpc/idl/krb5pac.idl >@@ -121,6 +121,10 @@ interface krb5pac > PAC_ATTRIBUTE_INFO_FLAGS flags; > } PAC_ATTRIBUTES_INFO; > >+ typedef struct { >+ dom_sid sid; >+ } PAC_REQUESTER_SID; >+ > typedef [public] struct { > PAC_LOGON_INFO *info; > } PAC_LOGON_INFO_CTR; >@@ -141,7 +145,8 @@ interface krb5pac > PAC_TYPE_DEVICE_INFO = 14, > PAC_TYPE_DEVICE_CLAIMS_INFO = 15, > PAC_TYPE_TICKET_CHECKSUM = 16, >- PAC_TYPE_ATTRIBUTES_INFO = 17 >+ PAC_TYPE_ATTRIBUTES_INFO = 17, >+ PAC_TYPE_REQUESTER_SID = 18 > } PAC_TYPE; > > typedef struct { >@@ -159,6 +164,7 @@ interface krb5pac > [case(PAC_TYPE_UPN_DNS_INFO)] PAC_UPN_DNS_INFO upn_dns_info; > [case(PAC_TYPE_TICKET_CHECKSUM)] PAC_SIGNATURE_DATA ticket_checksum; > [case(PAC_TYPE_ATTRIBUTES_INFO)] PAC_ATTRIBUTES_INFO attributes_info; >+ [case(PAC_TYPE_REQUESTER_SID)] PAC_REQUESTER_SID requester_sid; > /* when new PAC info types are added they are supposed to be done > in such a way that they are backwards compatible with existing > servers. This makes it safe to just use a [default] for >-- >2.25.1 > > >From 88dc9e4ed17f6927a701a8d6b6d776aa01d910e3 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 20:44:45 +1300 >Subject: [PATCH 121/190] tests/krb5: Provide expected parameters for both > AS-REQs in get_tgt() > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 9 +++++++-- > 1 file changed, 7 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index 9be6cbab30b..6d6dcc21607 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -1389,6 +1389,8 @@ class KDCBaseTest(RawKerberosTest): > ticket_decryption_key = ( > self.TicketDecryptionKey_from_creds(krbtgt_creds)) > >+ expected_etypes = krbtgt_creds.tgs_supported_enctypes >+ > if kdc_options is None: > kdc_options = ('forwardable,' > 'renewable,' >@@ -1415,6 +1417,7 @@ class KDCBaseTest(RawKerberosTest): > expected_salt=salt, > expected_flags=expected_flags, > unexpected_flags=unexpected_flags, >+ expected_supported_etypes=expected_etypes, > etypes=etype, > padata=None, > kdc_options=kdc_options, >@@ -1422,6 +1425,7 @@ class KDCBaseTest(RawKerberosTest): > ticket_decryption_key=ticket_decryption_key, > pac_request=pac_request, > pac_options=pac_options, >+ expect_pac=expect_pac, > to_rodc=to_rodc) > self.check_pre_authentication(rep) > >@@ -1440,8 +1444,6 @@ class KDCBaseTest(RawKerberosTest): > expected_sname = self.PrincipalName_create( > name_type=NT_SRV_INST, names=['krbtgt', realm.upper()]) > >- expected_etypes = krbtgt_creds.tgs_supported_enctypes >- > rep, kdc_exchange_dict = self._test_as_exchange( > cname=cname, > realm=realm, >@@ -1453,6 +1455,9 @@ class KDCBaseTest(RawKerberosTest): > expected_cname=cname, > expected_srealm=expected_realm, > expected_sname=expected_sname, >+ expected_account_name=expected_account_name, >+ expected_upn_name=expected_upn_name, >+ expected_sid=expected_sid, > expected_salt=salt, > expected_flags=expected_flags, > unexpected_flags=unexpected_flags, >-- >2.25.1 > > >From 8851f3c64eba001633825c92cf5c099b4d549fca Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 20:47:53 +1300 >Subject: [PATCH 122/190] tests/krb5: Allow update_pac_checksums=True if the > PAC is not present > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/raw_testcase.py | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > >diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py >index 18ee8738eaa..39ca4a69e1c 100644 >--- a/python/samba/tests/krb5/raw_testcase.py >+++ b/python/samba/tests/krb5/raw_testcase.py >@@ -3293,7 +3293,7 @@ class RawKerberosTest(TestCaseInTempDir): > self.assertFalse(checksum_keys) > self.assertFalse(include_checksums) > >- expect_pac = update_pac_checksums or modify_pac_fn is not None >+ expect_pac = modify_pac_fn is not None > > key = ticket.decryption_key > >-- >2.25.1 > > >From 7d4becdfc8abfc66a35135138a292e53b82d421c Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 20:51:34 +1300 >Subject: [PATCH 123/190] tests/krb5: Don't expect a kvno for user-to-user > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/raw_testcase.py | 16 +++++++++++++--- > 1 file changed, 13 insertions(+), 3 deletions(-) > >diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py >index 39ca4a69e1c..f39e57c8189 100644 >--- a/python/samba/tests/krb5/raw_testcase.py >+++ b/python/samba/tests/krb5/raw_testcase.py >@@ -2225,9 +2225,19 @@ class RawKerberosTest(TestCaseInTempDir): > self.assertIsNotNone(ticket_encpart) > if ticket_encpart is not None: # Never None, but gives indentation > self.assertElementPresent(ticket_encpart, 'etype') >- # 'unspecified' means present, with any value != 0 >- self.assertElementKVNO(ticket_encpart, 'kvno', >- self.unspecified_kvno) >+ >+ kdc_options = kdc_exchange_dict['kdc_options'] >+ pos = len(tuple(krb5_asn1.KDCOptions('enc-tkt-in-skey'))) - 1 >+ expect_kvno = (pos >= len(kdc_options) >+ or kdc_options[pos] != '1') >+ if expect_kvno: >+ # 'unspecified' means present, with any value != 0 >+ self.assertElementKVNO(ticket_encpart, 'kvno', >+ self.unspecified_kvno) >+ else: >+ # For user-to-user, don't expect a kvno. >+ self.assertElementMissing(ticket_encpart, 'kvno') >+ > self.assertElementPresent(ticket_encpart, 'cipher') > ticket_cipher = self.getElementValue(ticket_encpart, 'cipher') > self.assertElementPresent(rep, 'enc-part') >-- >2.25.1 > > >From 1f2ba1e8cf540e79c0866c3c3a1d3802dcc5579e Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 20:51:46 +1300 >Subject: [PATCH 124/190] tests/krb5: Expect 'renew-till' element when renewing > a TGT > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/raw_testcase.py | 8 ++++++-- > 1 file changed, 6 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py >index f39e57c8189..79fe9ec4620 100644 >--- a/python/samba/tests/krb5/raw_testcase.py >+++ b/python/samba/tests/krb5/raw_testcase.py >@@ -2369,6 +2369,10 @@ class RawKerberosTest(TestCaseInTempDir): > renewable_pos = len(tuple(krb5_asn1.KDCOptions('renewable'))) - 1 > renewable = (renewable_pos < len(kdc_options) > and kdc_options[renewable_pos] == '1') >+ renew_pos = len(tuple(krb5_asn1.KDCOptions('renew'))) - 1 >+ renew = (renew_pos < len(kdc_options) >+ and kdc_options[renew_pos] == '1') >+ expect_renew_till = renewable or renew > > expected_crealm = kdc_exchange_dict['expected_crealm'] > expected_cname = kdc_exchange_dict['expected_cname'] >@@ -2425,7 +2429,7 @@ class RawKerberosTest(TestCaseInTempDir): > if self.strict_checking: > self.assertElementPresent(ticket_private, 'starttime') > self.assertElementPresent(ticket_private, 'endtime') >- if renewable: >+ if expect_renew_till: > if self.strict_checking: > self.assertElementPresent(ticket_private, 'renew-till') > else: >@@ -2461,7 +2465,7 @@ class RawKerberosTest(TestCaseInTempDir): > if self.strict_checking: > self.assertElementPresent(encpart_private, 'starttime') > self.assertElementPresent(encpart_private, 'endtime') >- if renewable: >+ if expect_renew_till: > if self.strict_checking: > self.assertElementPresent(encpart_private, 'renew-till') > else: >-- >2.25.1 > > >From a22e7ce26611ae562564820fa9bf13a180adf31b Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 21:05:08 +1300 >Subject: [PATCH 125/190] tests/krb5: Return ticket from _tgs_req() > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 18 ++++++++++++------ > 1 file changed, 12 insertions(+), 6 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 480e3d8264d..11bf38766ae 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -1302,12 +1302,18 @@ class KdcTgsTests(KDCBaseTest): > expect_edata=False, > expect_claims=expect_claims) > >- self._generic_kdc_exchange(kdc_exchange_dict, >- cname=None, >- realm=srealm, >- sname=sname, >- etypes=etypes, >- additional_tickets=additional_tickets) >+ rep = self._generic_kdc_exchange(kdc_exchange_dict, >+ cname=None, >+ realm=srealm, >+ sname=sname, >+ etypes=etypes, >+ additional_tickets=additional_tickets) >+ if expected_error: >+ self.check_error_rep(rep, expected_error) >+ return None >+ else: >+ self.check_reply(rep, KRB_TGS_REP) >+ return kdc_exchange_dict['rep_ticket_creds'] > > > if __name__ == "__main__": >-- >2.25.1 > > >From 273565862fad03b0d6c448cbb257364632c7aede Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 21:14:45 +1300 >Subject: [PATCH 126/190] tests/krb5: Use correct credentials for user-to-user > tests > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 9 ++++----- > selftest/knownfail_heimdal_kdc | 1 - > selftest/knownfail_mit_kdc | 1 - > 3 files changed, 4 insertions(+), 7 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 11bf38766ae..2787185f04a 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -949,7 +949,7 @@ class KdcTgsTests(KDCBaseTest): > creds = self._get_creds() > tgt = self._get_tgt(creds) > >- user_name = self._get_mach_creds().get_username() >+ user_name = creds.get_username() > sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, > names=['host', user_name]) > >@@ -960,18 +960,17 @@ class KdcTgsTests(KDCBaseTest): > creds = self._get_creds() > tgt = self._get_tgt(creds) > >- user_name = self._get_mach_creds().get_username() >+ user_name = creds.get_username() > sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, > names=[user_name]) > >- self._user2user(tgt, creds, sname=sname, >- expected_error=KDC_ERR_BADMATCH) >+ self._user2user(tgt, creds, sname=sname, expected_error=0) > > def test_user2user_wrong_sname(self): > creds = self._get_creds() > tgt = self._get_tgt(creds) > >- other_creds = self.get_service_creds() >+ other_creds = self._get_mach_creds() > user_name = other_creds.get_username() > sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, > names=[user_name]) >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 342d69a6a03..90632f1e4b9 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -161,7 +161,6 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_user > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_host >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_no_host > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_non_existent_sname > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_req >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index ead0902b2d4..97269987d01 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -419,7 +419,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_upn_user > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_user > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_no_host > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_req > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_allowed_denied >-- >2.25.1 > > >From dee9e9e24d1ced98d97dd5ad2678cc0b3698f9ec Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 21:15:53 +1300 >Subject: [PATCH 127/190] tests/krb5: Adjust PAC tests to prepare for new > PAC_ATTRIBUTES_INFO buffer > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 10 +++++----- > selftest/knownfail_heimdal_kdc | 1 - > selftest/knownfail_mit_kdc | 1 - > 3 files changed, 5 insertions(+), 7 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 2787185f04a..10a146a5e59 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -324,10 +324,10 @@ class KdcTgsTests(KDCBaseTest): > self.assertIsNotNone(pac) > > ticket = self._make_tgs_request(client_creds, service_creds, tgt, >- pac_request=False) >+ pac_request=False, expect_pac=False) > >- pac = self.get_ticket_pac(ticket) >- self.assertIsNotNone(pac) >+ pac = self.get_ticket_pac(ticket, expect_pac=False) >+ self.assertIsNone(pac) > > def test_client_no_auth_data_required(self): > client_creds = self.get_cached_creds( >@@ -351,13 +351,13 @@ class KdcTgsTests(KDCBaseTest): > opts={'no_auth_data_required': True}) > service_creds = self.get_service_creds() > >- tgt = self.get_tgt(client_creds, pac_request=False) >+ tgt = self.get_tgt(client_creds) > > pac = self.get_ticket_pac(tgt) > self.assertIsNotNone(pac) > > ticket = self._make_tgs_request(client_creds, service_creds, tgt, >- pac_request=False) >+ pac_request=False, expect_pac=True) > > pac = self.get_ticket_pac(ticket) > self.assertIsNotNone(pac) >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 90632f1e4b9..1d344298037 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -103,7 +103,6 @@ > # > # KDC TGS PAC tests > # >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_client_no_auth_data_required > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_service_no_auth_data_required > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_client_no_auth_data_required >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index 97269987d01..e6d6af30d7a 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -258,7 +258,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > # > # KDC TGS PAC tests > # >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_client_no_auth_data_required\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_service_no_auth_data_required\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_client_no_auth_data_required\(ad_dc\) >-- >2.25.1 > > >From dc103d98416d12444bbaf445af0e8ee551006365 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 21:20:51 +1300 >Subject: [PATCH 128/190] tests/krb5: Adjust expected error codes for > user-to-user tests > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 4 +++- > 1 file changed, 3 insertions(+), 1 deletion(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 10a146a5e59..3a60b9cafb9 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -40,6 +40,7 @@ from samba.tests.krb5.rfc4120_constants import ( > KDC_ERR_BADMATCH, > KDC_ERR_BADOPTION, > KDC_ERR_CLIENT_NAME_MISMATCH, >+ KDC_ERR_MODIFIED, > KDC_ERR_POLICY, > KDC_ERR_S_PRINCIPAL_UNKNOWN, > KDC_ERR_TGT_REVOKED, >@@ -996,7 +997,8 @@ class KdcTgsTests(KDCBaseTest): > service_creds = self.get_service_creds() > service_ticket = self.get_service_ticket(tgt, service_creds) > >- self._user2user(service_ticket, creds, expected_error=KDC_ERR_POLICY) >+ self._user2user(service_ticket, creds, >+ expected_error=(KDC_ERR_MODIFIED, KDC_ERR_POLICY)) > > def _get_tgt(self, > client_creds, >-- >2.25.1 > > >From b21b8c7a5983b8655c53ff337f2aab7a0fb3d976 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 21:08:34 +1300 >Subject: [PATCH 129/190] tests/krb5: tests/krb5: Adjust expected error code > for S4U2Self no-PAC tests > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 32 ++++++++++++++++-------- > 1 file changed, 22 insertions(+), 10 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 3a60b9cafb9..3fd6fc1a7b1 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -23,7 +23,7 @@ import os > import ldb > > >-from samba import dsdb >+from samba import dsdb, ntstatus > > from samba.dcerpc import krb5pac > >@@ -40,6 +40,7 @@ from samba.tests.krb5.rfc4120_constants import ( > KDC_ERR_BADMATCH, > KDC_ERR_BADOPTION, > KDC_ERR_CLIENT_NAME_MISMATCH, >+ KDC_ERR_GENERIC, > KDC_ERR_MODIFIED, > KDC_ERR_POLICY, > KDC_ERR_S_PRINCIPAL_UNKNOWN, >@@ -528,7 +529,10 @@ class KdcTgsTests(KDCBaseTest): > def test_s4u2self_no_pac(self): > creds = self._get_creds() > tgt = self._get_tgt(creds, remove_pac=True) >- self._s4u2self(tgt, creds, expected_error=KDC_ERR_BADOPTION) >+ self._s4u2self(tgt, creds, >+ expected_error=(KDC_ERR_GENERIC, KDC_ERR_BADOPTION), >+ expected_status=ntstatus.NT_STATUS_INVALID_PARAMETER, >+ expect_edata=True) > > def test_user2user_no_pac(self): > creds = self._get_creds() >@@ -556,7 +560,10 @@ class KdcTgsTests(KDCBaseTest): > def test_s4u2self_authdata_no_pac(self): > creds = self._get_creds() > tgt = self._get_tgt(creds, remove_pac=True, allow_empty_authdata=True) >- self._s4u2self(tgt, creds, expected_error=KDC_ERR_BADOPTION) >+ self._s4u2self(tgt, creds, >+ expected_error=(KDC_ERR_GENERIC, KDC_ERR_BADOPTION), >+ expected_status=ntstatus.NT_STATUS_INVALID_PARAMETER, >+ expect_edata=True) > > def test_user2user_authdata_no_pac(self): > creds = self._get_creds() >@@ -1210,7 +1217,8 @@ class KdcTgsTests(KDCBaseTest): > self._tgs_req(tgt, expected_error, krbtgt_creds, > kdc_options=kdc_options) > >- def _s4u2self(self, tgt, tgt_creds, expected_error): >+ def _s4u2self(self, tgt, tgt_creds, expected_error, >+ expect_edata=False, expected_status=None): > user_creds = self._get_mach_creds() > > user_name = user_creds.get_username() >@@ -1229,10 +1237,11 @@ class KdcTgsTests(KDCBaseTest): > > return [padata], req_body > >- self._tgs_req(tgt, expected_error, tgt_creds, >- expected_cname=user_cname, >- generate_padata_fn=generate_s4u2self_padata, >- expect_claims=False) >+ return self._tgs_req(tgt, expected_error, tgt_creds, >+ expected_cname=user_cname, >+ generate_padata_fn=generate_s4u2self_padata, >+ expect_claims=False, expect_edata=expect_edata, >+ expected_status=expected_status) > > def _user2user(self, tgt, tgt_creds, expected_error, sname=None): > user_creds = self._get_mach_creds() >@@ -1250,7 +1259,9 @@ class KdcTgsTests(KDCBaseTest): > additional_ticket=None, > generate_padata_fn=None, > sname=None, >- expect_claims=True): >+ expect_claims=True, >+ expect_edata=False, >+ expected_status=None): > srealm = target_creds.get_realm() > > if sname is None: >@@ -1297,10 +1308,11 @@ class KdcTgsTests(KDCBaseTest): > check_rep_fn=check_rep_fn, > check_kdc_private_fn=self.generic_check_kdc_private, > expected_error_mode=expected_error, >+ expected_status=expected_status, > tgt=tgt, > authenticator_subkey=subkey, > kdc_options=kdc_options, >- expect_edata=False, >+ expect_edata=expect_edata, > expect_claims=expect_claims) > > rep = self._generic_kdc_exchange(kdc_exchange_dict, >-- >2.25.1 > > >From 35c41d222a55e63de82b3fbff03daea031548662 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 21:12:12 +1300 >Subject: [PATCH 130/190] tests/krb5: Extend _get_tgt() method to allow more > modifications to tickets > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 55 ++++++++++++++++-------- > 1 file changed, 37 insertions(+), 18 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 3fd6fc1a7b1..4cb32c96250 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -25,7 +25,7 @@ import ldb > > from samba import dsdb, ntstatus > >-from samba.dcerpc import krb5pac >+from samba.dcerpc import krb5pac, security > > sys.path.insert(0, "bin/python") > os.environ["PYTHONUNBUFFERED"] = "1" >@@ -1014,7 +1014,11 @@ class KdcTgsTests(KDCBaseTest): > from_rodc=False, > new_rid=None, > remove_pac=False, >- allow_empty_authdata=False): >+ allow_empty_authdata=False, >+ can_modify_logon_info=True, >+ can_modify_requester_sid=True, >+ remove_pac_attrs=False, >+ remove_requester_sid=False): > self.assertFalse(renewable and invalid) > > if remove_pac: >@@ -1027,19 +1031,38 @@ class KdcTgsTests(KDCBaseTest): > else: > krbtgt_creds = self.get_krbtgt_creds() > >- if new_rid is not None: >+ if new_rid is not None or remove_requester_sid or remove_pac_attrs: > def change_sid_fn(pac): >- for pac_buffer in pac.buffers: >+ pac_buffers = pac.buffers >+ for pac_buffer in pac_buffers: > if pac_buffer.type == krb5pac.PAC_TYPE_LOGON_INFO: >- logon_info = pac_buffer.info.info >+ if new_rid is not None and can_modify_logon_info: >+ logon_info = pac_buffer.info.info > >- logon_info.info3.base.rid = new_rid >+ logon_info.info3.base.rid = new_rid >+ elif pac_buffer.type == krb5pac.PAC_TYPE_REQUESTER_SID: >+ if remove_requester_sid: >+ pac.num_buffers -= 1 >+ pac_buffers.remove(pac_buffer) >+ elif new_rid is not None and can_modify_requester_sid: >+ requester_sid = pac_buffer.info > >- return pac >+ samdb = self.get_samdb() >+ domain_sid = samdb.get_domain_sid() >+ >+ new_sid = f'{domain_sid}-{new_rid}' >+ >+ requester_sid.sid = security.dom_sid(new_sid) >+ elif pac_buffer.type == krb5pac.PAC_TYPE_ATTRIBUTES_INFO: >+ if remove_pac_attrs: >+ pac.num_buffers -= 1 >+ pac_buffers.remove(pac_buffer) >+ >+ pac.buffers = pac_buffers > >- modify_pac_fn = change_sid_fn >+ return pac > else: >- modify_pac_fn = None >+ change_sid_fn = None > > krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds) > >@@ -1051,7 +1074,7 @@ class KdcTgsTests(KDCBaseTest): > } > > if renewable: >- def set_renewable(enc_part): >+ def flags_modify_fn(enc_part): > # Set the renewable flag. > renewable_flag = krb5_asn1.TicketFlags('renewable') > pos = len(tuple(renewable_flag)) - 1 >@@ -1067,10 +1090,8 @@ class KdcTgsTests(KDCBaseTest): > enc_part['renew-till'] = renew_till > > return enc_part >- >- modify_fn = set_renewable > elif invalid: >- def set_invalid(enc_part): >+ def flags_modify_fn(enc_part): > # Set the invalid flag. > invalid_flag = krb5_asn1.TicketFlags('invalid') > pos = len(tuple(invalid_flag)) - 1 >@@ -1086,16 +1107,14 @@ class KdcTgsTests(KDCBaseTest): > enc_part['starttime'] = past_time > > return enc_part >- >- modify_fn = set_invalid > else: >- modify_fn = None >+ flags_modify_fn = None > > return self.modified_ticket( > tgt, > new_ticket_key=krbtgt_key, >- modify_fn=modify_fn, >- modify_pac_fn=modify_pac_fn, >+ modify_fn=flags_modify_fn, >+ modify_pac_fn=change_sid_fn, > exclude_pac=remove_pac, > allow_empty_authdata=allow_empty_authdata, > update_pac_checksums=not remove_pac, >-- >2.25.1 > > >From 4ae8f46efde896a97afcf785f7d32bc0e30eb414 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Wed, 27 Oct 2021 10:25:08 +1300 >Subject: [PATCH 131/190] tests/krb5: Add _modify_tgt() method for modifying > already obtained tickets > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 62 +++++++++++++++++++++++- > 1 file changed, 60 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 4cb32c96250..52a347b9ed4 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -1026,6 +1026,33 @@ class KdcTgsTests(KDCBaseTest): > > tgt = self.get_tgt(client_creds) > >+ return self._modify_tgt( >+ tgt=tgt, >+ renewable=renewable, >+ invalid=invalid, >+ from_rodc=from_rodc, >+ new_rid=new_rid, >+ remove_pac=remove_pac, >+ allow_empty_authdata=allow_empty_authdata, >+ can_modify_logon_info=can_modify_logon_info, >+ can_modify_requester_sid=can_modify_requester_sid, >+ remove_pac_attrs=remove_pac_attrs, >+ remove_requester_sid=remove_requester_sid) >+ >+ def _modify_tgt(self, >+ tgt, >+ renewable=False, >+ invalid=False, >+ from_rodc=False, >+ new_rid=None, >+ remove_pac=False, >+ allow_empty_authdata=False, >+ cname=None, >+ crealm=None, >+ can_modify_logon_info=True, >+ can_modify_requester_sid=True, >+ remove_pac_attrs=False, >+ remove_requester_sid=False): > if from_rodc: > krbtgt_creds = self.get_mock_rodc_krbtgt_creds() > else: >@@ -1110,11 +1137,42 @@ class KdcTgsTests(KDCBaseTest): > else: > flags_modify_fn = None > >+ if cname is not None or crealm is not None: >+ def modify_fn(enc_part): >+ if flags_modify_fn is not None: >+ enc_part = flags_modify_fn(enc_part) >+ >+ if cname is not None: >+ enc_part['cname'] = cname >+ >+ if crealm is not None: >+ enc_part['crealm'] = crealm >+ >+ return enc_part >+ else: >+ modify_fn = flags_modify_fn >+ >+ if cname is not None: >+ def modify_pac_fn(pac): >+ if change_sid_fn is not None: >+ pac = change_sid_fn(pac) >+ >+ for pac_buffer in pac.buffers: >+ if pac_buffer.type == krb5pac.PAC_TYPE_LOGON_NAME: >+ logon_info = pac_buffer.info >+ >+ logon_info.account_name = ( >+ cname['name-string'][0].decode('utf-8')) >+ >+ return pac >+ else: >+ modify_pac_fn = change_sid_fn >+ > return self.modified_ticket( > tgt, > new_ticket_key=krbtgt_key, >- modify_fn=flags_modify_fn, >- modify_pac_fn=change_sid_fn, >+ modify_fn=modify_fn, >+ modify_pac_fn=modify_pac_fn, > exclude_pac=remove_pac, > allow_empty_authdata=allow_empty_authdata, > update_pac_checksums=not remove_pac, >-- >2.25.1 > > >From 6a99bcf0c498d4693bdd59ab7a66abea0f34dba9 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 20:50:09 +1300 >Subject: [PATCH 132/190] tests/krb5: Add testing for PAC_TYPE_ATTRIBUTES_INFO > PAC buffer > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 8 ++++- > python/samba/tests/krb5/raw_testcase.py | 37 ++++++++++++++++++++++++ > 2 files changed, 44 insertions(+), 1 deletion(-) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index 6d6dcc21607..dc1ba629b41 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -1360,7 +1360,9 @@ class KDCBaseTest(RawKerberosTest): > expected_flags=None, unexpected_flags=None, > expected_account_name=None, expected_upn_name=None, > expected_sid=None, >- pac_request=True, expect_pac=True, fresh=False): >+ pac_request=True, expect_pac=True, >+ expect_pac_attrs=None, expect_pac_attrs_pac_request=None, >+ fresh=False): > user_name = creds.get_username() > cache_key = (user_name, to_rodc, kdc_options, pac_request) > >@@ -1426,6 +1428,8 @@ class KDCBaseTest(RawKerberosTest): > pac_request=pac_request, > pac_options=pac_options, > expect_pac=expect_pac, >+ expect_pac_attrs=expect_pac_attrs, >+ expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, > to_rodc=to_rodc) > self.check_pre_authentication(rep) > >@@ -1470,6 +1474,8 @@ class KDCBaseTest(RawKerberosTest): > pac_request=pac_request, > pac_options=pac_options, > expect_pac=expect_pac, >+ expect_pac_attrs=expect_pac_attrs, >+ expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, > to_rodc=to_rodc) > self.check_as_reply(rep) > >diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py >index 79fe9ec4620..d63366318be 100644 >--- a/python/samba/tests/krb5/raw_testcase.py >+++ b/python/samba/tests/krb5/raw_testcase.py >@@ -2021,6 +2021,8 @@ class RawKerberosTest(TestCaseInTempDir): > expect_pac=True, > expect_claims=True, > expect_upn_dns_info_ex=None, >+ expect_pac_attrs=None, >+ expect_pac_attrs_pac_request=None, > to_rodc=False): > if expected_error_mode == 0: > expected_error_mode = () >@@ -2074,6 +2076,8 @@ class RawKerberosTest(TestCaseInTempDir): > 'expect_pac': expect_pac, > 'expect_claims': expect_claims, > 'expect_upn_dns_info_ex': expect_upn_dns_info_ex, >+ 'expect_pac_attrs': expect_pac_attrs, >+ 'expect_pac_attrs_pac_request': expect_pac_attrs_pac_request, > 'to_rodc': to_rodc > } > if callback_dict is None: >@@ -2122,6 +2126,8 @@ class RawKerberosTest(TestCaseInTempDir): > expect_pac=True, > expect_claims=True, > expect_upn_dns_info_ex=None, >+ expect_pac_attrs=None, >+ expect_pac_attrs_pac_request=None, > expected_proxy_target=None, > expected_transited_services=None, > to_rodc=False): >@@ -2176,6 +2182,8 @@ class RawKerberosTest(TestCaseInTempDir): > 'expect_pac': expect_pac, > 'expect_claims': expect_claims, > 'expect_upn_dns_info_ex': expect_upn_dns_info_ex, >+ 'expect_pac_attrs': expect_pac_attrs, >+ 'expect_pac_attrs_pac_request': expect_pac_attrs_pac_request, > 'expected_proxy_target': expected_proxy_target, > 'expected_transited_services': expected_transited_services, > 'to_rodc': to_rodc >@@ -2596,6 +2604,12 @@ class RawKerberosTest(TestCaseInTempDir): > if not self.tkt_sig_support: > require_strict.add(krb5pac.PAC_TYPE_TICKET_CHECKSUM) > >+ expect_pac_attrs = kdc_exchange_dict['expect_pac_attrs'] >+ if expect_pac_attrs: >+ expected_types.append(krb5pac.PAC_TYPE_ATTRIBUTES_INFO) >+ elif expect_pac_attrs is None: >+ require_strict.add(krb5pac.PAC_TYPE_ATTRIBUTES_INFO) >+ > buffer_types = [pac_buffer.type > for pac_buffer in pac.buffers] > self.assertSequenceElementsEqual( >@@ -2671,6 +2685,25 @@ class RawKerberosTest(TestCaseInTempDir): > self.assertEqual(expected_sid, > str(upn_dns_info_ex.objectsid)) > >+ elif (pac_buffer.type == krb5pac.PAC_TYPE_ATTRIBUTES_INFO >+ and expect_pac_attrs): >+ attr_info = pac_buffer.info >+ >+ self.assertEqual(2, attr_info.flags_length) >+ >+ flags = attr_info.flags >+ >+ requested_pac = bool(flags & 1) >+ given_pac = bool(flags & 2) >+ >+ expect_pac_attrs_pac_request = kdc_exchange_dict[ >+ 'expect_pac_attrs_pac_request'] >+ >+ self.assertEqual(expect_pac_attrs_pac_request is True, >+ requested_pac) >+ self.assertEqual(expect_pac_attrs_pac_request is None, >+ given_pac) >+ > def generic_check_kdc_error(self, > kdc_exchange_dict, > callback_dict, >@@ -3663,6 +3696,8 @@ class RawKerberosTest(TestCaseInTempDir): > pac_request=None, > pac_options=None, > expect_pac=True, >+ expect_pac_attrs=None, >+ expect_pac_attrs_pac_request=None, > to_rodc=False): > > def _generate_padata_copy(_kdc_exchange_dict, >@@ -3706,6 +3741,8 @@ class RawKerberosTest(TestCaseInTempDir): > pac_request=pac_request, > pac_options=pac_options, > expect_pac=expect_pac, >+ expect_pac_attrs=expect_pac_attrs, >+ expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, > to_rodc=to_rodc) > > rep = self._generic_kdc_exchange(kdc_exchange_dict, >-- >2.25.1 > > >From 5a1048ede79b7b30ee1d953a8d424b6e07e4448c Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 20:51:13 +1300 >Subject: [PATCH 133/190] tests/krb5: Add testing for PAC_TYPE_REQUESTER_SID > PAC buffer > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 3 +++ > python/samba/tests/krb5/raw_testcase.py | 19 +++++++++++++++++++ > 2 files changed, 22 insertions(+) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index dc1ba629b41..61eeb2333f9 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -1362,6 +1362,7 @@ class KDCBaseTest(RawKerberosTest): > expected_sid=None, > pac_request=True, expect_pac=True, > expect_pac_attrs=None, expect_pac_attrs_pac_request=None, >+ expect_requester_sid=None, > fresh=False): > user_name = creds.get_username() > cache_key = (user_name, to_rodc, kdc_options, pac_request) >@@ -1430,6 +1431,7 @@ class KDCBaseTest(RawKerberosTest): > expect_pac=expect_pac, > expect_pac_attrs=expect_pac_attrs, > expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, >+ expect_requester_sid=expect_requester_sid, > to_rodc=to_rodc) > self.check_pre_authentication(rep) > >@@ -1476,6 +1478,7 @@ class KDCBaseTest(RawKerberosTest): > expect_pac=expect_pac, > expect_pac_attrs=expect_pac_attrs, > expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, >+ expect_requester_sid=expect_requester_sid, > to_rodc=to_rodc) > self.check_as_reply(rep) > >diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py >index d63366318be..8779d0f7869 100644 >--- a/python/samba/tests/krb5/raw_testcase.py >+++ b/python/samba/tests/krb5/raw_testcase.py >@@ -2023,6 +2023,7 @@ class RawKerberosTest(TestCaseInTempDir): > expect_upn_dns_info_ex=None, > expect_pac_attrs=None, > expect_pac_attrs_pac_request=None, >+ expect_requester_sid=None, > to_rodc=False): > if expected_error_mode == 0: > expected_error_mode = () >@@ -2078,6 +2079,7 @@ class RawKerberosTest(TestCaseInTempDir): > 'expect_upn_dns_info_ex': expect_upn_dns_info_ex, > 'expect_pac_attrs': expect_pac_attrs, > 'expect_pac_attrs_pac_request': expect_pac_attrs_pac_request, >+ 'expect_requester_sid': expect_requester_sid, > 'to_rodc': to_rodc > } > if callback_dict is None: >@@ -2128,6 +2130,7 @@ class RawKerberosTest(TestCaseInTempDir): > expect_upn_dns_info_ex=None, > expect_pac_attrs=None, > expect_pac_attrs_pac_request=None, >+ expect_requester_sid=None, > expected_proxy_target=None, > expected_transited_services=None, > to_rodc=False): >@@ -2184,6 +2187,7 @@ class RawKerberosTest(TestCaseInTempDir): > 'expect_upn_dns_info_ex': expect_upn_dns_info_ex, > 'expect_pac_attrs': expect_pac_attrs, > 'expect_pac_attrs_pac_request': expect_pac_attrs_pac_request, >+ 'expect_requester_sid': expect_requester_sid, > 'expected_proxy_target': expected_proxy_target, > 'expected_transited_services': expected_transited_services, > 'to_rodc': to_rodc >@@ -2610,6 +2614,12 @@ class RawKerberosTest(TestCaseInTempDir): > elif expect_pac_attrs is None: > require_strict.add(krb5pac.PAC_TYPE_ATTRIBUTES_INFO) > >+ expect_requester_sid = kdc_exchange_dict['expect_requester_sid'] >+ if expect_requester_sid: >+ expected_types.append(krb5pac.PAC_TYPE_REQUESTER_SID) >+ elif expect_requester_sid is None: >+ require_strict.add(krb5pac.PAC_TYPE_REQUESTER_SID) >+ > buffer_types = [pac_buffer.type > for pac_buffer in pac.buffers] > self.assertSequenceElementsEqual( >@@ -2704,6 +2714,13 @@ class RawKerberosTest(TestCaseInTempDir): > self.assertEqual(expect_pac_attrs_pac_request is None, > given_pac) > >+ elif (pac_buffer.type == krb5pac.PAC_TYPE_REQUESTER_SID >+ and expect_requester_sid): >+ requester_sid = pac_buffer.info.sid >+ >+ self.assertIsNotNone(expected_sid) >+ self.assertEqual(expected_sid, str(requester_sid)) >+ > def generic_check_kdc_error(self, > kdc_exchange_dict, > callback_dict, >@@ -3698,6 +3715,7 @@ class RawKerberosTest(TestCaseInTempDir): > expect_pac=True, > expect_pac_attrs=None, > expect_pac_attrs_pac_request=None, >+ expect_requester_sid=None, > to_rodc=False): > > def _generate_padata_copy(_kdc_exchange_dict, >@@ -3743,6 +3761,7 @@ class RawKerberosTest(TestCaseInTempDir): > expect_pac=expect_pac, > expect_pac_attrs=expect_pac_attrs, > expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, >+ expect_requester_sid=expect_requester_sid, > to_rodc=to_rodc) > > rep = self._generic_kdc_exchange(kdc_exchange_dict, >-- >2.25.1 > > >From e14acc5aa02b0dbdf74ef69a0f9acf26bd110e10 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 20:47:24 +1300 >Subject: [PATCH 134/190] tests/krb5: Add EXPECT_PAC environment variable to > expect pac from all TGS tickets > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/raw_testcase.py | 25 ++++++++--- > source4/selftest/tests.py | 55 +++++++++++++++++-------- > 2 files changed, 56 insertions(+), 24 deletions(-) > >diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py >index 8779d0f7869..42f2e94f5aa 100644 >--- a/python/samba/tests/krb5/raw_testcase.py >+++ b/python/samba/tests/krb5/raw_testcase.py >@@ -596,6 +596,12 @@ class RawKerberosTest(TestCaseInTempDir): > tkt_sig_support = '0' > cls.tkt_sig_support = bool(int(tkt_sig_support)) > >+ expect_pac = samba.tests.env_get_var_value('EXPECT_PAC', >+ allow_missing=True) >+ if expect_pac is None: >+ expect_pac = '1' >+ cls.expect_pac = bool(int(expect_pac)) >+ > def setUp(self): > super().setUp() > self.do_asn1_print = False >@@ -2417,7 +2423,10 @@ class RawKerberosTest(TestCaseInTempDir): > etype=kcrypto.Enctype.RC4) > krbtgt_keys.append(krbtgt_key_rc4) > >- expect_pac = kdc_exchange_dict['expect_pac'] >+ if self.expect_pac and self.is_tgs(expected_sname): >+ expect_pac = True >+ else: >+ expect_pac = kdc_exchange_dict['expect_pac'] > > ticket_session_key = None > if ticket_private is not None: >@@ -2448,8 +2457,9 @@ class RawKerberosTest(TestCaseInTempDir): > self.assertElementMissing(ticket_private, 'renew-till') > if self.strict_checking: > self.assertElementEqual(ticket_private, 'caddr', []) >- self.assertElementPresent(ticket_private, 'authorization-data', >- expect_empty=not expect_pac) >+ if expect_pac is not None: >+ self.assertElementPresent(ticket_private, 'authorization-data', >+ expect_empty=not expect_pac) > > encpart_session_key = None > if encpart_private is not None: >@@ -2554,11 +2564,14 @@ class RawKerberosTest(TestCaseInTempDir): > > if ticket_private is not None: > pac_data = self.get_ticket_pac(ticket_creds, expect_pac=expect_pac) >- if expect_pac: >- self.check_pac_buffers(pac_data, kdc_exchange_dict) >- else: >+ if expect_pac is True: >+ self.assertIsNotNone(pac_data) >+ elif expect_pac is False: > self.assertIsNone(pac_data) > >+ if pac_data is not None: >+ self.check_pac_buffers(pac_data, kdc_exchange_dict) >+ > expect_ticket_checksum = kdc_exchange_dict['expect_ticket_checksum'] > if expect_ticket_checksum: > self.assertIsNotNone(ticket_decryption_key) >diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py >index deba427f3b9..a0de8ab661c 100755 >--- a/source4/selftest/tests.py >+++ b/source4/selftest/tests.py >@@ -914,30 +914,35 @@ for env in ['fileserver_smb1', 'nt4_member', 'clusteredmember', 'ktest', 'nt4_dc > > have_fast_support = int('SAMBA_USES_MITKDC' in config_hash) > tkt_sig_support = int('SAMBA4_USES_HEIMDAL' in config_hash) >+expect_pac = 0 > planoldpythontestsuite("none", "samba.tests.krb5.kcrypto") > planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.simple_tests", > environ={'SERVICE_USERNAME':'$SERVER', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support}) >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac}) > planoldpythontestsuite("ad_dc_default:local", "samba.tests.krb5.s4u_tests", > environ={'ADMIN_USERNAME':'$USERNAME', > 'ADMIN_PASSWORD':'$PASSWORD', > 'FOR_USER':'$USERNAME', > 'STRICT_CHECKING':'0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support}) >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac}) > planoldpythontestsuite("rodc:local", "samba.tests.krb5.rodc_tests", > environ={'ADMIN_USERNAME':'$USERNAME', > 'ADMIN_PASSWORD':'$PASSWORD', > 'STRICT_CHECKING':'0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support}) >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac}) > > planoldpythontestsuite("ad_dc_default", "samba.tests.dsdb_dns") > > planoldpythontestsuite("fl2008r2dc:local", "samba.tests.krb5.xrealm_tests", > environ={'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support}) >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac}) > > planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ccache", > environ={ >@@ -945,7 +950,8 @@ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ccache", > 'ADMIN_PASSWORD': '$PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ldap", > environ={ >@@ -953,7 +959,8 @@ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ldap", > 'ADMIN_PASSWORD': '$PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > for env in ['ad_dc_default', 'ad_member']: > planoldpythontestsuite(env, "samba.tests.krb5.test_rpc", >@@ -962,7 +969,8 @@ for env in ['ad_dc_default', 'ad_member']: > 'ADMIN_PASSWORD': '$DC_PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > planoldpythontestsuite("ad_dc_smb1", "samba.tests.krb5.test_smb", > environ={ >@@ -970,7 +978,8 @@ planoldpythontestsuite("ad_dc_smb1", "samba.tests.krb5.test_smb", > 'ADMIN_PASSWORD': '$PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > planoldpythontestsuite("ad_member_no_nss_wb:local", > "samba.tests.krb5.test_min_domain_uid", >@@ -1571,7 +1580,8 @@ for env in ["fl2008r2dc", "fl2003dc"]: > 'ADMIN_PASSWORD': '$PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > > planoldpythontestsuite('fl2008r2dc', 'samba.tests.krb5.salt_tests', >@@ -1580,7 +1590,8 @@ planoldpythontestsuite('fl2008r2dc', 'samba.tests.krb5.salt_tests', > 'ADMIN_PASSWORD': '$PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > > for env in ["rodc", "promoted_dc", "fl2000dc", "fl2008r2dc"]: >@@ -1602,7 +1613,8 @@ planpythontestsuite("ad_dc", "samba.tests.krb5.as_canonicalization_tests", > 'ADMIN_USERNAME': '$USERNAME', > 'ADMIN_PASSWORD': '$PASSWORD', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > planpythontestsuite("ad_dc", "samba.tests.krb5.compatability_tests", > environ={ >@@ -1610,11 +1622,13 @@ planpythontestsuite("ad_dc", "samba.tests.krb5.compatability_tests", > 'ADMIN_PASSWORD': '$PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > planpythontestsuite("ad_dc", "samba.tests.krb5.kdc_tests", > environ={'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support}) >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac}) > planpythontestsuite( > "ad_dc", > "samba.tests.krb5.kdc_tgs_tests", >@@ -1623,7 +1637,8 @@ planpythontestsuite( > 'ADMIN_PASSWORD': '$PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > planpythontestsuite( > "ad_dc", >@@ -1633,7 +1648,8 @@ planpythontestsuite( > 'ADMIN_PASSWORD': '$PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > planpythontestsuite( > "ad_dc", >@@ -1643,7 +1659,8 @@ planpythontestsuite( > 'ADMIN_PASSWORD': '$PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > planpythontestsuite( > "ad_dc", >@@ -1653,7 +1670,8 @@ planpythontestsuite( > 'ADMIN_PASSWORD': '$PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > planpythontestsuite( > "ad_dc", >@@ -1663,7 +1681,8 @@ planpythontestsuite( > 'ADMIN_PASSWORD': '$PASSWORD', > 'STRICT_CHECKING': '0', > 'FAST_SUPPORT': have_fast_support, >- 'TKT_SIG_SUPPORT': tkt_sig_support >+ 'TKT_SIG_SUPPORT': tkt_sig_support, >+ 'EXPECT_PAC': expect_pac > }) > > for env in [ >-- >2.25.1 > > >From e677d5a84cf355108eb045ae37efb7e51b1dd7ac Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Wed, 27 Oct 2021 11:18:36 +1300 >Subject: [PATCH 135/190] tests/krb5: Add expected parameters to cache key for > obtaining tickets > >If multiple calls to get_tgt() or get_service_ticket() specify different >expected parameters, we want to perform the request again so that the >checking can be performed, rather than reusing a previously obtained >ticket and potentially skipping checks. > >It should be fine to cache tickets with the same expected parameters, as >tickets that fail to be obtained will not be stored in the cache, so the >checking will happen for every call. > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_base_test.py | 9 +++++++-- > 1 file changed, 7 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py >index 61eeb2333f9..4b4f1486f60 100644 >--- a/python/samba/tests/krb5/kdc_base_test.py >+++ b/python/samba/tests/krb5/kdc_base_test.py >@@ -1294,7 +1294,8 @@ class KDCBaseTest(RawKerberosTest): > if target_name is None: > target_name = target_creds.get_username()[:-1] > cache_key = (user_name, target_name, service, to_rodc, kdc_options, >- pac_request) >+ pac_request, str(expected_flags), str(unexpected_flags), >+ expect_pac) > > if not fresh: > ticket = self.tkt_cache.get(cache_key) >@@ -1365,7 +1366,11 @@ class KDCBaseTest(RawKerberosTest): > expect_requester_sid=None, > fresh=False): > user_name = creds.get_username() >- cache_key = (user_name, to_rodc, kdc_options, pac_request) >+ cache_key = (user_name, to_rodc, kdc_options, pac_request, >+ str(expected_flags), str(unexpected_flags), >+ expected_account_name, expected_upn_name, expected_sid, >+ expect_pac, expect_pac_attrs, >+ expect_pac_attrs_pac_request, expect_requester_sid) > > if not fresh: > tgt = self.tkt_cache.get(cache_key) >-- >2.25.1 > > >From 7f21dac267e4c4756498a83d7099e2aa8b36895e Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 21:02:08 +1300 >Subject: [PATCH 136/190] tests/krb5: Add tests for PAC attributes buffer > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 281 +++++++++++++++++++++-- > selftest/knownfail_heimdal_kdc | 21 ++ > selftest/knownfail_mit_kdc | 22 ++ > 3 files changed, 308 insertions(+), 16 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 52a347b9ed4..40291677819 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -510,6 +510,20 @@ class KdcTgsTests(KDCBaseTest): > tgt = self._get_tgt(creds) > self._user2user(tgt, creds, expected_error=0) > >+ def test_tgs_req_no_pac_attrs(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, remove_pac_attrs=True) >+ >+ self._run_tgs(tgt, expected_error=0, expect_pac=True, >+ expect_pac_attrs=False) >+ >+ def test_tgs_req_from_rodc_no_pac_attrs(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True, remove_pac_attrs=True) >+ self._run_tgs(tgt, expected_error=0, expect_pac=True, >+ expect_pac_attrs=False) >+ > # Test making a request without a PAC. > def test_tgs_no_pac(self): > creds = self._get_creds() >@@ -1007,6 +1021,221 @@ class KdcTgsTests(KDCBaseTest): > self._user2user(service_ticket, creds, > expected_error=(KDC_ERR_MODIFIED, KDC_ERR_POLICY)) > >+ def test_pac_attrs_none(self): >+ creds = self._get_creds() >+ self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=None) >+ >+ def test_pac_attrs_false(self): >+ creds = self._get_creds() >+ self.get_tgt(creds, pac_request=False, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=False) >+ >+ def test_pac_attrs_true(self): >+ creds = self._get_creds() >+ self.get_tgt(creds, pac_request=True, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=True) >+ >+ def test_pac_attrs_renew_none(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=None) >+ tgt = self._modify_tgt(tgt, renewable=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=None) >+ >+ def test_pac_attrs_renew_false(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=False, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=False) >+ tgt = self._modify_tgt(tgt, renewable=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=False) >+ >+ def test_pac_attrs_renew_true(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=True, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=True) >+ tgt = self._modify_tgt(tgt, renewable=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=True) >+ >+ def test_pac_attrs_rodc_renew_none(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=None) >+ tgt = self._modify_tgt(tgt, from_rodc=True, renewable=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=None) >+ >+ def test_pac_attrs_rodc_renew_false(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self.get_tgt(creds, pac_request=False, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=False) >+ tgt = self._modify_tgt(tgt, from_rodc=True, renewable=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=False) >+ >+ def test_pac_attrs_rodc_renew_true(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self.get_tgt(creds, pac_request=True, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=True) >+ tgt = self._modify_tgt(tgt, from_rodc=True, renewable=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=True) >+ >+ def test_pac_attrs_missing_renew_none(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=None) >+ tgt = self._modify_tgt(tgt, renewable=True, >+ remove_pac_attrs=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=False) >+ >+ def test_pac_attrs_missing_renew_false(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=False, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=False) >+ tgt = self._modify_tgt(tgt, renewable=True, >+ remove_pac_attrs=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=False) >+ >+ def test_pac_attrs_missing_renew_true(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=True, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=True) >+ tgt = self._modify_tgt(tgt, renewable=True, >+ remove_pac_attrs=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=False) >+ >+ def test_pac_attrs_missing_rodc_renew_none(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=None) >+ tgt = self._modify_tgt(tgt, from_rodc=True, renewable=True, >+ remove_pac_attrs=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=False) >+ >+ def test_pac_attrs_missing_rodc_renew_false(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self.get_tgt(creds, pac_request=False, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=False) >+ tgt = self._modify_tgt(tgt, from_rodc=True, renewable=True, >+ remove_pac_attrs=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=False) >+ >+ def test_pac_attrs_missing_rodc_renew_true(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self.get_tgt(creds, pac_request=True, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=True) >+ tgt = self._modify_tgt(tgt, from_rodc=True, renewable=True, >+ remove_pac_attrs=True) >+ >+ self._renew_tgt(tgt, expected_error=0, >+ expect_pac=True, >+ expect_pac_attrs=False) >+ >+ def test_tgs_pac_attrs_none(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=None) >+ >+ self._run_tgs(tgt, expected_error=0, expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=None) >+ >+ def test_tgs_pac_attrs_false(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=False, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=False) >+ >+ self._run_tgs(tgt, expected_error=0, expect_pac=False) >+ >+ def test_tgs_pac_attrs_true(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=True, >+ expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=True) >+ >+ self._run_tgs(tgt, expected_error=0, expect_pac=True, >+ expect_pac_attrs=True, >+ expect_pac_attrs_pac_request=True) >+ >+ > def _get_tgt(self, > client_creds, > renewable=False, >@@ -1278,23 +1507,34 @@ class KdcTgsTests(KDCBaseTest): > def _get_non_existent_rid(self): > return (1 << 30) - 1 > >- def _run_tgs(self, tgt, expected_error): >+ def _run_tgs(self, tgt, expected_error, expect_pac=True, >+ expect_pac_attrs=None, expect_pac_attrs_pac_request=None): > target_creds = self.get_service_creds() >- self._tgs_req(tgt, expected_error, target_creds) >- >- def _renew_tgt(self, tgt, expected_error): >+ return self._tgs_req( >+ tgt, expected_error, target_creds, >+ expect_pac=expect_pac, >+ expect_pac_attrs=expect_pac_attrs, >+ expect_pac_attrs_pac_request=expect_pac_attrs_pac_request) >+ >+ def _renew_tgt(self, tgt, expected_error, expect_pac=True, >+ expect_pac_attrs=None, expect_pac_attrs_pac_request=None): > krbtgt_creds = self.get_krbtgt_creds() > kdc_options = str(krb5_asn1.KDCOptions('renew')) >- self._tgs_req(tgt, expected_error, krbtgt_creds, >- kdc_options=kdc_options) >+ return self._tgs_req( >+ tgt, expected_error, krbtgt_creds, >+ kdc_options=kdc_options, >+ expect_pac=expect_pac, >+ expect_pac_attrs=expect_pac_attrs, >+ expect_pac_attrs_pac_request=expect_pac_attrs_pac_request) > >- def _validate_tgt(self, tgt, expected_error): >+ def _validate_tgt(self, tgt, expected_error, expect_pac=True): > krbtgt_creds = self.get_krbtgt_creds() > kdc_options = str(krb5_asn1.KDCOptions('validate')) >- self._tgs_req(tgt, expected_error, krbtgt_creds, >- kdc_options=kdc_options) >+ return self._tgs_req(tgt, expected_error, krbtgt_creds, >+ kdc_options=kdc_options, >+ expect_pac=expect_pac) > >- def _s4u2self(self, tgt, tgt_creds, expected_error, >+ def _s4u2self(self, tgt, tgt_creds, expected_error, expect_pac=True, > expect_edata=False, expected_status=None): > user_creds = self._get_mach_creds() > >@@ -1318,17 +1558,20 @@ class KdcTgsTests(KDCBaseTest): > expected_cname=user_cname, > generate_padata_fn=generate_s4u2self_padata, > expect_claims=False, expect_edata=expect_edata, >- expected_status=expected_status) >+ expected_status=expected_status, >+ expect_pac=expect_pac) > >- def _user2user(self, tgt, tgt_creds, expected_error, sname=None): >+ def _user2user(self, tgt, tgt_creds, expected_error, sname=None, >+ expect_pac=True): > user_creds = self._get_mach_creds() > user_tgt = self.get_tgt(user_creds) > > kdc_options = str(krb5_asn1.KDCOptions('enc-tkt-in-skey')) >- self._tgs_req(user_tgt, expected_error, tgt_creds, >- kdc_options=kdc_options, >- additional_ticket=tgt, >- sname=sname) >+ return self._tgs_req(user_tgt, expected_error, tgt_creds, >+ kdc_options=kdc_options, >+ additional_ticket=tgt, >+ sname=sname, >+ expect_pac=expect_pac) > > def _tgs_req(self, tgt, expected_error, target_creds, > kdc_options='0', >@@ -1337,6 +1580,9 @@ class KdcTgsTests(KDCBaseTest): > generate_padata_fn=None, > sname=None, > expect_claims=True, >+ expect_pac=True, >+ expect_pac_attrs=None, >+ expect_pac_attrs_pac_request=None, > expect_edata=False, > expected_status=None): > srealm = target_creds.get_realm() >@@ -1390,6 +1636,9 @@ class KdcTgsTests(KDCBaseTest): > authenticator_subkey=subkey, > kdc_options=kdc_options, > expect_edata=expect_edata, >+ expect_pac=expect_pac, >+ expect_pac_attrs=expect_pac_attrs, >+ expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, > expect_claims=expect_claims) > > rep = self._generic_kdc_exchange(kdc_exchange_dict, >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 1d344298037..c31a738d482 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -187,3 +187,24 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_nonexisting >+# >+# PAC attributes tests >+# >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_true >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index e6d6af30d7a..ef05e1d4782 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -445,3 +445,25 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_nonexisting >+# >+# PAC attributes tests >+# >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_pac_attrs >-- >2.25.1 > > >From 6fe497a67c040a13dc1504361f1bc57712367ebb Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 21:19:44 +1300 >Subject: [PATCH 137/190] tests/krb5: Add tests for PAC-REQUEST padata > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 232 ++++++++++++++++++++++- > selftest/knownfail_heimdal_kdc | 9 + > selftest/knownfail_mit_kdc | 18 ++ > 3 files changed, 256 insertions(+), 3 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 40291677819..53d7dd4effb 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -1235,6 +1235,231 @@ class KdcTgsTests(KDCBaseTest): > expect_pac_attrs=True, > expect_pac_attrs_pac_request=True) > >+ def test_tgs_pac_request_none(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=None) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_tgs_pac_request_false(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=False) >+ >+ pac = self.get_ticket_pac(ticket, expect_pac=False) >+ self.assertIsNone(pac) >+ >+ def test_tgs_pac_request_true(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=True) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_renew_pac_request_none(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=None) >+ tgt = self._modify_tgt(tgt, renewable=True) >+ >+ tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_renew_pac_request_false(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None) >+ tgt = self._modify_tgt(tgt, renewable=True) >+ >+ tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=False) >+ >+ pac = self.get_ticket_pac(ticket, expect_pac=False) >+ self.assertIsNone(pac) >+ >+ def test_renew_pac_request_true(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=True) >+ tgt = self._modify_tgt(tgt, renewable=True) >+ >+ tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_validate_pac_request_none(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=None) >+ tgt = self._modify_tgt(tgt, invalid=True) >+ >+ tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_validate_pac_request_false(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None) >+ tgt = self._modify_tgt(tgt, invalid=True) >+ >+ tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=False) >+ >+ pac = self.get_ticket_pac(ticket, expect_pac=False) >+ self.assertIsNone(pac) >+ >+ def test_validate_pac_request_true(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=True) >+ tgt = self._modify_tgt(tgt, invalid=True) >+ >+ tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_s4u2self_pac_request_none(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=None) >+ >+ ticket = self._s4u2self(tgt, creds, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_s4u2self_pac_request_false(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None) >+ >+ ticket = self._s4u2self(tgt, creds, expected_error=0, expect_pac=False) >+ >+ pac = self.get_ticket_pac(ticket, expect_pac=False) >+ self.assertIsNone(pac) >+ >+ def test_s4u2self_pac_request_true(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=True) >+ >+ ticket = self._s4u2self(tgt, creds, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_user2user_pac_request_none(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=None) >+ >+ ticket = self._user2user(tgt, creds, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_user2user_pac_request_false(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None) >+ >+ ticket = self._user2user(tgt, creds, expected_error=0, >+ expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket, expect_pac=True) >+ self.assertIsNotNone(pac) >+ >+ def test_user2user_pac_request_true(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds, pac_request=True) >+ >+ ticket = self._user2user(tgt, creds, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_user2user_user_pac_request_none(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds) >+ >+ user_creds = self._get_mach_creds() >+ user_tgt = self.get_tgt(user_creds, pac_request=None) >+ >+ ticket = self._user2user(tgt, creds, expected_error=0, >+ user_tgt=user_tgt, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_user2user_user_pac_request_false(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds) >+ >+ user_creds = self._get_mach_creds() >+ user_tgt = self.get_tgt(user_creds, pac_request=False, expect_pac=None) >+ >+ ticket = self._user2user(tgt, creds, expected_error=0, >+ user_tgt=user_tgt, expect_pac=False) >+ >+ pac = self.get_ticket_pac(ticket, expect_pac=False) >+ self.assertIsNone(pac) >+ >+ def test_user2user_user_pac_request_true(self): >+ creds = self._get_creds() >+ tgt = self.get_tgt(creds) >+ >+ user_creds = self._get_mach_creds() >+ user_tgt = self.get_tgt(user_creds, pac_request=True) >+ >+ ticket = self._user2user(tgt, creds, expected_error=0, >+ user_tgt=user_tgt, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_tgs_rodc_pac_request_none(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self.get_tgt(creds, pac_request=None) >+ tgt = self._modify_tgt(tgt, from_rodc=True) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) >+ >+ def test_tgs_rodc_pac_request_false(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None) >+ tgt = self._modify_tgt(tgt, from_rodc=True) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=False) >+ >+ pac = self.get_ticket_pac(ticket, expect_pac=False) >+ self.assertIsNone(pac) >+ >+ def test_tgs_rodc_pac_request_true(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self.get_tgt(creds, pac_request=True) >+ tgt = self._modify_tgt(tgt, from_rodc=True) >+ >+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True) >+ >+ pac = self.get_ticket_pac(ticket) >+ self.assertIsNotNone(pac) > > def _get_tgt(self, > client_creds, >@@ -1562,9 +1787,10 @@ class KdcTgsTests(KDCBaseTest): > expect_pac=expect_pac) > > def _user2user(self, tgt, tgt_creds, expected_error, sname=None, >- expect_pac=True): >- user_creds = self._get_mach_creds() >- user_tgt = self.get_tgt(user_creds) >+ user_tgt=None, expect_pac=True): >+ if user_tgt is None: >+ user_creds = self._get_mach_creds() >+ user_tgt = self.get_tgt(user_creds) > > kdc_options = str(krb5_asn1.KDCOptions('enc-tkt-in-skey')) > return self._tgs_req(user_tgt, expected_error, tgt_creds, >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index c31a738d482..7119dbe7a40 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -208,3 +208,12 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_false > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_none > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_true >+# >+# PAC request tests >+# >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_pac_request_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_pac_request_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_pac_request_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_true >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index ef05e1d4782..546316413b9 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -467,3 +467,21 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_none > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_true > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_pac_attrs >+# >+# PAC request tests >+# >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_pac_request_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_request_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_pac_request_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_pac_request_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_pac_request_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_false >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_none >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_true >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_pac_request_false >-- >2.25.1 > > >From 58287f5a2e43ffd057ab877d3faca5168e15369e Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 21:04:25 +1300 >Subject: [PATCH 138/190] tests/krb5: Add tests for requester SID PAC buffer > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 222 ++++++++++++++++++++++- > selftest/knownfail_heimdal_kdc | 18 ++ > selftest/knownfail_mit_kdc | 20 ++ > 3 files changed, 256 insertions(+), 4 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 53d7dd4effb..2005d71fa81 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -510,6 +510,13 @@ class KdcTgsTests(KDCBaseTest): > tgt = self._get_tgt(creds) > self._user2user(tgt, creds, expected_error=0) > >+ def test_tgs_req_no_requester_sid(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds, remove_requester_sid=True) >+ >+ self._run_tgs(tgt, expected_error=0, expect_pac=True, >+ expect_requester_sid=False) # Note: not expected >+ > def test_tgs_req_no_pac_attrs(self): > creds = self._get_creds() > tgt = self._get_tgt(creds, remove_pac_attrs=True) >@@ -517,6 +524,17 @@ class KdcTgsTests(KDCBaseTest): > self._run_tgs(tgt, expected_error=0, expect_pac=True, > expect_pac_attrs=False) > >+ def test_tgs_req_from_rodc_no_requester_sid(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True, remove_requester_sid=True) >+ >+ samdb = self.get_samdb() >+ sid = self.get_objectSid(samdb, creds.get_dn()) >+ >+ self._run_tgs(tgt, expected_error=0, expect_pac=True, >+ expect_requester_sid=True, expected_sid=sid) >+ > def test_tgs_req_from_rodc_no_pac_attrs(self): > creds = self._get_creds(replication_allowed=True, > revealed_to_rodc=True) >@@ -617,6 +635,27 @@ class KdcTgsTests(KDCBaseTest): > self._user2user(tgt, creds, > expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) > >+ def test_requester_sid_mismatch_existing(self): >+ creds = self._get_creds() >+ existing_rid = self._get_existing_rid() >+ tgt = self._get_tgt(creds, new_rid=existing_rid, >+ can_modify_logon_info=False) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_logon_info_sid_mismatch_existing(self): >+ creds = self._get_creds() >+ existing_rid = self._get_existing_rid() >+ tgt = self._get_tgt(creds, new_rid=existing_rid, >+ can_modify_requester_sid=False) >+ self._run_tgs(tgt, expected_error=0) >+ >+ def test_logon_info_only_sid_mismatch_existing(self): >+ creds = self._get_creds() >+ existing_rid = self._get_existing_rid() >+ tgt = self._get_tgt(creds, new_rid=existing_rid, >+ remove_requester_sid=True) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ > # Test changing the SID in the PAC to a non-existent one. > def test_tgs_sid_mismatch_nonexisting(self): > creds = self._get_creds() >@@ -652,6 +691,27 @@ class KdcTgsTests(KDCBaseTest): > self._user2user(tgt, creds, > expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) > >+ def test_requester_sid_mismatch_nonexisting(self): >+ creds = self._get_creds() >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, new_rid=nonexistent_rid, >+ can_modify_logon_info=False) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_logon_info_sid_mismatch_nonexisting(self): >+ creds = self._get_creds() >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, new_rid=nonexistent_rid, >+ can_modify_requester_sid=False) >+ self._run_tgs(tgt, expected_error=0) >+ >+ def test_logon_info_only_sid_mismatch_nonexisting(self): >+ creds = self._get_creds() >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, new_rid=nonexistent_rid, >+ remove_requester_sid=True) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ > # Test with an RODC-issued ticket where the client is revealed to the RODC. > def test_tgs_rodc_revealed(self): > creds = self._get_creds(replication_allowed=True, >@@ -728,6 +788,33 @@ class KdcTgsTests(KDCBaseTest): > self._user2user(tgt, creds, > expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) > >+ def test_tgs_rodc_requester_sid_mismatch_existing(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ existing_rid = self._get_existing_rid(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid, >+ can_modify_logon_info=False) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_tgs_rodc_logon_info_sid_mismatch_existing(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ existing_rid = self._get_existing_rid(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid, >+ can_modify_requester_sid=False) >+ self._run_tgs(tgt, expected_error=0) >+ >+ def test_tgs_rodc_logon_info_only_sid_mismatch_existing(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ existing_rid = self._get_existing_rid(replication_allowed=True, >+ revealed_to_rodc=True) >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid, >+ remove_requester_sid=True) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ > # Test with an RODC-issued ticket where the SID in the PAC is changed to a > # non-existent one. > def test_tgs_rodc_sid_mismatch_nonexisting(self): >@@ -768,6 +855,30 @@ class KdcTgsTests(KDCBaseTest): > self._user2user(tgt, creds, > expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) > >+ def test_tgs_rodc_requester_sid_mismatch_nonexisting(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid, >+ can_modify_logon_info=False) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ >+ def test_tgs_rodc_logon_info_sid_mismatch_nonexisting(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid, >+ can_modify_requester_sid=False) >+ self._run_tgs(tgt, expected_error=0) >+ >+ def test_tgs_rodc_logon_info_only_sid_mismatch_nonexisting(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ nonexistent_rid = self._get_non_existent_rid() >+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid, >+ remove_requester_sid=True) >+ self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH) >+ > # Test with an RODC-issued ticket where the client is not revealed to the > # RODC. > def test_tgs_rodc_not_revealed(self): >@@ -1235,6 +1346,99 @@ class KdcTgsTests(KDCBaseTest): > expect_pac_attrs=True, > expect_pac_attrs_pac_request=True) > >+ def test_as_requester_sid(self): >+ creds = self._get_creds() >+ >+ samdb = self.get_samdb() >+ sid = self.get_objectSid(samdb, creds.get_dn()) >+ >+ self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expected_sid=sid, >+ expect_requester_sid=True) >+ >+ def test_tgs_requester_sid(self): >+ creds = self._get_creds() >+ >+ samdb = self.get_samdb() >+ sid = self.get_objectSid(samdb, creds.get_dn()) >+ >+ tgt = self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expected_sid=sid, >+ expect_requester_sid=True) >+ >+ self._run_tgs(tgt, expected_error=0, expect_pac=True, >+ expected_sid=sid, >+ expect_requester_sid=True) >+ >+ def test_tgs_requester_sid_renew(self): >+ creds = self._get_creds() >+ >+ samdb = self.get_samdb() >+ sid = self.get_objectSid(samdb, creds.get_dn()) >+ >+ tgt = self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expected_sid=sid, >+ expect_requester_sid=True) >+ tgt = self._modify_tgt(tgt, renewable=True) >+ >+ self._renew_tgt(tgt, expected_error=0, expect_pac=True, >+ expected_sid=sid, >+ expect_requester_sid=True) >+ >+ def test_tgs_requester_sid_rodc_renew(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ >+ samdb = self.get_samdb() >+ sid = self.get_objectSid(samdb, creds.get_dn()) >+ >+ tgt = self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expected_sid=sid, >+ expect_requester_sid=True) >+ tgt = self._modify_tgt(tgt, from_rodc=True, renewable=True) >+ >+ self._renew_tgt(tgt, expected_error=0, expect_pac=True, >+ expected_sid=sid, >+ expect_requester_sid=True) >+ >+ def test_tgs_requester_sid_missing_renew(self): >+ creds = self._get_creds() >+ >+ samdb = self.get_samdb() >+ sid = self.get_objectSid(samdb, creds.get_dn()) >+ >+ tgt = self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expected_sid=sid, >+ expect_requester_sid=True) >+ tgt = self._modify_tgt(tgt, renewable=True, >+ remove_requester_sid=True) >+ >+ self._renew_tgt(tgt, expected_error=0, expect_pac=True, >+ expect_requester_sid=False) # Note: not expected >+ >+ def test_tgs_requester_sid_missing_rodc_renew(self): >+ creds = self._get_creds(replication_allowed=True, >+ revealed_to_rodc=True) >+ >+ samdb = self.get_samdb() >+ sid = self.get_objectSid(samdb, creds.get_dn()) >+ >+ tgt = self.get_tgt(creds, pac_request=None, >+ expect_pac=True, >+ expected_sid=sid, >+ expect_requester_sid=True) >+ tgt = self._modify_tgt(tgt, from_rodc=True, renewable=True, >+ remove_requester_sid=True) >+ >+ self._renew_tgt(tgt, expected_error=0, expect_pac=True, >+ expected_sid=sid, >+ expect_requester_sid=True) >+ > def test_tgs_pac_request_none(self): > creds = self._get_creds() > tgt = self.get_tgt(creds, pac_request=None) >@@ -1733,16 +1937,20 @@ class KdcTgsTests(KDCBaseTest): > return (1 << 30) - 1 > > def _run_tgs(self, tgt, expected_error, expect_pac=True, >- expect_pac_attrs=None, expect_pac_attrs_pac_request=None): >+ expect_pac_attrs=None, expect_pac_attrs_pac_request=None, >+ expect_requester_sid=None, expected_sid=None): > target_creds = self.get_service_creds() > return self._tgs_req( > tgt, expected_error, target_creds, > expect_pac=expect_pac, > expect_pac_attrs=expect_pac_attrs, >- expect_pac_attrs_pac_request=expect_pac_attrs_pac_request) >+ expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, >+ expect_requester_sid=expect_requester_sid, >+ expected_sid=expected_sid) > > def _renew_tgt(self, tgt, expected_error, expect_pac=True, >- expect_pac_attrs=None, expect_pac_attrs_pac_request=None): >+ expect_pac_attrs=None, expect_pac_attrs_pac_request=None, >+ expect_requester_sid=None, expected_sid=None): > krbtgt_creds = self.get_krbtgt_creds() > kdc_options = str(krb5_asn1.KDCOptions('renew')) > return self._tgs_req( >@@ -1750,7 +1958,9 @@ class KdcTgsTests(KDCBaseTest): > kdc_options=kdc_options, > expect_pac=expect_pac, > expect_pac_attrs=expect_pac_attrs, >- expect_pac_attrs_pac_request=expect_pac_attrs_pac_request) >+ expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, >+ expect_requester_sid=expect_requester_sid, >+ expected_sid=expected_sid) > > def _validate_tgt(self, tgt, expected_error, expect_pac=True): > krbtgt_creds = self.get_krbtgt_creds() >@@ -1809,7 +2019,9 @@ class KdcTgsTests(KDCBaseTest): > expect_pac=True, > expect_pac_attrs=None, > expect_pac_attrs_pac_request=None, >+ expect_requester_sid=None, > expect_edata=False, >+ expected_sid=None, > expected_status=None): > srealm = target_creds.get_realm() > >@@ -1865,6 +2077,8 @@ class KdcTgsTests(KDCBaseTest): > expect_pac=expect_pac, > expect_pac_attrs=expect_pac_attrs, > expect_pac_attrs_pac_request=expect_pac_attrs_pac_request, >+ expect_requester_sid=expect_requester_sid, >+ expected_sid=expected_sid, > expect_claims=expect_claims) > > rep = self._generic_kdc_exchange(kdc_exchange_dict, >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 7119dbe7a40..749a8892ab0 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -217,3 +217,21 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_false > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_none > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_true >+# >+# PAC requester SID tests >+# >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_as_requester_sid >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_renew >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_renew >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_nonexisting >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index 546316413b9..176c0747b48 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -485,3 +485,23 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_none > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_true > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_pac_request_false >+# >+# PAC requester SID tests >+# >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_as_requester_sid >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_renew >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_renew >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_nonexisting >-- >2.25.1 > > >From 6191ef18fd7fadbedcf47e105765f836e10649cc Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 21:06:58 +1300 >Subject: [PATCH 139/190] tests/krb5: Add test for user-to-user with no sname > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 38 +++++++++++++++++------- > selftest/knownfail_heimdal_kdc | 1 + > selftest/knownfail_mit_kdc | 1 + > 3 files changed, 29 insertions(+), 11 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index 2005d71fa81..b0f60c0a8ce 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -1122,6 +1122,14 @@ class KdcTgsTests(KDCBaseTest): > self._user2user(tgt, creds, sname=sname, > expected_error=KDC_ERR_S_PRINCIPAL_UNKNOWN) > >+ def test_user2user_no_sname(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ self._user2user(tgt, creds, sname=False, >+ expected_error=(KDC_ERR_GENERIC, >+ KDC_ERR_S_PRINCIPAL_UNKNOWN)) >+ > def test_user2user_service_ticket(self): > creds = self._get_creds() > tgt = self._get_tgt(creds) >@@ -2025,16 +2033,24 @@ class KdcTgsTests(KDCBaseTest): > expected_status=None): > srealm = target_creds.get_realm() > >- if sname is None: >- target_name = target_creds.get_username() >- if target_name == 'krbtgt': >- sname = self.PrincipalName_create(name_type=NT_SRV_INST, >- names=[target_name, srealm]) >- else: >- if target_name[-1] == '$': >- target_name = target_name[:-1] >- sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >- names=['host', target_name]) >+ if sname is False: >+ sname = None >+ expected_sname = self.get_krbtgt_sname() >+ else: >+ if sname is None: >+ target_name = target_creds.get_username() >+ if target_name == 'krbtgt': >+ sname = self.PrincipalName_create( >+ name_type=NT_SRV_INST, >+ names=[target_name, srealm]) >+ else: >+ if target_name[-1] == '$': >+ target_name = target_name[:-1] >+ sname = self.PrincipalName_create( >+ name_type=NT_PRINCIPAL, >+ names=['host', target_name]) >+ >+ expected_sname = sname > > if additional_ticket is not None: > additional_tickets = [additional_ticket.ticket] >@@ -2062,7 +2078,7 @@ class KdcTgsTests(KDCBaseTest): > expected_crealm=tgt.crealm, > expected_cname=expected_cname, > expected_srealm=srealm, >- expected_sname=sname, >+ expected_sname=expected_sname, > ticket_decryption_key=decryption_key, > generate_padata_fn=generate_padata_fn, > check_error_fn=check_error_fn, >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 749a8892ab0..005d348484e 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -161,6 +161,7 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_matching_sname_host > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_sname > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_non_existent_sname > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_req > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_allowed_denied >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index 176c0747b48..e77d01fecea 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -419,6 +419,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_user > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_sname > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_req > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_allowed_denied > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_denied >-- >2.25.1 > > >From 5f8936a26a03462dc26c58c8fae1e68f46b35b24 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 21:09:32 +1300 >Subject: [PATCH 140/190] tests/krb5: Add tests for mismatched names with > user-to-user > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/krb5/kdc_tgs_tests.py | 105 ++++++++++++++++++- > python/samba/tests/krb5/rfc4120_constants.py | 1 + > selftest/knownfail_heimdal_kdc | 8 ++ > selftest/knownfail_mit_kdc | 8 ++ > 4 files changed, 120 insertions(+), 2 deletions(-) > >diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py >index b0f60c0a8ce..cfe1ad42d61 100755 >--- a/python/samba/tests/krb5/kdc_tgs_tests.py >+++ b/python/samba/tests/krb5/kdc_tgs_tests.py >@@ -43,8 +43,10 @@ from samba.tests.krb5.rfc4120_constants import ( > KDC_ERR_GENERIC, > KDC_ERR_MODIFIED, > KDC_ERR_POLICY, >+ KDC_ERR_C_PRINCIPAL_UNKNOWN, > KDC_ERR_S_PRINCIPAL_UNKNOWN, > KDC_ERR_TGT_REVOKED, >+ KDC_ERR_WRONG_REALM, > NT_PRINCIPAL, > NT_SRV_INST, > ) >@@ -1112,6 +1114,100 @@ class KdcTgsTests(KDCBaseTest): > expected_error=(KDC_ERR_BADMATCH, > KDC_ERR_BADOPTION)) > >+ def test_user2user_other_sname(self): >+ other_name = self.get_new_username() >+ spn = f'host/{other_name}' >+ creds = self.get_cached_creds( >+ account_type=self.AccountType.COMPUTER, >+ opts={'spn': spn}) >+ tgt = self._get_tgt(creds) >+ >+ sname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=['host', other_name]) >+ >+ self._user2user(tgt, creds, sname=sname, expected_error=0) >+ >+ def test_user2user_wrong_sname_krbtgt(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ sname = self.get_krbtgt_sname() >+ >+ self._user2user(tgt, creds, sname=sname, >+ expected_error=(KDC_ERR_BADMATCH, >+ KDC_ERR_BADOPTION)) >+ >+ def test_user2user_wrong_srealm(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ self._user2user(tgt, creds, srealm='OTHER.REALM', >+ expected_error=(KDC_ERR_WRONG_REALM, >+ KDC_ERR_S_PRINCIPAL_UNKNOWN)) >+ >+ def test_user2user_tgt_correct_realm(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ realm = creds.get_realm().encode('utf-8') >+ tgt = self._modify_tgt(tgt, realm) >+ >+ self._user2user(tgt, creds, >+ expected_error=0) >+ >+ def test_user2user_tgt_wrong_realm(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ tgt = self._modify_tgt(tgt, b'OTHER.REALM') >+ >+ self._user2user(tgt, creds, >+ expected_error=0) >+ >+ def test_user2user_tgt_correct_cname(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ user_name = creds.get_username() >+ user_name = user_name.encode('utf-8') >+ cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=[user_name]) >+ >+ tgt = self._modify_tgt(tgt, cname=cname) >+ >+ self._user2user(tgt, creds, expected_error=0) >+ >+ def test_user2user_tgt_other_cname(self): >+ samdb = self.get_samdb() >+ >+ other_name = self.get_new_username() >+ upn = f'{other_name}@{samdb.domain_dns_name()}' >+ >+ creds = self.get_cached_creds( >+ account_type=self.AccountType.COMPUTER, >+ opts={'upn': upn}) >+ tgt = self._get_tgt(creds) >+ >+ cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=[other_name.encode('utf-8')]) >+ >+ tgt = self._modify_tgt(tgt, cname=cname) >+ >+ self._user2user(tgt, creds, expected_error=0) >+ >+ def test_user2user_tgt_cname_host(self): >+ creds = self._get_creds() >+ tgt = self._get_tgt(creds) >+ >+ user_name = creds.get_username() >+ user_name = user_name.encode('utf-8') >+ cname = self.PrincipalName_create(name_type=NT_PRINCIPAL, >+ names=[b'host', user_name]) >+ >+ tgt = self._modify_tgt(tgt, cname=cname) >+ >+ self._user2user(tgt, creds, expected_error=KDC_ERR_C_PRINCIPAL_UNKNOWN) >+ > def test_user2user_non_existent_sname(self): > creds = self._get_creds() > tgt = self._get_tgt(creds) >@@ -2005,7 +2101,7 @@ class KdcTgsTests(KDCBaseTest): > expect_pac=expect_pac) > > def _user2user(self, tgt, tgt_creds, expected_error, sname=None, >- user_tgt=None, expect_pac=True): >+ srealm=None, user_tgt=None, expect_pac=True): > if user_tgt is None: > user_creds = self._get_mach_creds() > user_tgt = self.get_tgt(user_creds) >@@ -2015,6 +2111,7 @@ class KdcTgsTests(KDCBaseTest): > kdc_options=kdc_options, > additional_ticket=tgt, > sname=sname, >+ srealm=srealm, > expect_pac=expect_pac) > > def _tgs_req(self, tgt, expected_error, target_creds, >@@ -2023,6 +2120,7 @@ class KdcTgsTests(KDCBaseTest): > additional_ticket=None, > generate_padata_fn=None, > sname=None, >+ srealm=None, > expect_claims=True, > expect_pac=True, > expect_pac_attrs=None, >@@ -2031,7 +2129,10 @@ class KdcTgsTests(KDCBaseTest): > expect_edata=False, > expected_sid=None, > expected_status=None): >- srealm = target_creds.get_realm() >+ if srealm is False: >+ srealm = None >+ elif srealm is None: >+ srealm = target_creds.get_realm() > > if sname is False: > sname = None >diff --git a/python/samba/tests/krb5/rfc4120_constants.py b/python/samba/tests/krb5/rfc4120_constants.py >index 490cd255ec3..5251e291fde 100644 >--- a/python/samba/tests/krb5/rfc4120_constants.py >+++ b/python/samba/tests/krb5/rfc4120_constants.py >@@ -82,6 +82,7 @@ KDC_ERR_SKEW = 37 > KDC_ERR_MODIFIED = 41 > KDC_ERR_INAPP_CKSUM = 50 > KDC_ERR_GENERIC = 60 >+KDC_ERR_WRONG_REALM = 68 > KDC_ERR_CLIENT_NAME_MISMATCH = 75 > KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS = 93 > >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index 005d348484e..a52e6c741fc 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -163,6 +163,7 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_sname > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_non_existent_sname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_other_sname > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_req > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_allowed_denied > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_denied >@@ -175,7 +176,14 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_tgt_cname_host >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_tgt_correct_cname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_tgt_correct_realm >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_tgt_other_cname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_tgt_wrong_realm > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname_krbtgt >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_srealm > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_allowed_denied >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index e77d01fecea..d74d55fb01c 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -420,6 +420,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_sname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_other_sname > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_req > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_allowed_denied > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_denied >@@ -432,7 +433,14 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_tgt_cname_host >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_tgt_correct_cname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_tgt_correct_realm >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_tgt_other_cname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_tgt_wrong_realm > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname_krbtgt >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_srealm > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_allowed_denied >-- >2.25.1 > > >From 570001a9c81458f905a5af1c6877b8f1371370d7 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Fri, 29 Oct 2021 11:00:38 +1300 >Subject: [PATCH 141/190] s4/torture: Expect additional PAC buffers > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > selftest/knownfail_heimdal_kdc | 39 ++++++++++++++++++++++++++++++++ > source4/torture/rpc/remote_pac.c | 24 ++++++++++++++++++-- > 2 files changed, 61 insertions(+), 2 deletions(-) > >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index a52e6c741fc..f8a5d8bf7da 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -244,3 +244,42 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_nonexisting >+# >+# PAC tests >+# >+^samba4.blackbox.pkinit_pac.STEP1 remote.pac verification.ad_dc:local >+^samba4.blackbox.pkinit_pac.STEP1 remote.pac verification.ad_dc_ntvfs:local >+^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc:local >+^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc_ntvfs:local >+^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc:local >+^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc_ntvfs:local >+^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc:local >+^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc_ntvfs:local >+^samba4.blackbox.pkinit_pac.netr-mem-arcfour.s4u2proxy-arcfour.ad_dc:local >+^samba4.blackbox.pkinit_pac.netr-mem-arcfour.s4u2proxy-arcfour.ad_dc_ntvfs:local >+^samba4.blackbox.pkinit_pac.netr-mem-arcfour.verify-sig-arcfour.ad_dc:local >+^samba4.blackbox.pkinit_pac.netr-mem-arcfour.verify-sig-arcfour.ad_dc_ntvfs:local >+^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2000dc >+^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2003dc >+^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2008dc >+^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2008r2dc >+^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2000dc >+^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2003dc >+^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2008dc >+^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2008r2dc >+^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2000dc >+^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2003dc >+^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2008dc >+^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2008r2dc >+^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2000dc >+^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2003dc >+^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2008dc >+^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2008r2dc >+^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2000dc >+^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2003dc >+^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2008dc >+^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2008r2dc >+^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2000dc >+^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2003dc >+^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2008dc >+^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2008r2dc >diff --git a/source4/torture/rpc/remote_pac.c b/source4/torture/rpc/remote_pac.c >index 16249799e36..5a1567f1bde 100644 >--- a/source4/torture/rpc/remote_pac.c >+++ b/source4/torture/rpc/remote_pac.c >@@ -308,7 +308,7 @@ static bool test_PACVerify(struct torture_context *tctx, > (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); > torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_pull_struct_blob of PAC_DATA structure failed"); > >- num_pac_buffers = 5; >+ num_pac_buffers = 7; > if (expect_pac_upn_dns_info) { > num_pac_buffers += 1; > } >@@ -365,6 +365,18 @@ static bool test_PACVerify(struct torture_context *tctx, > pac_buf->info != NULL, > "PAC_TYPE_TICKET_CHECKSUM info"); > >+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_ATTRIBUTES_INFO); >+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_ATTRIBUTES_INFO"); >+ torture_assert(tctx, >+ pac_buf->info != NULL, >+ "PAC_TYPE_ATTRIBUTES_INFO info"); >+ >+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_REQUESTER_SID); >+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_REQUESTER_SID"); >+ torture_assert(tctx, >+ pac_buf->info != NULL, >+ "PAC_TYPE_REQUESTER_SID info"); >+ > ok = netlogon_validate_pac(tctx, p, server_creds, secure_channel_type, test_machine_name, > negotiate_flags, pac_data, session_info); > >@@ -1128,7 +1140,7 @@ static bool test_S4U2Proxy(struct torture_context *tctx, > (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); > torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_pull_struct_blob of PAC_DATA structure failed"); > >- num_pac_buffers = 7; >+ num_pac_buffers = 9; > > torture_assert_int_equal(tctx, pac_data_struct.version, 0, "version"); > torture_assert_int_equal(tctx, pac_data_struct.num_buffers, num_pac_buffers, "num_buffers"); >@@ -1168,6 +1180,14 @@ static bool test_S4U2Proxy(struct torture_context *tctx, > talloc_asprintf(tctx, "%s@%s", self_princ, cli_credentials_get_realm(credentials)), > "wrong transited_services[0]"); > >+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_ATTRIBUTES_INFO); >+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_ATTRIBUTES_INFO"); >+ torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_ATTRIBUTES_INFO info"); >+ >+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_REQUESTER_SID); >+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_REQUESTER_SID"); >+ torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_REQUESTER_SID info"); >+ > return netlogon_validate_pac(tctx, p, server_creds, secure_channel_type, test_machine_name, > negotiate_flags, pac_data, session_info); > } >-- >2.25.1 > > >From e308a224d38b994fb4603fda8412964776a0b17f Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Wed, 27 Oct 2021 19:18:20 +1300 >Subject: [PATCH 142/190] pytest: Raise an error when adding a dynamic test > that would overwrite an existing test > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > python/samba/tests/__init__.py | 5 ++++- > 1 file changed, 4 insertions(+), 1 deletion(-) > >diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py >index ab366c7ed30..6d4993ac255 100644 >--- a/python/samba/tests/__init__.py >+++ b/python/samba/tests/__init__.py >@@ -99,7 +99,10 @@ class TestCase(unittest.TestCase): > def fn(self): > getattr(self, "_%s_with_args" % fnname)(*args) > fn.__doc__ = doc >- setattr(cls, "%s_%s" % (fnname, suffix), fn) >+ attr = "%s_%s" % (fnname, suffix) >+ if hasattr(cls, attr): >+ raise RuntimeError(f"Dynamic test {attr} already exists!") >+ setattr(cls, attr, fn) > > @classmethod > def setUpDynamicTestCases(cls): >-- >2.25.1 > > >From 7a2497a9fb8cee9ee3ebe0b94a3fb3a248226a1f Mon Sep 17 00:00:00 2001 >From: Andreas Schneider <asn@samba.org> >Date: Mon, 12 Jul 2021 12:32:12 +0200 >Subject: [PATCH 143/190] mit-samba: Make ks_get_principal() internally public > >Signed-off-by: Andreas Schneider <asn@samba.org> >--- > source4/kdc/mit-kdb/kdb_samba.h | 5 +++++ > source4/kdc/mit-kdb/kdb_samba_principals.c | 8 ++++---- > 2 files changed, 9 insertions(+), 4 deletions(-) > >diff --git a/source4/kdc/mit-kdb/kdb_samba.h b/source4/kdc/mit-kdb/kdb_samba.h >index 8a29334bcea..d666216b0c1 100644 >--- a/source4/kdc/mit-kdb/kdb_samba.h >+++ b/source4/kdc/mit-kdb/kdb_samba.h >@@ -41,6 +41,11 @@ > > struct mit_samba_context *ks_get_context(krb5_context kcontext); > >+krb5_error_code ks_get_principal(krb5_context context, >+ krb5_const_principal principal, >+ unsigned int kflags, >+ krb5_db_entry **kentry); >+ > bool ks_data_eq_string(krb5_data d, const char *s); > > krb5_data ks_make_data(void *data, unsigned int len); >diff --git a/source4/kdc/mit-kdb/kdb_samba_principals.c b/source4/kdc/mit-kdb/kdb_samba_principals.c >index a8c99b025c9..b543600a360 100644 >--- a/source4/kdc/mit-kdb/kdb_samba_principals.c >+++ b/source4/kdc/mit-kdb/kdb_samba_principals.c >@@ -36,10 +36,10 @@ > #define ADMIN_LIFETIME 60*60*3 /* 3 hours */ > #define CHANGEPW_LIFETIME 60*5 /* 5 minutes */ > >-static krb5_error_code ks_get_principal(krb5_context context, >- krb5_const_principal principal, >- unsigned int kflags, >- krb5_db_entry **kentry) >+krb5_error_code ks_get_principal(krb5_context context, >+ krb5_const_principal principal, >+ unsigned int kflags, >+ krb5_db_entry **kentry) > { > struct mit_samba_context *mit_ctx; > krb5_error_code code; >-- >2.25.1 > > >From 66e6f8f73b62ca8b6eb8fb6123bd16aee177c881 Mon Sep 17 00:00:00 2001 >From: Andreas Schneider <asn@samba.org> >Date: Wed, 14 Jul 2021 14:51:34 +0200 >Subject: [PATCH 144/190] mit-samba: Add ks_free_principal() > >--- > source4/kdc/mit-kdb/kdb_samba.h | 2 + > source4/kdc/mit-kdb/kdb_samba_principals.c | 52 ++++++++++++++++++++++ > 2 files changed, 54 insertions(+) > >diff --git a/source4/kdc/mit-kdb/kdb_samba.h b/source4/kdc/mit-kdb/kdb_samba.h >index d666216b0c1..e9613e2fc7e 100644 >--- a/source4/kdc/mit-kdb/kdb_samba.h >+++ b/source4/kdc/mit-kdb/kdb_samba.h >@@ -46,6 +46,8 @@ krb5_error_code ks_get_principal(krb5_context context, > unsigned int kflags, > krb5_db_entry **kentry); > >+void ks_free_principal(krb5_context context, krb5_db_entry *entry); >+ > bool ks_data_eq_string(krb5_data d, const char *s); > > krb5_data ks_make_data(void *data, unsigned int len); >diff --git a/source4/kdc/mit-kdb/kdb_samba_principals.c b/source4/kdc/mit-kdb/kdb_samba_principals.c >index b543600a360..3917b9824c6 100644 >--- a/source4/kdc/mit-kdb/kdb_samba_principals.c >+++ b/source4/kdc/mit-kdb/kdb_samba_principals.c >@@ -62,6 +62,58 @@ cleanup: > return code; > } > >+static void ks_free_principal_e_data(krb5_context context, krb5_octet *e_data) >+{ >+ struct samba_kdc_entry *skdc_entry; >+ >+ skdc_entry = talloc_get_type_abort(e_data, >+ struct samba_kdc_entry); >+ talloc_set_destructor(skdc_entry, NULL); >+ TALLOC_FREE(skdc_entry); >+} >+ >+void ks_free_principal(krb5_context context, krb5_db_entry *entry) >+{ >+ krb5_tl_data *tl_data_next = NULL; >+ krb5_tl_data *tl_data = NULL; >+ size_t i, j; >+ >+ if (entry != NULL) { >+ krb5_free_principal(context, entry->princ); >+ >+ for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) { >+ tl_data_next = tl_data->tl_data_next; >+ if (tl_data->tl_data_contents != NULL) { >+ free(tl_data->tl_data_contents); >+ } >+ free(tl_data); >+ } >+ >+ if (entry->key_data != NULL) { >+ for (i = 0; i < entry->n_key_data; i++) { >+ for (j = 0; j < entry->key_data[i].key_data_ver; j++) { >+ if (entry->key_data[i].key_data_length[j] != 0) { >+ if (entry->key_data[i].key_data_contents[j] != NULL) { >+ memset(entry->key_data[i].key_data_contents[j], 0, entry->key_data[i].key_data_length[j]); >+ free(entry->key_data[i].key_data_contents[j]); >+ } >+ } >+ entry->key_data[i].key_data_contents[j] = NULL; >+ entry->key_data[i].key_data_length[j] = 0; >+ entry->key_data[i].key_data_type[j] = 0; >+ } >+ } >+ free(entry->key_data); >+ } >+ >+ if (entry->e_data) { >+ ks_free_principal_e_data(context, entry->e_data); >+ } >+ >+ free(entry); >+ } >+} >+ > static krb5_boolean ks_is_master_key_principal(krb5_context context, > krb5_const_principal princ) > { >-- >2.25.1 > > >From 0ea96e935617b7cf7e96d66773988632c5b0c662 Mon Sep 17 00:00:00 2001 >From: Andreas Schneider <asn@samba.org> >Date: Mon, 12 Jul 2021 11:20:29 +0200 >Subject: [PATCH 145/190] mit-samba: If we use client_princ, always lookup the > db entry > >Signed-off-by: Andreas Schneider <asn@samba.org> >--- > source4/kdc/mit-kdb/kdb_samba_policies.c | 81 ++++++++++++++++++++++-- > 1 file changed, 75 insertions(+), 6 deletions(-) > >diff --git a/source4/kdc/mit-kdb/kdb_samba_policies.c b/source4/kdc/mit-kdb/kdb_samba_policies.c >index ac9865aac60..7a404f90e5e 100644 >--- a/source4/kdc/mit-kdb/kdb_samba_policies.c >+++ b/source4/kdc/mit-kdb/kdb_samba_policies.c >@@ -309,6 +309,8 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context, > krb5_data ***auth_indicators, > krb5_authdata ***signed_auth_data) > { >+ krb5_const_principal ks_client_princ = NULL; >+ krb5_db_entry *client_entry = NULL; > krb5_authdata **authdata = NULL; > krb5_boolean is_as_req; > krb5_error_code code; >@@ -325,8 +327,72 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context, > > is_as_req = ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0); > >+ /* >+ * When using s4u2proxy client_princ actually refers to the proxied user >+ * while client->princ to the proxy service asking for the TGS on behalf >+ * of the proxied user. So always use client_princ in preference. >+ * >+ * Note that when client principal is not NULL, client entry might be >+ * NULL for cross-realm case, so we need to make sure to not >+ * dereference NULL pointer here. >+ */ >+ if (client_princ != NULL) { >+ ks_client_princ = client_princ; >+ if (!is_as_req) { >+ krb5_boolean is_equal = false; >+ >+ if (client != NULL && client->princ != NULL) { >+ is_equal = >+ krb5_principal_compare(context, >+ client_princ, >+ client->princ); >+ } >+ >+ /* >+ * When client principal is the same as supplied client >+ * entry, don't fetch it. >+ */ >+ if (!is_equal) { >+ code = ks_get_principal(context, >+ ks_client_princ, >+ 0, >+ &client_entry); >+ if (code != 0) { >+ char *client_name = NULL; >+ >+ (void)krb5_unparse_name(context, >+ ks_client_princ, >+ &client_name); >+ >+ DBG_DEBUG("We didn't find the client " >+ "principal [%s] in our " >+ "database.\n", >+ client_name); >+ SAFE_FREE(client_name); >+ >+ /* >+ * If we didn't find client_princ in our >+ * database it might be from another >+ * realm. >+ */ >+ client_entry = NULL; >+ } >+ } >+ } >+ } else { >+ if (client == NULL) { >+ *signed_auth_data = NULL; >+ return 0; >+ } >+ ks_client_princ = client->princ; >+ } >+ >+ if (client_entry == NULL) { >+ client_entry = client; >+ } >+ > if (is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC)) { >- code = ks_get_pac(context, client, client_key, &pac); >+ code = ks_get_pac(context, client_entry, client_key, &pac); > if (code != 0) { > goto done; > } >@@ -335,8 +401,8 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context, > if (!is_as_req) { > code = ks_verify_pac(context, > flags, >- client_princ, >- client, >+ ks_client_princ, >+ client_entry, > server, > krbtgt, > server_key, >@@ -349,9 +415,9 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context, > } > } > >- if (pac == NULL && client != NULL) { >+ if (pac == NULL) { > >- code = ks_get_pac(context, client, client_key, &pac); >+ code = ks_get_pac(context, client_entry, client_key, &pac); > if (code != 0) { > goto done; > } >@@ -362,7 +428,7 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context, > goto done; > } > >- code = krb5_pac_sign(context, pac, authtime, client_princ, >+ code = krb5_pac_sign(context, pac, authtime, ks_client_princ, > server_key, krbtgt_key, &pac_data); > if (code != 0) { > DBG_ERR("krb5_pac_sign failed: %d\n", code); >@@ -396,6 +462,9 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context, > code = 0; > > done: >+ if (client_entry != NULL && client_entry != client) { >+ ks_free_principal(context, client_entry); >+ } > krb5_pac_free(context, pac); > krb5_free_authdata(context, authdata); > >-- >2.25.1 > > >From 5cd7de5e89b5d26611fff641a6b9449eda7501a1 Mon Sep 17 00:00:00 2001 >From: Andreas Schneider <asn@samba.org> >Date: Mon, 12 Jul 2021 13:12:00 +0200 >Subject: [PATCH 146/190] mit-samba: Add mit_samba_princ_needs_pac() > >Signed-off-by: Andreas Schneider <asn@samba.org> >--- > source4/kdc/mit_samba.c | 8 ++++++++ > source4/kdc/mit_samba.h | 2 ++ > 2 files changed, 10 insertions(+) > >diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c >index 22f9a54a05b..3f8b0b9fb2b 100644 >--- a/source4/kdc/mit_samba.c >+++ b/source4/kdc/mit_samba.c >@@ -1176,3 +1176,11 @@ void mit_samba_update_bad_password_count(krb5_db_entry *db_entry) > p->msg, > ldb_get_default_basedn(p->kdc_db_ctx->samdb)); > } >+ >+bool mit_samba_princ_needs_pac(krb5_db_entry *db_entry) >+{ >+ struct samba_kdc_entry *skdc_entry = >+ talloc_get_type_abort(db_entry->e_data, struct samba_kdc_entry); >+ >+ return samba_princ_needs_pac(skdc_entry); >+} >diff --git a/source4/kdc/mit_samba.h b/source4/kdc/mit_samba.h >index ba824557bd5..636c77ec97c 100644 >--- a/source4/kdc/mit_samba.h >+++ b/source4/kdc/mit_samba.h >@@ -85,4 +85,6 @@ void mit_samba_zero_bad_password_count(krb5_db_entry *db_entry); > > void mit_samba_update_bad_password_count(krb5_db_entry *db_entry); > >+bool mit_samba_princ_needs_pac(krb5_db_entry *db_entry); >+ > #endif /* _MIT_SAMBA_H */ >-- >2.25.1 > > >From a108522825ababa2e7f338e7d9c057569a125121 Mon Sep 17 00:00:00 2001 >From: Andreas Schneider <asn@samba.org> >Date: Mon, 12 Jul 2021 13:58:57 +0200 >Subject: [PATCH 147/190] mit-samba: Handle no DB entry in mit_samba_get_pac() > >Signed-off-by: Andreas Schneider <asn@samba.org> >--- > source4/kdc/mit_samba.c | 4 ++++ > 1 file changed, 4 insertions(+) > >diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c >index 3f8b0b9fb2b..e7fe67241ef 100644 >--- a/source4/kdc/mit_samba.c >+++ b/source4/kdc/mit_samba.c >@@ -463,6 +463,10 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx, > &upn_dns_info_blob); > if (!NT_STATUS_IS_OK(nt_status)) { > talloc_free(tmp_ctx); >+ if (NT_STATUS_EQUAL(nt_status, >+ NT_STATUS_OBJECT_NAME_NOT_FOUND)) { >+ return ENOENT; >+ } > return EINVAL; > } > >-- >2.25.1 > > >From e450bf8ce5a52d210c44ce09f931f4ff73242e94 Mon Sep 17 00:00:00 2001 >From: Andreas Schneider <asn@samba.org> >Date: Mon, 12 Jul 2021 14:00:19 +0200 >Subject: [PATCH 148/190] mit-samba: Rework PAC handling in > kdb_samba_db_sign_auth_data() > >Signed-off-by: Andreas Schneider <asn@samba.org> >--- > selftest/knownfail_mit_kdc | 6 +- > source4/kdc/mit-kdb/kdb_samba_policies.c | 116 ++++++++++++++++++----- > 2 files changed, 93 insertions(+), 29 deletions(-) > >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index d74d55fb01c..10c41c51fd0 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -258,12 +258,13 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > # > # KDC TGS PAC tests > # >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_client_no_auth_data_required\(ad_dc\) >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_client_no_auth_data_required\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_service_no_auth_data_required\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_client_no_auth_data_required\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_service_no_auth_data_required\(ad_dc\) > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_request_no_pac\(ad_dc\) >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_service_no_auth_data_required\(ad_dc\) > # > # MIT currently fails the following MS-KILE tests. > # >@@ -479,11 +480,9 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > # > # PAC request tests > # >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_pac_request_false > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_false > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_none > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_true >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_request_false > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_false > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_none > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_true >@@ -493,7 +492,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_false > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_none > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_user_pac_request_true >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_pac_request_false > # > # PAC requester SID tests > # >diff --git a/source4/kdc/mit-kdb/kdb_samba_policies.c b/source4/kdc/mit-kdb/kdb_samba_policies.c >index 7a404f90e5e..f35210669c2 100644 >--- a/source4/kdc/mit-kdb/kdb_samba_policies.c >+++ b/source4/kdc/mit-kdb/kdb_samba_policies.c >@@ -4,7 +4,7 @@ > Samba KDB plugin for MIT Kerberos > > Copyright (c) 2010 Simo Sorce <idra@samba.org>. >- Copyright (c) 2014 Andreas Schneider <asn@samba.org> >+ Copyright (c) 2014-2021 Andreas Schneider <asn@samba.org> > > 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 >@@ -311,11 +311,16 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context, > { > krb5_const_principal ks_client_princ = NULL; > krb5_db_entry *client_entry = NULL; >+ krb5_authdata **pac_auth_data = NULL; > krb5_authdata **authdata = NULL; > krb5_boolean is_as_req; > krb5_error_code code; > krb5_pac pac = NULL; > krb5_data pac_data; >+ bool with_pac = false; >+ bool generate_pac = false; >+ char *client_name = NULL; >+ > > krbtgt = krbtgt == NULL ? local_krbtgt : krbtgt; > krbtgt_key = krbtgt_key == NULL ? local_krbtgt_key : krbtgt_key; >@@ -358,8 +363,6 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context, > 0, > &client_entry); > if (code != 0) { >- char *client_name = NULL; >- > (void)krb5_unparse_name(context, > ks_client_princ, > &client_name); >@@ -391,43 +394,105 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context, > client_entry = client; > } > >- if (is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC)) { >+ if (is_as_req) { >+ with_pac = mit_samba_princ_needs_pac(client_entry); >+ } else { >+ with_pac = mit_samba_princ_needs_pac(server); >+ } >+ >+ code = krb5_unparse_name(context, >+ client_princ, >+ &client_name); >+ if (code != 0) { >+ goto done; >+ } >+ >+ if (is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC) != 0) { >+ generate_pac = true; >+ } >+ >+ DBG_DEBUG("*** Sign data for client principal: %s [%s %s%s]\n", >+ client_name, >+ is_as_req ? "AS-REQ" : "TGS_REQ", >+ with_pac ? is_as_req ? "WITH_PAC" : "FIND_PAC" : "NO_PAC", >+ generate_pac ? " GENERATE_PAC" : ""); >+ >+ /* >+ * Generate PAC for the AS-REQ or check or generate one for the TGS if >+ * needed. >+ */ >+ if (with_pac && generate_pac) { >+ DBG_DEBUG("Generate PAC for AS-REQ [%s]\n", client_name); > code = ks_get_pac(context, client_entry, client_key, &pac); > if (code != 0) { > goto done; > } >- } >- >- if (!is_as_req) { >- code = ks_verify_pac(context, >- flags, >- ks_client_princ, >- client_entry, >- server, >- krbtgt, >- server_key, >- krbtgt_key, >- authtime, >- tgt_auth_data, >- &pac); >+ } else if (with_pac && !is_as_req) { >+ /* >+ * Find the PAC in the TGS, if one exists. >+ */ >+ code = krb5_find_authdata(context, >+ tgt_auth_data, >+ NULL, >+ KRB5_AUTHDATA_WIN2K_PAC, >+ &pac_auth_data); > if (code != 0) { >+ DBG_ERR("krb5_find_authdata failed: %d\n", code); > goto done; > } >- } >+ DBG_DEBUG("Found PAC data for TGS-REQ [%s]\n", client_name); > >- if (pac == NULL) { >+ if (pac_auth_data != NULL && pac_auth_data[0] != NULL) { >+ if (pac_auth_data[1] != NULL) { >+ DBG_ERR("Invalid PAC data!\n"); >+ code = KRB5KDC_ERR_BADOPTION; >+ goto done; >+ } > >- code = ks_get_pac(context, client_entry, client_key, &pac); >- if (code != 0) { >- goto done; >+ DBG_DEBUG("Verify PAC for TGS [%s]\n", >+ client_name); >+ >+ code = ks_verify_pac(context, >+ flags, >+ ks_client_princ, >+ client_entry, >+ server, >+ krbtgt, >+ server_key, >+ krbtgt_key, >+ authtime, >+ tgt_auth_data, >+ &pac); >+ if (code != 0) { >+ goto done; >+ } >+ } else { >+ if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) { >+ DBG_DEBUG("Generate PAC for constrained" >+ "delegation TGS [%s]\n", >+ client_name); >+ >+ code = ks_get_pac(context, >+ client_entry, >+ client_key, >+ &pac); >+ if (code != 0 && code != ENOENT) { >+ goto done; >+ } >+ } > } > } > > if (pac == NULL) { >- code = KRB5_KDB_DBTYPE_NOSUP; >+ DBG_DEBUG("No PAC data - we're done [%s]\n", client_name); >+ *signed_auth_data = NULL; >+ code = 0; > goto done; > } > >+ DBG_DEBUG("Signing PAC for %s [%s]\n", >+ is_as_req ? "AS-REQ" : "TGS-REQ", >+ client_name); > code = krb5_pac_sign(context, pac, authtime, ks_client_princ, > server_key, krbtgt_key, &pac_data); > if (code != 0) { >@@ -465,8 +530,9 @@ done: > if (client_entry != NULL && client_entry != client) { > ks_free_principal(context, client_entry); > } >- krb5_pac_free(context, pac); >+ SAFE_FREE(client_name); > krb5_free_authdata(context, authdata); >+ krb5_pac_free(context, pac); > > return code; > } >-- >2.25.1 > > >From 491d34d0c8887a99be1799b4cf42aca7706f8b6e Mon Sep 17 00:00:00 2001 >From: Andreas Schneider <asn@samba.org> >Date: Mon, 9 Aug 2021 17:22:52 +0200 >Subject: [PATCH 149/190] mit_samba: The samba_princ_needs_pac check should be > on the server entry > >This does the same check as the hdb plugin now. The client check is already >done earlier. > >Signed-off-by: Andreas Schneider <asn@samba.org> >--- > source4/kdc/mit_samba.c | 12 ++++++++++++ > 1 file changed, 12 insertions(+) > >diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c >index e7fe67241ef..8651fc2979e 100644 >--- a/source4/kdc/mit_samba.c >+++ b/source4/kdc/mit_samba.c >@@ -512,6 +512,7 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, > DATA_BLOB *deleg_blob = NULL; > struct samba_kdc_entry *client_skdc_entry = NULL; > struct samba_kdc_entry *krbtgt_skdc_entry = NULL; >+ struct samba_kdc_entry *server_skdc_entry = NULL; > bool is_in_db = false; > bool is_untrusted = false; > size_t num_types = 0; >@@ -525,6 +526,7 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, > ssize_t srv_checksum_idx = -1; > ssize_t kdc_checksum_idx = -1; > krb5_pac new_pac = NULL; >+ bool ok; > > if (client != NULL) { > client_skdc_entry = >@@ -536,6 +538,16 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, > return EINVAL; > } > >+ server_skdc_entry = >+ talloc_get_type_abort(server->e_data, >+ struct samba_kdc_entry); >+ >+ /* The account may be set not to want the PAC */ >+ ok = samba_princ_needs_pac(server_skdc_entry); >+ if (!ok) { >+ return EINVAL; >+ } >+ > if (krbtgt == NULL) { > return EINVAL; > } >-- >2.25.1 > > >From bd5b7ae49ce5a46735067e8804bb81b3dab0d5ec Mon Sep 17 00:00:00 2001 >From: Andreas Schneider <asn@samba.org> >Date: Mon, 9 Aug 2021 17:25:53 +0200 >Subject: [PATCH 150/190] mit_samba: Create the talloc context earlier > >Signed-off-by: Andreas Schneider <asn@samba.org> >--- > source4/kdc/mit_samba.c | 20 ++++++++++++-------- > 1 file changed, 12 insertions(+), 8 deletions(-) > >diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c >index 8651fc2979e..eee364fb617 100644 >--- a/source4/kdc/mit_samba.c >+++ b/source4/kdc/mit_samba.c >@@ -528,6 +528,12 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, > krb5_pac new_pac = NULL; > bool ok; > >+ /* Create a memory context early so code can use talloc_stackframe() */ >+ tmp_ctx = talloc_named(ctx, 0, "mit_samba_reget_pac context"); >+ if (tmp_ctx == NULL) { >+ return ENOMEM; >+ } >+ > if (client != NULL) { > client_skdc_entry = > talloc_get_type_abort(client->e_data, >@@ -535,7 +541,8 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, > } > > if (server == NULL) { >- return EINVAL; >+ code = EINVAL; >+ goto done; > } > > server_skdc_entry = >@@ -545,21 +552,18 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, > /* The account may be set not to want the PAC */ > ok = samba_princ_needs_pac(server_skdc_entry); > if (!ok) { >- return EINVAL; >+ code = EINVAL; >+ goto done; > } > > if (krbtgt == NULL) { >- return EINVAL; >+ code = EINVAL; >+ goto done; > } > krbtgt_skdc_entry = > talloc_get_type_abort(krbtgt->e_data, > struct samba_kdc_entry); > >- tmp_ctx = talloc_named(ctx, 0, "mit_samba_reget_pac context"); >- if (tmp_ctx == NULL) { >- return ENOMEM; >- } >- > code = samba_krbtgt_is_in_db(krbtgt_skdc_entry, > &is_in_db, > &is_untrusted); >-- >2.25.1 > > >From 58ade7688323e8548730194f24bd447a3c93a70a Mon Sep 17 00:00:00 2001 >From: Andreas Schneider <asn@samba.org> >Date: Fri, 6 Aug 2021 12:03:49 +0200 >Subject: [PATCH 151/190] s4:kdc: Remove trailing spaces in pac-glue.c > >Signed-off-by: Andreas Schneider <asn@samba.org> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/kdc/pac-glue.c | 6 +++--- > 1 file changed, 3 insertions(+), 3 deletions(-) > >diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c >index 688103d8477..4066389e717 100644 >--- a/source4/kdc/pac-glue.c >+++ b/source4/kdc/pac-glue.c >@@ -575,12 +575,12 @@ int samba_krbtgt_is_in_db(struct samba_kdc_entry *p, > if (!mem_ctx) { > return ENOMEM; > } >- >+ > trust_direction = ldb_msg_find_attr_as_int(p->msg, "trustDirection", 0); > > if (trust_direction != 0) { > /* Domain trust - we cannot check the sig, but we trust it for a correct PAC >- >+ > This is exactly where we should flag for SID > validation when we do inter-foreest trusts > */ >@@ -768,7 +768,7 @@ NTSTATUS samba_kdc_update_pac_blob(TALLOC_CTX *mem_ctx, > return nt_status; > } > >- nt_status = samba_get_logon_info_pac_blob(mem_ctx, >+ nt_status = samba_get_logon_info_pac_blob(mem_ctx, > user_info_dc, pac_blob); > > return nt_status; >-- >2.25.1 > > >From 8fc9ee546b1287084c91a2a2943d11417f4e1225 Mon Sep 17 00:00:00 2001 >From: Andreas Schneider <asn@samba.org> >Date: Mon, 9 Aug 2021 17:19:45 +0200 >Subject: [PATCH 152/190] s4:kdc: Add samba_kdc_validate_pac_blob() > >Signed-off-by: Andreas Schneider <asn@samba.org> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > source4/kdc/pac-glue.c | 55 ++++++++++++++++++++++++++++++++++++++++++ > source4/kdc/pac-glue.h | 5 ++++ > 2 files changed, 60 insertions(+) > >diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c >index 4066389e717..1c6117ad6cb 100644 >--- a/source4/kdc/pac-glue.c >+++ b/source4/kdc/pac-glue.c >@@ -918,3 +918,58 @@ NTSTATUS samba_kdc_check_client_access(struct samba_kdc_entry *kdc_entry, > talloc_free(tmp_ctx); > return nt_status; > } >+ >+krb5_error_code samba_kdc_validate_pac_blob( >+ krb5_context context, >+ struct samba_kdc_entry *client_skdc_entry, >+ const krb5_pac pac) >+{ >+ TALLOC_CTX *frame = talloc_stackframe(); >+ struct auth_user_info_dc *pac_user_info = NULL; >+ struct dom_sid *client_sid = NULL; >+ struct dom_sid pac_sid; >+ krb5_error_code code; >+ bool ok; >+ >+ code = kerberos_pac_to_user_info_dc(frame, >+ pac, >+ context, >+ &pac_user_info, >+ NULL, >+ NULL); >+ if (code != 0) { >+ goto out; >+ } >+ >+ if (pac_user_info->num_sids == 0) { >+ code = EINVAL; >+ goto out; >+ } >+ >+ pac_sid = pac_user_info->sids[0]; >+ client_sid = samdb_result_dom_sid(frame, >+ client_skdc_entry->msg, >+ "objectSid"); >+ >+ ok = dom_sid_equal(&pac_sid, client_sid); >+ if (!ok) { >+ struct dom_sid_buf buf1; >+ struct dom_sid_buf buf2; >+ >+ DBG_ERR("SID mismatch between PAC and looked up client: " >+ "PAC[%s] != CLI[%s]\n", >+ dom_sid_str_buf(&pac_sid, &buf1), >+ dom_sid_str_buf(client_sid, &buf2)); >+#if defined(KRB5KDC_ERR_CLIENT_NAME_MISMATCH) /* MIT */ >+ code = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; >+#else /* Heimdal (where this is an enum) */ >+ code = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; >+#endif >+ goto out; >+ } >+ >+ code = 0; >+out: >+ TALLOC_FREE(frame); >+ return code; >+} >diff --git a/source4/kdc/pac-glue.h b/source4/kdc/pac-glue.h >index 7b51b0389f5..e83446647b3 100644 >--- a/source4/kdc/pac-glue.h >+++ b/source4/kdc/pac-glue.h >@@ -69,3 +69,8 @@ NTSTATUS samba_kdc_check_client_access(struct samba_kdc_entry *kdc_entry, > const char *client_name, > const char *workstation, > bool password_change); >+ >+krb5_error_code samba_kdc_validate_pac_blob( >+ krb5_context context, >+ struct samba_kdc_entry *client_skdc_entry, >+ const krb5_pac pac); >-- >2.25.1 > > >From 913b6a8f38ed1874ae68e5511ce90c937f1460ba Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Thu, 28 Oct 2021 11:30:40 +1300 >Subject: [PATCH 153/190] fix > >--- > source4/kdc/pac-glue.c | 1 + > 1 file changed, 1 insertion(+) > >diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c >index 1c6117ad6cb..8a3ec22190c 100644 >--- a/source4/kdc/pac-glue.c >+++ b/source4/kdc/pac-glue.c >@@ -919,6 +919,7 @@ NTSTATUS samba_kdc_check_client_access(struct samba_kdc_entry *kdc_entry, > return nt_status; > } > >+/* Does a parse and SID check, but no crypto. */ > krb5_error_code samba_kdc_validate_pac_blob( > krb5_context context, > struct samba_kdc_entry *client_skdc_entry, >-- >2.25.1 > > >From 5a1bae820947a89af83996c425cc820de5f8e134 Mon Sep 17 00:00:00 2001 >From: Andreas Schneider <asn@samba.org> >Date: Mon, 9 Aug 2021 17:20:31 +0200 >Subject: [PATCH 154/190] s4:kdc: Check if the pac is valid before updating it > >Signed-off-by: Andreas Schneider <asn@samba.org> >Reviewed-by: Andrew Bartlett <abartlet@samba.org> >--- > selftest/knownfail_heimdal_kdc | 31 ++++--------------------------- > selftest/knownfail_mit_kdc | 10 ++-------- > source4/kdc/mit_samba.c | 8 ++++++++ > source4/kdc/wdc-samba4.c | 16 ++++++++++++++++ > 4 files changed, 30 insertions(+), 35 deletions(-) > >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index f8a5d8bf7da..d0333880012 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -109,13 +109,6 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_service_no_auth_data_required > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_request_no_pac > # >-# Alias tests >-# >-^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_delete >-^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_rename >-^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_delete >-^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_rename >-# > # KDC TGT tests > # > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_authdata_no_pac >@@ -126,10 +119,6 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_no_partial_secrets > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_not_allowed > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_not_revealed >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_allowed_denied >@@ -138,10 +127,6 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_no_partial_secrets > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_not_allowed > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_not_revealed >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_nonexisting >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_allowed_denied >@@ -150,10 +135,6 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_no_partial_secrets > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_not_allowed > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_not_revealed >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_mac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_upn_mac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_upn_user >@@ -192,10 +173,6 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_no_partial_secrets > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_not_allowed > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_not_revealed >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_nonexisting > # > # PAC attributes tests > # >@@ -230,8 +207,8 @@ > # PAC requester SID tests > # > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_as_requester_sid >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid >@@ -240,8 +217,8 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_renew > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_renew > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_nonexisting > # >diff --git a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc >index 10c41c51fd0..f67f6bfe187 100644 >--- a/selftest/knownfail_mit_kdc >+++ b/selftest/knownfail_mit_kdc >@@ -385,8 +385,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_revealed > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_authdata_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_req >@@ -412,8 +410,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_revealed > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_mac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_upn_mac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_upn_dns_info_ex_upn_user >@@ -453,8 +449,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_nonexisting > # > # PAC attributes tests > # >@@ -496,8 +490,8 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_ > # PAC requester SID tests > # > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_as_requester_sid >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_existing >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_sid_mismatch_existing >+^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_existing > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_nonexisting > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid >diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c >index eee364fb617..be894f60aa7 100644 >--- a/source4/kdc/mit_samba.c >+++ b/source4/kdc/mit_samba.c >@@ -538,6 +538,14 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, > client_skdc_entry = > talloc_get_type_abort(client->e_data, > struct samba_kdc_entry); >+ >+ /* >+ * Check the objectSID of the client and pac data are the same. >+ */ >+ code = samba_kdc_validate_pac_blob(context, client_skdc_entry, *pac); >+ if (code != 0) { >+ goto done; >+ } > } > > if (server == NULL) { >diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c >index ac9d7d51733..4e60044997c 100644 >--- a/source4/kdc/wdc-samba4.c >+++ b/source4/kdc/wdc-samba4.c >@@ -137,6 +137,22 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context, > return ENOMEM; > } > >+ if (client != NULL) { >+ struct samba_kdc_entry *client_skdc_entry = NULL; >+ >+ client_skdc_entry = talloc_get_type_abort(client->ctx, >+ struct samba_kdc_entry); >+ >+ /* >+ * Check the objectSID of the client and pac data are the same. >+ */ >+ ret = samba_kdc_validate_pac_blob(context, client_skdc_entry, *pac); >+ if (ret != 0) { >+ talloc_free(mem_ctx); >+ return ret; >+ } >+ } >+ > /* If the krbtgt was generated by an RODC, and we are not that > * RODC, then we need to regenerate the PAC - we can't trust > * it */ >-- >2.25.1 > > >From ade08022287f7a175ac18652aa40a9a0cdf43de1 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Thu, 28 Oct 2021 11:31:38 +1300 >Subject: [PATCH 155/190] fix > >--- > source4/kdc/mit_samba.c | 1 + > source4/kdc/wdc-samba4.c | 1 + > 2 files changed, 2 insertions(+) > >diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c >index be894f60aa7..a0c5015b655 100644 >--- a/source4/kdc/mit_samba.c >+++ b/source4/kdc/mit_samba.c >@@ -541,6 +541,7 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, > > /* > * Check the objectSID of the client and pac data are the same. >+ * Does a parse and SID check, but no crypto. > */ > code = samba_kdc_validate_pac_blob(context, client_skdc_entry, *pac); > if (code != 0) { >diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c >index 4e60044997c..ed6e9fb9b63 100644 >--- a/source4/kdc/wdc-samba4.c >+++ b/source4/kdc/wdc-samba4.c >@@ -145,6 +145,7 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context, > > /* > * Check the objectSID of the client and pac data are the same. >+ * Does a parse and SID check, but no crypto. > */ > ret = samba_kdc_validate_pac_blob(context, client_skdc_entry, *pac); > if (ret != 0) { >-- >2.25.1 > > >From b79e3902baae1b929555d6a08ea84676ef7061f8 Mon Sep 17 00:00:00 2001 >From: Joseph Sutton <josephsutton@catalyst.net.nz> >Date: Tue, 26 Oct 2021 20:41:31 +1300 >Subject: [PATCH 156/190] s4:kdc: Add KDC support for PAC_ATTRIBUTES_INFO PAC > buffer > >Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> >--- > selftest/knownfail_heimdal_kdc | 23 ----- > source4/heimdal/kdc/kerberos5.c | 23 +++-- > source4/heimdal/kdc/krb5tgs.c | 2 +- > source4/heimdal/kdc/windc.c | 7 +- > source4/heimdal/kdc/windc_plugin.h | 2 + > source4/kdc/mit_samba.c | 7 +- > source4/kdc/pac-glue.c | 147 ++++++++++++++++++++++++++++- > source4/kdc/pac-glue.h | 10 +- > source4/kdc/wdc-samba4.c | 45 ++++++++- > 9 files changed, 223 insertions(+), 43 deletions(-) > >diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc >index d0333880012..6e9f8ff8b94 100644 >--- a/selftest/knownfail_heimdal_kdc >+++ b/selftest/knownfail_heimdal_kdc >@@ -103,11 +103,9 @@ > # > # KDC TGS PAC tests > # >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_no_pac_service_no_auth_data_required > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_client_no_auth_data_required > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_service_no_auth_data_required >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_request_no_pac > # > # KDC TGT tests > # >@@ -174,27 +172,6 @@ > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_not_allowed > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_not_revealed > # >-# PAC attributes tests >-# >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_false >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_false >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_none >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_true >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_false >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_none >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_true >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_none >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_false >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_none >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_true >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_false >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_none >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_true >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_true >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_false >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_none >-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_true >-# > # PAC request tests > # > ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_pac_request_false >diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c >index ec0c5ade153..b8fec62333d 100644 >--- a/source4/heimdal/kdc/kerberos5.c >+++ b/source4/heimdal/kdc/kerberos5.c >@@ -914,27 +914,30 @@ _kdc_check_addresses(krb5_context context, > */ > > static krb5_boolean >-send_pac_p(krb5_context context, KDC_REQ *req) >+send_pac_p(krb5_context context, KDC_REQ *req, krb5_boolean *pac_request) > { > krb5_error_code ret; > PA_PAC_REQUEST pacreq; > const PA_DATA *pa; > int i = 0; > >+ *pac_request = TRUE; >+ > pa = _kdc_find_padata(req, &i, KRB5_PADATA_PA_PAC_REQUEST); > if (pa == NULL) >- return TRUE; >+ return FALSE; > > ret = decode_PA_PAC_REQUEST(pa->padata_value.data, > pa->padata_value.length, > &pacreq, > NULL); > if (ret) >- return TRUE; >+ return FALSE; > i = pacreq.include_pac; > free_PA_PAC_REQUEST(&pacreq); >- if (i == 0) >- return FALSE; >+ if (i == 0) { >+ *pac_request = FALSE; >+ } > return TRUE; > } > >@@ -1758,13 +1761,19 @@ _kdc_as_rep(krb5_context context, > } > > /* Add the PAC */ >- if (send_pac_p(context, req)) { >+ { > krb5_pac p = NULL; > krb5_data data; > uint16_t rodc_id; > krb5_principal client_pac; >+ krb5_boolean sent_pac_request; >+ krb5_boolean pac_request; >+ >+ sent_pac_request = send_pac_p(context, req, &pac_request); > >- ret = _kdc_pac_generate(context, client, pk_reply_key, &p); >+ ret = _kdc_pac_generate(context, client, pk_reply_key, >+ sent_pac_request ? &pac_request : NULL, >+ &p); > if (ret) { > kdc_log(context, config, 0, "PAC generation failed for -- %s", > client_name); >diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c >index 7e9379db64a..301ca92091a 100644 >--- a/source4/heimdal/kdc/krb5tgs.c >+++ b/source4/heimdal/kdc/krb5tgs.c >@@ -1762,7 +1762,7 @@ server_lookup: > if (mspac) { > krb5_pac_free(context, mspac); > mspac = NULL; >- ret = _kdc_pac_generate(context, s4u2self_impersonated_client, NULL, &mspac); >+ ret = _kdc_pac_generate(context, s4u2self_impersonated_client, NULL, NULL, &mspac); > if (ret) { > kdc_log(context, config, 0, "PAC generation failed for -- %s", > tpn); >diff --git a/source4/heimdal/kdc/windc.c b/source4/heimdal/kdc/windc.c >index 43dc89e2bc0..93b973f576b 100644 >--- a/source4/heimdal/kdc/windc.c >+++ b/source4/heimdal/kdc/windc.c >@@ -74,6 +74,7 @@ krb5_error_code > _kdc_pac_generate(krb5_context context, > hdb_entry_ex *client, > const krb5_keyblock *pk_reply_key, >+ const krb5_boolean *pac_request, > krb5_pac *pac) > { > *pac = NULL; >@@ -87,8 +88,10 @@ _kdc_pac_generate(krb5_context context, > > if (windcft->pac_pk_generate != NULL && pk_reply_key != NULL) > return (windcft->pac_pk_generate)(windcctx, context, >- client, pk_reply_key, pac); >- return (windcft->pac_generate)(windcctx, context, client, pac); >+ client, pk_reply_key, >+ pac_request, pac); >+ return (windcft->pac_generate)(windcctx, context, client, >+ pac_request, pac); > } > > krb5_error_code >diff --git a/source4/heimdal/kdc/windc_plugin.h b/source4/heimdal/kdc/windc_plugin.h >index dda258da3d1..c7f2bcb5ed9 100644 >--- a/source4/heimdal/kdc/windc_plugin.h >+++ b/source4/heimdal/kdc/windc_plugin.h >@@ -55,12 +55,14 @@ struct hdb_entry_ex; > typedef krb5_error_code > (*krb5plugin_windc_pac_generate)(void *, krb5_context, > struct hdb_entry_ex *, /* client */ >+ const krb5_boolean *, /* pac_request */ > krb5_pac *); > > typedef krb5_error_code > (*krb5plugin_windc_pac_pk_generate)(void *, krb5_context, > struct hdb_entry_ex *, /* client */ > const krb5_keyblock *, /* pk_replykey */ >+ const krb5_boolean *, /* pac_request */ > krb5_pac *); > > typedef krb5_error_code >diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c >index a0c5015b655..1668d11fc7f 100644 >--- a/source4/kdc/mit_samba.c >+++ b/source4/kdc/mit_samba.c >@@ -460,7 +460,8 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx, > skdc_entry, > &logon_info_blob, > cred_ndr_ptr, >- &upn_dns_info_blob); >+ &upn_dns_info_blob, >+ NULL, NULL); > if (!NT_STATUS_IS_OK(nt_status)) { > talloc_free(tmp_ctx); > if (NT_STATUS_EQUAL(nt_status, >@@ -488,6 +489,7 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx, > pcred_blob, > upn_dns_info_blob, > NULL, >+ NULL, > pac); > > talloc_free(tmp_ctx); >@@ -590,7 +592,8 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx, > client_skdc_entry, > &pac_blob, > NULL, >- &upn_blob); >+ &upn_blob, >+ NULL, NULL); > if (!NT_STATUS_IS_OK(nt_status)) { > code = EINVAL; > goto done; >diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c >index 8a3ec22190c..06019e579eb 100644 >--- a/source4/kdc/pac-glue.c >+++ b/source4/kdc/pac-glue.c >@@ -113,6 +113,43 @@ NTSTATUS samba_get_upn_info_pac_blob(TALLOC_CTX *mem_ctx, > return NT_STATUS_OK; > } > >+static >+NTSTATUS samba_get_pac_attrs_blob(TALLOC_CTX *mem_ctx, >+ const krb5_boolean *pac_request, >+ DATA_BLOB *pac_attrs_data) >+{ >+ union PAC_INFO pac_attrs; >+ enum ndr_err_code ndr_err; >+ NTSTATUS nt_status; >+ >+ ZERO_STRUCT(pac_attrs); >+ >+ *pac_attrs_data = data_blob_null; >+ >+ /* Set the length of the flags in bits. */ >+ pac_attrs.attributes_info.flags_length = 2; >+ >+ if (pac_request == NULL) { >+ pac_attrs.attributes_info.flags >+ |= PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY; >+ } else if (*pac_request) { >+ pac_attrs.attributes_info.flags >+ |= PAC_ATTRIBUTE_FLAG_PAC_WAS_REQUESTED; >+ } >+ >+ ndr_err = ndr_push_union_blob(pac_attrs_data, mem_ctx, &pac_attrs, >+ PAC_TYPE_ATTRIBUTES_INFO, >+ (ndr_push_flags_fn_t)ndr_push_PAC_INFO); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ nt_status = ndr_map_error2ntstatus(ndr_err); >+ DEBUG(1, ("PAC ATTRIBUTES_INFO (presig) push failed: %s\n", >+ nt_errstr(nt_status))); >+ return nt_status; >+ } >+ >+ return NT_STATUS_OK; >+} >+ > static > NTSTATUS samba_get_cred_info_ndr_blob(TALLOC_CTX *mem_ctx, > const struct ldb_message *msg, >@@ -413,12 +450,14 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, > const DATA_BLOB *logon_blob, > const DATA_BLOB *cred_blob, > const DATA_BLOB *upn_blob, >+ const DATA_BLOB *pac_attrs_blob, > const DATA_BLOB *deleg_blob, > krb5_pac *pac) > { > krb5_data logon_data; > krb5_data cred_data; > krb5_data upn_data; >+ krb5_data pac_attrs_data; > krb5_data deleg_data; > krb5_error_code ret; > #ifdef SAMBA4_USES_HEIMDAL >@@ -463,6 +502,19 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, > } > } > >+ ZERO_STRUCT(pac_attrs_data); >+ if (pac_attrs_blob != NULL) { >+ ret = smb_krb5_copy_data_contents(&pac_attrs_data, >+ pac_attrs_blob->data, >+ pac_attrs_blob->length); >+ if (ret != 0) { >+ smb_krb5_free_data_contents(context, &logon_data); >+ smb_krb5_free_data_contents(context, &cred_data); >+ smb_krb5_free_data_contents(context, &upn_data); >+ return ret; >+ } >+ } >+ > ZERO_STRUCT(deleg_data); > if (deleg_blob != NULL) { > ret = smb_krb5_copy_data_contents(&deleg_data, >@@ -472,6 +524,7 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, > smb_krb5_free_data_contents(context, &logon_data); > smb_krb5_free_data_contents(context, &cred_data); > smb_krb5_free_data_contents(context, &upn_data); >+ smb_krb5_free_data_contents(context, &pac_attrs_data); > return ret; > } > } >@@ -481,6 +534,7 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, > smb_krb5_free_data_contents(context, &logon_data); > smb_krb5_free_data_contents(context, &cred_data); > smb_krb5_free_data_contents(context, &upn_data); >+ smb_krb5_free_data_contents(context, &pac_attrs_data); > smb_krb5_free_data_contents(context, &deleg_data); > return ret; > } >@@ -488,8 +542,9 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, > ret = krb5_pac_add_buffer(context, *pac, PAC_TYPE_LOGON_INFO, &logon_data); > smb_krb5_free_data_contents(context, &logon_data); > if (ret != 0) { >- smb_krb5_free_data_contents(context, &upn_data); > smb_krb5_free_data_contents(context, &cred_data); >+ smb_krb5_free_data_contents(context, &upn_data); >+ smb_krb5_free_data_contents(context, &pac_attrs_data); > smb_krb5_free_data_contents(context, &deleg_data); > return ret; > } >@@ -501,6 +556,7 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, > smb_krb5_free_data_contents(context, &cred_data); > if (ret != 0) { > smb_krb5_free_data_contents(context, &upn_data); >+ smb_krb5_free_data_contents(context, &pac_attrs_data); > smb_krb5_free_data_contents(context, &deleg_data); > return ret; > } >@@ -519,6 +575,7 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, > &null_data); > if (ret != 0) { > smb_krb5_free_data_contents(context, &upn_data); >+ smb_krb5_free_data_contents(context, &pac_attrs_data); > smb_krb5_free_data_contents(context, &deleg_data); > return ret; > } >@@ -529,6 +586,18 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, > PAC_TYPE_UPN_DNS_INFO, > &upn_data); > smb_krb5_free_data_contents(context, &upn_data); >+ if (ret != 0) { >+ smb_krb5_free_data_contents(context, &pac_attrs_data); >+ smb_krb5_free_data_contents(context, &deleg_data); >+ return ret; >+ } >+ } >+ >+ if (pac_attrs_blob != NULL) { >+ ret = krb5_pac_add_buffer(context, *pac, >+ PAC_TYPE_ATTRIBUTES_INFO, >+ &pac_attrs_data); >+ smb_krb5_free_data_contents(context, &pac_attrs_data); > if (ret != 0) { > smb_krb5_free_data_contents(context, &deleg_data); > return ret; >@@ -562,6 +631,48 @@ bool samba_princ_needs_pac(struct samba_kdc_entry *skdc_entry) > return true; > } > >+int samba_client_requested_pac(krb5_context context, >+ krb5_pac *pac, >+ TALLOC_CTX *mem_ctx, >+ bool *requested_pac) >+{ >+ enum ndr_err_code ndr_err; >+ krb5_data k5pac_attrs_in; >+ DATA_BLOB pac_attrs_in; >+ union PAC_INFO pac_attrs; >+ int ret; >+ >+ *requested_pac = true; >+ >+ ret = krb5_pac_get_buffer(context, *pac, PAC_TYPE_ATTRIBUTES_INFO, >+ &k5pac_attrs_in); >+ if (ret != 0) { >+ return ret == ENOENT ? 0 : ret; >+ } >+ >+ pac_attrs_in = data_blob_const(k5pac_attrs_in.data, >+ k5pac_attrs_in.length); >+ >+ ndr_err = ndr_pull_union_blob(&pac_attrs_in, mem_ctx, &pac_attrs, >+ PAC_TYPE_ATTRIBUTES_INFO, >+ (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO); >+ smb_krb5_free_data_contents(context, &k5pac_attrs_in); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err); >+ DEBUG(0,("can't parse the PAC ATTRIBUTES_INFO: %s\n", nt_errstr(nt_status))); >+ return EINVAL; >+ } >+ >+ if (pac_attrs.attributes_info.flags & (PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY >+ | PAC_ATTRIBUTE_FLAG_PAC_WAS_REQUESTED)) { >+ *requested_pac = true; >+ } else { >+ *requested_pac = false; >+ } >+ >+ return 0; >+} >+ > /* Was the krbtgt in this DB (ie, should we check the incoming signature) and was it an RODC */ > int samba_krbtgt_is_in_db(struct samba_kdc_entry *p, > bool *is_in_db, >@@ -637,12 +748,15 @@ NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx, > struct samba_kdc_entry *p, > DATA_BLOB **_logon_info_blob, > DATA_BLOB **_cred_ndr_blob, >- DATA_BLOB **_upn_info_blob) >+ DATA_BLOB **_upn_info_blob, >+ DATA_BLOB **_pac_attrs_blob, >+ const krb5_boolean *pac_request) > { > struct auth_user_info_dc *user_info_dc; > DATA_BLOB *logon_blob = NULL; > DATA_BLOB *cred_blob = NULL; > DATA_BLOB *upn_blob = NULL; >+ DATA_BLOB *pac_attrs_blob = NULL; > NTSTATUS nt_status; > > *_logon_info_blob = NULL; >@@ -650,6 +764,9 @@ NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx, > *_cred_ndr_blob = NULL; > } > *_upn_info_blob = NULL; >+ if (_pac_attrs_blob != NULL) { >+ *_pac_attrs_blob = NULL; >+ } > > logon_blob = talloc_zero(mem_ctx, DATA_BLOB); > if (logon_blob == NULL) { >@@ -668,6 +785,13 @@ NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx, > return NT_STATUS_NO_MEMORY; > } > >+ if (_pac_attrs_blob != NULL) { >+ pac_attrs_blob = talloc_zero(mem_ctx, DATA_BLOB); >+ if (pac_attrs_blob == NULL) { >+ return NT_STATUS_NO_MEMORY; >+ } >+ } >+ > nt_status = authsam_make_user_info_dc(mem_ctx, p->kdc_db_ctx->samdb, > lpcfg_netbios_name(p->kdc_db_ctx->lp_ctx), > lpcfg_sam_name(p->kdc_db_ctx->lp_ctx), >@@ -712,12 +836,27 @@ NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx, > return nt_status; > } > >+ if (pac_attrs_blob != NULL) { >+ nt_status = samba_get_pac_attrs_blob(pac_attrs_blob, >+ pac_request, >+ pac_attrs_blob); >+ >+ if (!NT_STATUS_IS_OK(nt_status)) { >+ DEBUG(0, ("Building PAC ATTRIBUTES failed: %s\n", >+ nt_errstr(nt_status))); >+ return nt_status; >+ } >+ } >+ > TALLOC_FREE(user_info_dc); > *_logon_info_blob = logon_blob; > if (_cred_ndr_blob != NULL) { > *_cred_ndr_blob = cred_blob; > } > *_upn_info_blob = upn_blob; >+ if (_pac_attrs_blob != NULL) { >+ *_pac_attrs_blob = pac_attrs_blob; >+ } > return NT_STATUS_OK; > } > >@@ -731,7 +870,9 @@ NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx, > nt_status = samba_kdc_get_pac_blobs(mem_ctx, p, > _logon_info_blob, > NULL, /* cred_blob */ >- &upn_blob); >+ &upn_blob, >+ NULL, >+ NULL); > if (!NT_STATUS_IS_OK(nt_status)) { > return nt_status; > } >diff --git a/source4/kdc/pac-glue.h b/source4/kdc/pac-glue.h >index e83446647b3..1b6264cb2c3 100644 >--- a/source4/kdc/pac-glue.h >+++ b/source4/kdc/pac-glue.h >@@ -31,11 +31,17 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, > const DATA_BLOB *logon_blob, > const DATA_BLOB *cred_blob, > const DATA_BLOB *upn_blob, >+ const DATA_BLOB *pac_attrs_blob, > const DATA_BLOB *deleg_blob, > krb5_pac *pac); > > bool samba_princ_needs_pac(struct samba_kdc_entry *skdc_entry); > >+int samba_client_requested_pac(krb5_context context, >+ krb5_pac *pac, >+ TALLOC_CTX *mem_ctx, >+ bool *requested_pac); >+ > int samba_krbtgt_is_in_db(struct samba_kdc_entry *skdc_entry, > bool *is_in_db, > bool *is_untrusted); >@@ -44,7 +50,9 @@ NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx, > struct samba_kdc_entry *skdc_entry, > DATA_BLOB **_logon_info_blob, > DATA_BLOB **_cred_ndr_blob, >- DATA_BLOB **_upn_info_blob); >+ DATA_BLOB **_upn_info_blob, >+ DATA_BLOB **_pac_attrs_blob, >+ const krb5_boolean *pac_request); > NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx, > struct samba_kdc_entry *skdc_entry, > DATA_BLOB **_logon_info_blob); >diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c >index ed6e9fb9b63..11d9ff84f04 100644 >--- a/source4/kdc/wdc-s