diff --git a/backport-0001-CVE-2022-32743-s4-acl-Add-tests-for-validated-dNSHos.patch b/backport-0001-CVE-2022-32743-s4-acl-Add-tests-for-validated-dNSHos.patch new file mode 100644 index 0000000000000000000000000000000000000000..2fc183646add976a3ab04a4d2fe74096986d1220 --- /dev/null +++ b/backport-0001-CVE-2022-32743-s4-acl-Add-tests-for-validated-dNSHos.patch @@ -0,0 +1,815 @@ +From d277700710dc118f61065ed9e16e08e76820b66a Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Wed, 1 Jun 2022 16:07:17 +1200 +Subject: [PATCH 01/15] CVE-2022-32743 s4-acl: Add tests for validated + dNSHostName write + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + selftest/knownfail.d/validated-dns-host-name | 15 + + source4/dsdb/tests/python/acl.py | 757 +++++++++++++++++++++++++++ + 2 files changed, 772 insertions(+) + create mode 100644 selftest/knownfail.d/validated-dns-host-name + +diff --git a/selftest/knownfail.d/validated-dns-host-name b/selftest/knownfail.d/validated-dns-host-name +new file mode 100644 +index 0000000..ee51f44 +--- /dev/null ++++ b/selftest/knownfail.d/validated-dns-host-name +@@ -0,0 +1,15 @@ ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_account_no_dollar\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_allowed_suffixes\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_case\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_dollar\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_empty_string\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_invalid\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_no_suffix\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_no_value\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_spn\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_spn_matching_account_name_new\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_spn_matching_account_name_original\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_wrong_prefix\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_wrong_suffix\( ++^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_spn_matching_dns_host_name_invalid\( +diff --git a/source4/dsdb/tests/python/acl.py b/source4/dsdb/tests/python/acl.py +index 0061d0c..6751934 100755 +--- a/source4/dsdb/tests/python/acl.py ++++ b/source4/dsdb/tests/python/acl.py +@@ -300,6 +300,7 @@ class AclModifyTests(AclTests): + delete_force(self.ldb_admin, "CN=test_modify_group1,CN=Users," + self.base_dn) + delete_force(self.ldb_admin, "CN=test_modify_group2,CN=Users," + self.base_dn) + delete_force(self.ldb_admin, "CN=test_modify_group3,CN=Users," + self.base_dn) ++ delete_force(self.ldb_admin, "CN=test_mod_hostname,OU=test_modify_ou1," + self.base_dn) + delete_force(self.ldb_admin, "OU=test_modify_ou1," + self.base_dn) + delete_force(self.ldb_admin, self.get_user_dn(self.user_with_wp)) + delete_force(self.ldb_admin, self.get_user_dn(self.user_with_sm)) +@@ -651,6 +652,762 @@ Member: CN=test_modify_user2,CN=Users,""" + self.base_dn + else: + self.fail() + ++ def test_modify_dns_host_name(self): ++ '''Test modifying dNSHostName with validated write''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ host_name = f'{account_name}.{self.ldb_user.domain_dns_name()}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError: ++ self.fail() ++ ++ def test_modify_dns_host_name_no_validated_write(self): ++ '''Test modifying dNSHostName without validated write''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ host_name = f'{account_name}.{self.ldb_user.domain_dns_name()}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError as err: ++ num, estr = err.args ++ self.assertEqual(ERR_INSUFFICIENT_ACCESS_RIGHTS, num) ++ else: ++ self.fail() ++ ++ def test_modify_dns_host_name_invalid(self): ++ '''Test modifying dNSHostName to an invalid value''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ host_name = 'invalid' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError as err: ++ num, estr = err.args ++ self.assertEqual(ERR_CONSTRAINT_VIOLATION, num) ++ else: ++ self.fail() ++ ++ def test_modify_dns_host_name_invalid_wp(self): ++ '''Test modifying dNSHostName to an invalid value when we have WP''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Write Property. ++ mod = (f'(OA;CI;WP;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ host_name = 'invalid' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError: ++ self.fail() ++ ++ def test_modify_dns_host_name_invalid_non_computer(self): ++ '''Test modifying dNSHostName to an invalid value on a non-computer''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'user', ++ 'sAMAccountName': f'{account_name}', ++ }) ++ ++ host_name = 'invalid' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError as err: ++ num, estr = err.args ++ self.assertEqual(ERR_INSUFFICIENT_ACCESS_RIGHTS, num) ++ else: ++ self.fail() ++ ++ def test_modify_dns_host_name_no_value(self): ++ '''Test modifying dNSHostName with validated write with no value''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement([], ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError as err: ++ num, estr = err.args ++ self.assertEqual(ERR_OPERATIONS_ERROR, num) ++ else: ++ # Windows accepts this. ++ pass ++ ++ def test_modify_dns_host_name_empty_string(self): ++ '''Test modifying dNSHostName with validated write of an empty string''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement('\0', ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError as err: ++ num, estr = err.args ++ self.assertEqual(ERR_CONSTRAINT_VIOLATION, num) ++ else: ++ self.fail() ++ ++ def test_modify_dns_host_name_dollar(self): ++ '''Test modifying dNSHostName with validated write of a value including a dollar''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ host_name = f'{account_name}$.{self.ldb_user.domain_dns_name()}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError as err: ++ num, estr = err.args ++ self.assertEqual(ERR_CONSTRAINT_VIOLATION, num) ++ else: ++ self.fail() ++ ++ def test_modify_dns_host_name_account_no_dollar(self): ++ '''Test modifying dNSHostName with validated write with no dollar in sAMAccountName''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}', ++ }) ++ ++ host_name = f'{account_name}.{self.ldb_user.domain_dns_name()}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError: ++ self.fail() ++ ++ def test_modify_dns_host_name_no_suffix(self): ++ '''Test modifying dNSHostName with validated write of a value missing the suffix''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ host_name = f'{account_name}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError as err: ++ num, estr = err.args ++ self.assertEqual(ERR_CONSTRAINT_VIOLATION, num) ++ else: ++ self.fail() ++ ++ def test_modify_dns_host_name_wrong_prefix(self): ++ '''Test modifying dNSHostName with validated write of a value with the wrong prefix''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ host_name = f'invalid.{self.ldb_user.domain_dns_name()}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError as err: ++ num, estr = err.args ++ self.assertEqual(ERR_CONSTRAINT_VIOLATION, num) ++ else: ++ self.fail() ++ ++ def test_modify_dns_host_name_wrong_suffix(self): ++ '''Test modifying dNSHostName with validated write of a value with the wrong suffix''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ host_name = f'{account_name}.invalid.example.com' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError as err: ++ num, estr = err.args ++ self.assertEqual(ERR_CONSTRAINT_VIOLATION, num) ++ else: ++ self.fail() ++ ++ def test_modify_dns_host_name_case(self): ++ '''Test modifying dNSHostName with validated write of a value with irregular case''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ host_name = f'{account_name}.{self.ldb_user.domain_dns_name()}' ++ host_name = host_name.capitalize() ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError: ++ self.fail() ++ ++ def test_modify_dns_host_name_allowed_suffixes(self): ++ '''Test modifying dNSHostName with validated write and an allowed suffix''' ++ ++ allowed_suffix = 'suffix.that.is.allowed' ++ ++ # Add the allowed suffix. ++ ++ res = self.ldb_admin.search(self.base_dn, ++ scope=SCOPE_BASE, ++ attrs=['msDS-AllowedDNSSuffixes']) ++ self.assertEqual(1, len(res)) ++ old_allowed_suffixes = res[0].get('msDS-AllowedDNSSuffixes') ++ ++ def modify_allowed_suffixes(suffixes): ++ if suffixes is None: ++ suffixes = [] ++ flag = FLAG_MOD_DELETE ++ else: ++ flag = FLAG_MOD_REPLACE ++ ++ m = Message(Dn(self.ldb_admin, self.base_dn)) ++ m['msDS-AllowedDNSSuffixes'] = MessageElement( ++ suffixes, ++ flag, ++ 'msDS-AllowedDNSSuffixes') ++ self.ldb_admin.modify(m) ++ ++ self.addCleanup(modify_allowed_suffixes, old_allowed_suffixes) ++ ++ if old_allowed_suffixes is None: ++ allowed_suffixes = [] ++ else: ++ allowed_suffixes = list(old_allowed_suffixes) ++ ++ if (allowed_suffix not in allowed_suffixes and ++ allowed_suffix.encode('utf-8') not in allowed_suffixes): ++ allowed_suffixes.append(allowed_suffix) ++ ++ modify_allowed_suffixes(allowed_suffixes) ++ ++ # Create the account and run the test. ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ host_name = f'{account_name}.{allowed_suffix}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['dNSHostName'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError: ++ self.fail() ++ ++ def test_modify_dns_host_name_spn(self): ++ '''Test modifying dNSHostName and SPN with validated write''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_VALIDATE_SPN};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ host_name = f'{account_name}.{self.ldb_user.domain_dns_name()}' ++ spn = f'host/{host_name}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['0'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ m['1'] = MessageElement(spn, ++ FLAG_MOD_ADD, ++ 'servicePrincipalName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError: ++ self.fail() ++ ++ def test_modify_spn_matching_dns_host_name_invalid(self): ++ '''Test modifying SPN with validated write, matching a valid dNSHostName ''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Write Property. ++ mod = (f'(OA;CI;WP;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_VALIDATE_SPN};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ invalid_host_name = 'invalid' ++ ++ host_name = f'{account_name}.{self.ldb_user.domain_dns_name()}' ++ spn = f'host/{host_name}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['0'] = MessageElement(invalid_host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ m['1'] = MessageElement(spn, ++ FLAG_MOD_ADD, ++ 'servicePrincipalName') ++ m['2'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError: ++ self.fail() ++ ++ def test_modify_spn_matching_dns_host_name_original(self): ++ '''Test modifying SPN with validated write, matching the original dNSHostName ''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_VALIDATE_SPN};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ original_host_name = 'invalid_host_name' ++ original_spn = 'host/{original_host_name}' ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ 'dNSHostName': original_host_name, ++ }) ++ ++ host_name = f'{account_name}.{self.ldb_user.domain_dns_name()}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['0'] = MessageElement(original_spn, ++ FLAG_MOD_ADD, ++ 'servicePrincipalName') ++ m['1'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError as err: ++ num, estr = err.args ++ self.assertEqual(ERR_CONSTRAINT_VIOLATION, num) ++ else: ++ self.fail() ++ ++ def test_modify_dns_host_name_spn_matching_account_name_original(self): ++ '''Test modifying dNSHostName and SPN with validated write, matching the original sAMAccountName''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ sam_account_name = '3e0abfd0-126a-11d0-a060-00aa006c33ed' ++ ++ # Grant Write Property. ++ mod = (f'(OA;CI;WP;{sam_account_name};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_VALIDATE_SPN};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ new_account_name = 'test_mod_hostname2' ++ host_name = f'{account_name}.{self.ldb_user.domain_dns_name()}' ++ spn = f'host/{host_name}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['0'] = MessageElement(host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ m['1'] = MessageElement(spn, ++ FLAG_MOD_ADD, ++ 'servicePrincipalName') ++ m['2'] = MessageElement(f'{new_account_name}$', ++ FLAG_MOD_REPLACE, ++ 'sAMAccountName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError as err: ++ num, estr = err.args ++ self.assertEqual(ERR_CONSTRAINT_VIOLATION, num) ++ else: ++ self.fail() ++ ++ def test_modify_dns_host_name_spn_matching_account_name_new(self): ++ '''Test modifying dNSHostName and SPN with validated write, matching the new sAMAccountName''' ++ ++ ou_dn = f'OU=test_modify_ou1,{self.base_dn}' ++ ++ account_name = 'test_mod_hostname' ++ dn = f'CN={account_name},{ou_dn}' ++ ++ self.ldb_admin.create_ou(ou_dn) ++ ++ sam_account_name = '3e0abfd0-126a-11d0-a060-00aa006c33ed' ++ ++ # Grant Write Property. ++ mod = (f'(OA;CI;WP;{sam_account_name};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ # Grant Validated Write. ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ mod = (f'(OA;CI;SW;{security.GUID_DRS_VALIDATE_SPN};;' ++ f'{self.user_sid})') ++ self.sd_utils.dacl_add_ace(ou_dn, mod) ++ ++ # Create the account. ++ self.ldb_admin.add({ ++ 'dn': dn, ++ 'objectClass': 'computer', ++ 'sAMAccountName': f'{account_name}$', ++ }) ++ ++ new_account_name = 'test_mod_hostname2' ++ new_host_name = f'{new_account_name}.{self.ldb_user.domain_dns_name()}' ++ new_spn = f'host/{new_host_name}' ++ ++ m = Message(Dn(self.ldb_user, dn)) ++ m['0'] = MessageElement(new_spn, ++ FLAG_MOD_ADD, ++ 'servicePrincipalName') ++ m['1'] = MessageElement(new_host_name, ++ FLAG_MOD_REPLACE, ++ 'dNSHostName') ++ m['2'] = MessageElement(f'{new_account_name}$', ++ FLAG_MOD_REPLACE, ++ 'sAMAccountName') ++ try: ++ self.ldb_user.modify(m) ++ except LdbError: ++ self.fail() ++ + # enable these when we have search implemented + + +-- +1.8.3.1 + diff --git a/backport-0002-CVE-2022-32743-tests-py_credentials-Add-tests-for-se.patch b/backport-0002-CVE-2022-32743-tests-py_credentials-Add-tests-for-se.patch new file mode 100644 index 0000000000000000000000000000000000000000..1321b79c7f26e9afa75a4b3e22f497155b27c3f7 --- /dev/null +++ b/backport-0002-CVE-2022-32743-tests-py_credentials-Add-tests-for-se.patch @@ -0,0 +1,346 @@ +From b41691d0e546795bda994d94091b8e0a03ab96d6 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 7 Jun 2022 17:35:35 +1200 +Subject: [PATCH 02/15] CVE-2022-32743 tests/py_credentials: Add tests for + setting dNSHostName with LogonGetDomainInfo() + +Test that the value is properly validated, and that it can be set +regardless of rights on the account. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + python/samba/tests/py_credentials.py | 281 +++++++++++++++++++++++++++- + selftest/knownfail.d/netlogon-dns-host-name | 2 + + 2 files changed, 281 insertions(+), 2 deletions(-) + create mode 100644 selftest/knownfail.d/netlogon-dns-host-name + +diff --git a/python/samba/tests/py_credentials.py b/python/samba/tests/py_credentials.py +index ecb8271..0c442b8 100644 +--- a/python/samba/tests/py_credentials.py ++++ b/python/samba/tests/py_credentials.py +@@ -18,6 +18,8 @@ + from samba.tests import TestCase, delete_force + import os + ++import ldb ++ + import samba + from samba.auth import system_session + from samba.credentials import ( +@@ -25,7 +27,7 @@ from samba.credentials import ( + CLI_CRED_NTLMv2_AUTH, + CLI_CRED_NTLM_AUTH, + DONT_USE_KERBEROS) +-from samba.dcerpc import netlogon, ntlmssp, srvsvc ++from samba.dcerpc import lsa, netlogon, ntlmssp, security, srvsvc + from samba.dcerpc.netlogon import ( + netr_Authenticator, + netr_WorkstationInformation, +@@ -36,10 +38,11 @@ from samba.dsdb import ( + UF_WORKSTATION_TRUST_ACCOUNT, + UF_PASSWD_NOTREQD, + UF_NORMAL_ACCOUNT) +-from samba.ndr import ndr_pack ++from samba.ndr import ndr_pack, ndr_unpack + from samba.samdb import SamDB + from samba import NTSTATUSError, ntstatus + from samba.compat import get_string ++from samba.sd_utils import SDUtils + + import ctypes + +@@ -105,6 +108,280 @@ class PyCredentialsTests(TestCase): + (authenticator, subsequent) = self.get_authenticator(c) + self.do_NetrLogonGetDomainInfo(c, authenticator, subsequent) + ++ # Test using LogonGetDomainInfo to update dNSHostName to an allowed value. ++ def test_set_dns_hostname_valid(self): ++ c = self.get_netlogon_connection() ++ authenticator, subsequent = self.get_authenticator(c) ++ ++ domain_hostname = self.ldb.domain_dns_name() ++ ++ new_dns_hostname = f'{self.machine_name}.{domain_hostname}' ++ new_dns_hostname = new_dns_hostname.encode('utf-8') ++ ++ query = netr_WorkstationInformation() ++ query.os_name = lsa.String('some OS') ++ query.dns_hostname = new_dns_hostname ++ ++ c.netr_LogonGetDomainInfo( ++ server_name=self.server, ++ computer_name=self.user_creds.get_workstation(), ++ credential=authenticator, ++ return_authenticator=subsequent, ++ level=1, ++ query=query) ++ ++ # Check the result. ++ ++ res = self.ldb.search(self.machine_dn, ++ scope=ldb.SCOPE_BASE, ++ attrs=['dNSHostName']) ++ self.assertEqual(1, len(res)) ++ ++ got_dns_hostname = res[0].get('dNSHostName', idx=0) ++ self.assertEqual(new_dns_hostname, got_dns_hostname) ++ ++ # Test using LogonGetDomainInfo to update dNSHostName to an allowed value, ++ # when we are denied the right to do so. ++ def test_set_dns_hostname_valid_denied(self): ++ c = self.get_netlogon_connection() ++ authenticator, subsequent = self.get_authenticator(c) ++ ++ res = self.ldb.search(self.machine_dn, ++ scope=ldb.SCOPE_BASE, ++ attrs=['objectSid']) ++ self.assertEqual(1, len(res)) ++ ++ machine_sid = ndr_unpack(security.dom_sid, ++ res[0].get('objectSid', idx=0)) ++ ++ sd_utils = SDUtils(self.ldb) ++ ++ # Deny Validated Write and Write Property. ++ mod = (f'(OD;;SWWP;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{machine_sid})') ++ sd_utils.dacl_add_ace(self.machine_dn, mod) ++ ++ domain_hostname = self.ldb.domain_dns_name() ++ ++ new_dns_hostname = f'{self.machine_name}.{domain_hostname}' ++ new_dns_hostname = new_dns_hostname.encode('utf-8') ++ ++ query = netr_WorkstationInformation() ++ query.os_name = lsa.String('some OS') ++ query.dns_hostname = new_dns_hostname ++ ++ c.netr_LogonGetDomainInfo( ++ server_name=self.server, ++ computer_name=self.user_creds.get_workstation(), ++ credential=authenticator, ++ return_authenticator=subsequent, ++ level=1, ++ query=query) ++ ++ # Check the result. ++ ++ res = self.ldb.search(self.machine_dn, ++ scope=ldb.SCOPE_BASE, ++ attrs=['dNSHostName']) ++ self.assertEqual(1, len(res)) ++ ++ got_dns_hostname = res[0].get('dNSHostName', idx=0) ++ self.assertEqual(new_dns_hostname, got_dns_hostname) ++ ++ # Ensure we can't use LogonGetDomainInfo to update dNSHostName to an ++ # invalid value, even with Validated Write. ++ def test_set_dns_hostname_invalid_validated_write(self): ++ c = self.get_netlogon_connection() ++ authenticator, subsequent = self.get_authenticator(c) ++ ++ res = self.ldb.search(self.machine_dn, ++ scope=ldb.SCOPE_BASE, ++ attrs=['objectSid']) ++ self.assertEqual(1, len(res)) ++ ++ machine_sid = ndr_unpack(security.dom_sid, ++ res[0].get('objectSid', idx=0)) ++ ++ sd_utils = SDUtils(self.ldb) ++ ++ # Grant Validated Write. ++ mod = (f'(OA;;SW;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{machine_sid})') ++ sd_utils.dacl_add_ace(self.machine_dn, mod) ++ ++ new_dns_hostname = b'invalid' ++ ++ query = netr_WorkstationInformation() ++ query.os_name = lsa.String('some OS') ++ query.dns_hostname = new_dns_hostname ++ ++ c.netr_LogonGetDomainInfo( ++ server_name=self.server, ++ computer_name=self.user_creds.get_workstation(), ++ credential=authenticator, ++ return_authenticator=subsequent, ++ level=1, ++ query=query) ++ ++ # Check the result. ++ ++ res = self.ldb.search(self.machine_dn, ++ scope=ldb.SCOPE_BASE, ++ attrs=['dNSHostName']) ++ self.assertEqual(1, len(res)) ++ ++ got_dns_hostname = res[0].get('dNSHostName', idx=0) ++ self.assertIsNone(got_dns_hostname) ++ ++ # Ensure we can't use LogonGetDomainInfo to update dNSHostName to an ++ # invalid value, even with Write Property. ++ def test_set_dns_hostname_invalid_write_property(self): ++ c = self.get_netlogon_connection() ++ authenticator, subsequent = self.get_authenticator(c) ++ ++ res = self.ldb.search(self.machine_dn, ++ scope=ldb.SCOPE_BASE, ++ attrs=['objectSid']) ++ self.assertEqual(1, len(res)) ++ ++ machine_sid = ndr_unpack(security.dom_sid, ++ res[0].get('objectSid', idx=0)) ++ ++ sd_utils = SDUtils(self.ldb) ++ ++ # Grant Write Property. ++ mod = (f'(OA;;WP;{security.GUID_DRS_DNS_HOST_NAME};;' ++ f'{machine_sid})') ++ sd_utils.dacl_add_ace(self.machine_dn, mod) ++ ++ new_dns_hostname = b'invalid' ++ ++ query = netr_WorkstationInformation() ++ query.os_name = lsa.String('some OS') ++ query.dns_hostname = new_dns_hostname ++ ++ c.netr_LogonGetDomainInfo( ++ server_name=self.server, ++ computer_name=self.user_creds.get_workstation(), ++ credential=authenticator, ++ return_authenticator=subsequent, ++ level=1, ++ query=query) ++ ++ # Check the result. ++ ++ res = self.ldb.search(self.machine_dn, ++ scope=ldb.SCOPE_BASE, ++ attrs=['dNSHostName']) ++ self.assertEqual(1, len(res)) ++ ++ got_dns_hostname = res[0].get('dNSHostName', idx=0) ++ self.assertIsNone(got_dns_hostname) ++ ++ # Show we can't use LogonGetDomainInfo to set the dNSHostName to just the ++ # machine name. ++ def test_set_dns_hostname_to_machine_name(self): ++ c = self.get_netlogon_connection() ++ authenticator, subsequent = self.get_authenticator(c) ++ ++ new_dns_hostname = self.machine_name.encode('utf-8') ++ ++ query = netr_WorkstationInformation() ++ query.os_name = lsa.String('some OS') ++ query.dns_hostname = new_dns_hostname ++ ++ c.netr_LogonGetDomainInfo( ++ server_name=self.server, ++ computer_name=self.user_creds.get_workstation(), ++ credential=authenticator, ++ return_authenticator=subsequent, ++ level=1, ++ query=query) ++ ++ # Check the result. ++ ++ res = self.ldb.search(self.machine_dn, ++ scope=ldb.SCOPE_BASE, ++ attrs=['dNSHostName']) ++ self.assertEqual(1, len(res)) ++ ++ got_dns_hostname = res[0].get('dNSHostName', idx=0) ++ self.assertIsNone(got_dns_hostname) ++ ++ # Show we can't use LogonGetDomainInfo to set dNSHostName with an invalid ++ # suffix. ++ def test_set_dns_hostname_invalid_suffix(self): ++ c = self.get_netlogon_connection() ++ authenticator, subsequent = self.get_authenticator(c) ++ ++ domain_hostname = self.ldb.domain_dns_name() ++ ++ new_dns_hostname = f'{self.machine_name}.foo.{domain_hostname}' ++ new_dns_hostname = new_dns_hostname.encode('utf-8') ++ ++ query = netr_WorkstationInformation() ++ query.os_name = lsa.String('some OS') ++ query.dns_hostname = new_dns_hostname ++ ++ c.netr_LogonGetDomainInfo( ++ server_name=self.server, ++ computer_name=self.user_creds.get_workstation(), ++ credential=authenticator, ++ return_authenticator=subsequent, ++ level=1, ++ query=query) ++ ++ # Check the result. ++ ++ res = self.ldb.search(self.machine_dn, ++ scope=ldb.SCOPE_BASE, ++ attrs=['dNSHostName']) ++ self.assertEqual(1, len(res)) ++ ++ got_dns_hostname = res[0].get('dNSHostName', idx=0) ++ self.assertIsNone(got_dns_hostname) ++ ++ # Test that setting the HANDLES_SPN_UPDATE flag inhibits the dNSHostName ++ # update, but other attributes are still updated. ++ def test_set_dns_hostname_with_flag(self): ++ c = self.get_netlogon_connection() ++ authenticator, subsequent = self.get_authenticator(c) ++ ++ domain_hostname = self.ldb.domain_dns_name() ++ ++ new_dns_hostname = f'{self.machine_name}.{domain_hostname}' ++ new_dns_hostname = new_dns_hostname.encode('utf-8') ++ ++ operating_system = 'some OS' ++ ++ query = netr_WorkstationInformation() ++ query.os_name = lsa.String(operating_system) ++ ++ query.dns_hostname = new_dns_hostname ++ query.workstation_flags = netlogon.NETR_WS_FLAG_HANDLES_SPN_UPDATE ++ ++ c.netr_LogonGetDomainInfo( ++ server_name=self.server, ++ computer_name=self.user_creds.get_workstation(), ++ credential=authenticator, ++ return_authenticator=subsequent, ++ level=1, ++ query=query) ++ ++ # Check the result. ++ ++ res = self.ldb.search(self.machine_dn, ++ scope=ldb.SCOPE_BASE, ++ attrs=['dNSHostName', ++ 'operatingSystem']) ++ self.assertEqual(1, len(res)) ++ ++ got_dns_hostname = res[0].get('dNSHostName', idx=0) ++ self.assertIsNone(got_dns_hostname) ++ ++ got_os = res[0].get('operatingSystem', idx=0) ++ self.assertEqual(operating_system.encode('utf-8'), got_os) ++ + def test_SamLogonEx(self): + c = self.get_netlogon_connection() + +diff --git a/selftest/knownfail.d/netlogon-dns-host-name b/selftest/knownfail.d/netlogon-dns-host-name +new file mode 100644 +index 0000000..2d0a0ec +--- /dev/null ++++ b/selftest/knownfail.d/netlogon-dns-host-name +@@ -0,0 +1,2 @@ ++^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_invalid_suffix\( ++^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_with_flag\( +-- +1.8.3.1 + diff --git a/backport-0003-CVE-2022-32743-s4-torture-rpc-Fix-tests-to-match-Win.patch b/backport-0003-CVE-2022-32743-s4-torture-rpc-Fix-tests-to-match-Win.patch new file mode 100644 index 0000000000000000000000000000000000000000..a85579891220ddbab0bbd64c070a352950259d00 --- /dev/null +++ b/backport-0003-CVE-2022-32743-s4-torture-rpc-Fix-tests-to-match-Win.patch @@ -0,0 +1,65 @@ +From e38b75a50f79c1d1ea2d7d4489896ca5aa16d9d9 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 14 Jun 2022 17:19:00 +1200 +Subject: [PATCH 03/15] CVE-2022-32743 s4:torture/rpc: Fix tests to match + Windows + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + selftest/knownfail.d/netlogon-dns-host-name | 9 +++++++++ + source4/torture/rpc/netlogon.c | 12 +++++++----- + 2 files changed, 16 insertions(+), 5 deletions(-) + +diff --git a/selftest/knownfail.d/netlogon-dns-host-name b/selftest/knownfail.d/netlogon-dns-host-name +index 2d0a0ec..0164a7c 100644 +--- a/selftest/knownfail.d/netlogon-dns-host-name ++++ b/selftest/knownfail.d/netlogon-dns-host-name +@@ -1,2 +1,11 @@ + ^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_invalid_suffix\( + ^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_with_flag\( ++^samba4.rpc.netlogon on ncacn_ip_tcp with bigendian.netlogon.GetDomainInfo\( ++^samba4.rpc.netlogon on ncacn_ip_tcp with seal,padcheck.netlogon.GetDomainInfo\( ++^samba4.rpc.netlogon on ncacn_ip_tcp with validate.netlogon.GetDomainInfo\( ++^samba4.rpc.netlogon on ncacn_np with bigendian.netlogon.GetDomainInfo\( ++^samba4.rpc.netlogon on ncacn_np with seal,padcheck.netlogon.GetDomainInfo\( ++^samba4.rpc.netlogon on ncacn_np with validate.netlogon.GetDomainInfo\( ++^samba4.rpc.netlogon with bigendian.netlogon.GetDomainInfo\( ++^samba4.rpc.netlogon with seal,padcheck.netlogon.GetDomainInfo\( ++^samba4.rpc.netlogon with validate.netlogon.GetDomainInfo\( +diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c +index 11f950d..59d7feb 100644 +--- a/source4/torture/rpc/netlogon.c ++++ b/source4/torture/rpc/netlogon.c +@@ -5251,9 +5251,9 @@ static bool test_GetDomainInfo(struct torture_context *tctx, + torture_assert(tctx, + ldb_msg_find_attr_as_string(res[0], "operatingSystemServicePack", NULL) == NULL, + "'operatingSystemServicePack' shouldn't stick!"); +- torture_assert(tctx, +- ldb_msg_find_attr_as_string(res[0], "operatingSystemVersion", NULL) == NULL, +- "'operatingSystemVersion' shouldn't stick!"); ++ torture_assert_str_equal(tctx, ++ ldb_msg_find_attr_as_string(res[0], "operatingSystemVersion", NULL), ++ version_str, "'operatingSystemVersion' wrong!"); + + /* The DNS host name shouldn't have been updated by the server */ + +@@ -5387,9 +5387,11 @@ static bool test_GetDomainInfo(struct torture_context *tctx, + + torture_assert(tctx, odiT->domainname.string != NULL, + "trust_list domainname should be valid"); +- if (texT->trust_type == LSA_TRUST_TYPE_DOWNLEVEL) { ++ if (texT->trust_type == LSA_TRUST_TYPE_DOWNLEVEL || ++ texT->trust_type == LSA_TRUST_TYPE_MIT) ++ { + torture_assert(tctx, odiT->dns_domainname.string == NULL, +- "trust_list dns_domainname should be NULL for downlevel"); ++ "trust_list dns_domainname should be NULL for downlevel or MIT"); + } else { + torture_assert(tctx, odiT->dns_domainname.string != NULL, + "trust_list dns_domainname should be valid for uplevel"); +-- +1.8.3.1 + diff --git a/backport-0004-CVE-2022-32743-s4-dsdb-util-Add-dsdb_msg_get_single_.patch b/backport-0004-CVE-2022-32743-s4-dsdb-util-Add-dsdb_msg_get_single_.patch new file mode 100644 index 0000000000000000000000000000000000000000..33f4e2358e6cdadcbe838529f0ebf9a987ac3278 --- /dev/null +++ b/backport-0004-CVE-2022-32743-s4-dsdb-util-Add-dsdb_msg_get_single_.patch @@ -0,0 +1,140 @@ +From 49ac07e786df58b914ee85e2db773c0ba8d4e171 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 7 Jun 2022 17:36:56 +1200 +Subject: [PATCH 04/15] CVE-2022-32743 s4/dsdb/util: Add + dsdb_msg_get_single_value() + +This function simulates an add or modify operation for an ldb message to +determine the final value of a particular single-valued attribute. This +is useful when validating attributes that should stay in sync with other +attributes, such as servicePrincipalName and dNSHostName. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + source4/dsdb/samdb/ldb_modules/util.c | 107 ++++++++++++++++++++++++++++++++++ + 1 file changed, 107 insertions(+) + +diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c +index e7fe8f8..42aa9a2 100644 +--- a/source4/dsdb/samdb/ldb_modules/util.c ++++ b/source4/dsdb/samdb/ldb_modules/util.c +@@ -1568,6 +1568,113 @@ int dsdb_get_expected_new_values(TALLOC_CTX *mem_ctx, + return LDB_SUCCESS; + } + ++ ++/* ++ * Get the value of a single-valued attribute from an ADDed message. 'val' will only live as ++ * long as 'msg' and 'original_val' do, and must not be freed. ++ */ ++int dsdb_msg_add_get_single_value(const struct ldb_message *msg, ++ const char *attr_name, ++ const struct ldb_val **val) ++{ ++ const struct ldb_message_element *el = NULL; ++ ++ /* ++ * The ldb_msg_normalize() call in ldb_request() ensures that ++ * there is at most one message element for each ++ * attribute. Thus, we don't need a loop to deal with an ++ * LDB_ADD. ++ */ ++ el = ldb_msg_find_element(msg, attr_name); ++ if (el == NULL) { ++ *val = NULL; ++ return LDB_SUCCESS; ++ } ++ if (el->num_values != 1) { ++ return LDB_ERR_CONSTRAINT_VIOLATION; ++ } ++ ++ *val = &el->values[0]; ++ return LDB_SUCCESS; ++} ++ ++/* ++ * Get the value of a single-valued attribute after processing a ++ * message. 'operation' is either LDB_ADD or LDB_MODIFY. 'val' will only live as ++ * long as 'msg' and 'original_val' do, and must not be freed. ++ */ ++int dsdb_msg_get_single_value(const struct ldb_message *msg, ++ const char *attr_name, ++ const struct ldb_val *original_val, ++ const struct ldb_val **val, ++ enum ldb_request_type operation) ++{ ++ unsigned idx; ++ ++ *val = NULL; ++ ++ if (operation == LDB_ADD) { ++ if (original_val != NULL) { ++ /* This is an error on the caller's part. */ ++ return LDB_ERR_CONSTRAINT_VIOLATION; ++ } ++ return dsdb_msg_add_get_single_value(msg, attr_name, val); ++ } ++ ++ SMB_ASSERT(operation == LDB_MODIFY); ++ ++ *val = original_val; ++ ++ for (idx = 0; idx < msg->num_elements; ++idx) { ++ const struct ldb_message_element *el = &msg->elements[idx]; ++ ++ if (ldb_attr_cmp(el->name, attr_name) != 0) { ++ continue; ++ } ++ ++ switch (el->flags & LDB_FLAG_MOD_MASK) { ++ case LDB_FLAG_MOD_ADD: ++ if (el->num_values != 1) { ++ return LDB_ERR_CONSTRAINT_VIOLATION; ++ } ++ if (*val != NULL) { ++ return LDB_ERR_CONSTRAINT_VIOLATION; ++ } ++ ++ *val = &el->values[0]; ++ ++ break; ++ ++ case LDB_FLAG_MOD_REPLACE: ++ if (el->num_values > 1) { ++ return LDB_ERR_CONSTRAINT_VIOLATION; ++ } ++ ++ *val = el->num_values ? &el->values[0] : NULL; ++ ++ break; ++ ++ case LDB_FLAG_MOD_DELETE: ++ if (el->num_values > 1) { ++ return LDB_ERR_CONSTRAINT_VIOLATION; ++ } ++ ++ /* ++ * If a value was specified for the delete, we don't ++ * bother checking it matches the value we currently ++ * have. Any mismatch will be caught later (e.g. in ++ * ldb_kv_modify_internal). ++ */ ++ ++ *val = NULL; ++ ++ break; ++ } ++ } ++ ++ return LDB_SUCCESS; ++} ++ + /* + * Gets back a single-valued attribute by the rules of the DSDB triggers when + * performing a modify operation. +-- +1.8.3.1 + diff --git a/backport-0005-CVE-2022-32743-s4-dsdb-util-Add-function-to-check-fo.patch b/backport-0005-CVE-2022-32743-s4-dsdb-util-Add-function-to-check-fo.patch new file mode 100644 index 0000000000000000000000000000000000000000..6e42c0871de99838652b03a435e7932b2d62dee6 --- /dev/null +++ b/backport-0005-CVE-2022-32743-s4-dsdb-util-Add-function-to-check-fo.patch @@ -0,0 +1,69 @@ +From 0d888f0c902ebd98cfb82d50ab8b8b3928341ee2 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 14 Jun 2022 14:16:10 +1200 +Subject: [PATCH 05/15] CVE-2022-32743 s4/dsdb/util: Add function to check for + a subclass relationship + +We need to be able to determine whether an object is a subclass of a +specific objectclass such as 'computer'. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + source4/dsdb/samdb/ldb_modules/util.c | 38 +++++++++++++++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c +index 42aa9a2..9e00aed 100644 +--- a/source4/dsdb/samdb/ldb_modules/util.c ++++ b/source4/dsdb/samdb/ldb_modules/util.c +@@ -1718,6 +1718,44 @@ const struct dsdb_class *dsdb_get_structural_oc_from_msg(const struct dsdb_schem + return dsdb_get_last_structural_class(schema, oc_el); + } + ++/* ++ Get the parent class of an objectclass, or NULL if none exists. ++ */ ++const struct dsdb_class *dsdb_get_parent_class(const struct dsdb_schema *schema, ++ const struct dsdb_class *objectclass) ++{ ++ if (ldb_attr_cmp(objectclass->lDAPDisplayName, "top") == 0) { ++ return NULL; ++ } ++ ++ if (objectclass->subClassOf == NULL) { ++ return NULL; ++ } ++ ++ return dsdb_class_by_lDAPDisplayName(schema, objectclass->subClassOf); ++} ++ ++/* ++ Return true if 'struct_objectclass' is a subclass of 'other_objectclass'. The ++ two objectclasses must originate from the same schema, to allow for ++ pointer-based identity comparison. ++ */ ++bool dsdb_is_subclass_of(const struct dsdb_schema *schema, ++ const struct dsdb_class *struct_objectclass, ++ const struct dsdb_class *other_objectclass) ++{ ++ while (struct_objectclass != NULL) { ++ /* Pointer comparison can be used due to the same schema str. */ ++ if (struct_objectclass == other_objectclass) { ++ return true; ++ } ++ ++ struct_objectclass = dsdb_get_parent_class(schema, struct_objectclass); ++ } ++ ++ return false; ++} ++ + /* Fix the DN so that the relative attribute names are in upper case so that the DN: + cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com becomes + CN=Adminstrator,CN=users,DC=samba,DC=example,DC=com +-- +1.8.3.1 + diff --git a/backport-0006-CVE-2022-32743-dsdb-Implement-validated-dNSHostName-.patch b/backport-0006-CVE-2022-32743-dsdb-Implement-validated-dNSHostName-.patch new file mode 100644 index 0000000000000000000000000000000000000000..7774a2b5fdaa60140513165ac5f56e865045e1a7 --- /dev/null +++ b/backport-0006-CVE-2022-32743-dsdb-Implement-validated-dNSHostName-.patch @@ -0,0 +1,339 @@ +From b95431ab2303eb258e37e88d8841f2fb79fc4af5 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Wed, 1 Jun 2022 16:08:42 +1200 +Subject: [PATCH 06/15] CVE-2022-32743 dsdb: Implement validated dNSHostName + write + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + selftest/knownfail.d/validated-dns-host-name | 12 -- + source4/dsdb/samdb/ldb_modules/acl.c | 283 +++++++++++++++++++++++++++ + 2 files changed, 283 insertions(+), 12 deletions(-) + +diff --git a/selftest/knownfail.d/validated-dns-host-name b/selftest/knownfail.d/validated-dns-host-name +index ee51f44..4b61658 100644 +--- a/selftest/knownfail.d/validated-dns-host-name ++++ b/selftest/knownfail.d/validated-dns-host-name +@@ -1,15 +1,3 @@ +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_account_no_dollar\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_allowed_suffixes\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_case\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_dollar\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_empty_string\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_invalid\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_no_suffix\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_no_value\( + ^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_spn\( + ^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_spn_matching_account_name_new\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_spn_matching_account_name_original\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_wrong_prefix\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_wrong_suffix\( + ^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_spn_matching_dns_host_name_invalid\( +diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c +index 1fc6dbf..50802ae 100644 +--- a/source4/dsdb/samdb/ldb_modules/acl.c ++++ b/source4/dsdb/samdb/ldb_modules/acl.c +@@ -802,6 +802,277 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx, + return LDB_SUCCESS; + } + ++static int acl_check_dns_host_name(TALLOC_CTX *mem_ctx, ++ struct ldb_module *module, ++ struct ldb_request *req, ++ const struct ldb_message_element *el, ++ struct security_descriptor *sd, ++ struct dom_sid *sid, ++ const struct dsdb_attribute *attr, ++ const struct dsdb_class *objectclass) ++{ ++ int ret; ++ unsigned i; ++ TALLOC_CTX *tmp_ctx = NULL; ++ struct ldb_context *ldb = ldb_module_get_ctx(module); ++ const struct dsdb_schema *schema = NULL; ++ const struct ldb_message_element *allowed_suffixes = NULL; ++ struct ldb_result *nc_res = NULL; ++ struct ldb_dn *nc_root = NULL; ++ const char *nc_dns_name = NULL; ++ const char *dnsHostName_str = NULL; ++ size_t dns_host_name_len; ++ size_t account_name_len; ++ const struct ldb_message *msg = NULL; ++ const struct ldb_message *search_res = NULL; ++ const struct ldb_val *samAccountName = NULL; ++ const struct ldb_val *dnsHostName = NULL; ++ const struct dsdb_class *computer_objectclass = NULL; ++ bool is_subclass; ++ ++ static const char *nc_attrs[] = { ++ "msDS-AllowedDNSSuffixes", ++ NULL ++ }; ++ ++ if (el->num_values == 0) { ++ return LDB_SUCCESS; ++ } ++ dnsHostName = &el->values[0]; ++ ++ tmp_ctx = talloc_new(mem_ctx); ++ if (tmp_ctx == NULL) { ++ return ldb_oom(ldb); ++ } ++ ++ /* if we have wp, we can do whatever we like */ ++ ret = acl_check_access_on_attribute(module, ++ tmp_ctx, ++ sd, ++ sid, ++ SEC_ADS_WRITE_PROP, ++ attr, objectclass); ++ if (ret == LDB_SUCCESS) { ++ talloc_free(tmp_ctx); ++ return LDB_SUCCESS; ++ } ++ ++ ret = acl_check_extended_right(tmp_ctx, ++ module, ++ req, ++ objectclass, ++ sd, ++ acl_user_token(module), ++ GUID_DRS_DNS_HOST_NAME, ++ SEC_ADS_SELF_WRITE, ++ sid); ++ ++ if (ret != LDB_SUCCESS) { ++ dsdb_acl_debug(sd, acl_user_token(module), ++ req->op.mod.message->dn, ++ true, ++ 10); ++ talloc_free(tmp_ctx); ++ return ret; ++ } ++ ++ /* ++ * If we have "validated write dnshostname", allow delete of ++ * any existing value (this keeps constrained delete to the ++ * same rules as unconstrained) ++ */ ++ if (req->operation == LDB_MODIFY) { ++ struct ldb_result *acl_res = NULL; ++ ++ static const char *acl_attrs[] = { ++ "sAMAccountName", ++ NULL ++ }; ++ ++ msg = req->op.mod.message; ++ ++ /* ++ * If not add or replace (eg delete), ++ * return success ++ */ ++ if ((el->flags ++ & (LDB_FLAG_MOD_ADD|LDB_FLAG_MOD_REPLACE)) == 0) ++ { ++ talloc_free(tmp_ctx); ++ return LDB_SUCCESS; ++ } ++ ++ ret = dsdb_module_search_dn(module, tmp_ctx, ++ &acl_res, msg->dn, ++ acl_attrs, ++ DSDB_FLAG_NEXT_MODULE | ++ DSDB_FLAG_AS_SYSTEM | ++ DSDB_SEARCH_SHOW_RECYCLED, ++ req); ++ if (ret != LDB_SUCCESS) { ++ talloc_free(tmp_ctx); ++ return ret; ++ } ++ ++ search_res = acl_res->msgs[0]; ++ } else if (req->operation == LDB_ADD) { ++ msg = req->op.add.message; ++ search_res = msg; ++ } else { ++ talloc_free(tmp_ctx); ++ return LDB_ERR_OPERATIONS_ERROR; ++ } ++ ++ /* Check if the account has objectclass 'computer' or 'server'. */ ++ ++ schema = dsdb_get_schema(ldb, req); ++ if (schema == NULL) { ++ talloc_free(tmp_ctx); ++ return ldb_operr(ldb); ++ } ++ ++ computer_objectclass = dsdb_class_by_lDAPDisplayName(schema, "computer"); ++ if (computer_objectclass == NULL) { ++ talloc_free(tmp_ctx); ++ return ldb_operr(ldb); ++ } ++ ++ is_subclass = dsdb_is_subclass_of(schema, objectclass, computer_objectclass); ++ if (!is_subclass) { ++ /* The account is not a computer -- check if it's a server. */ ++ ++ const struct dsdb_class *server_objectclass = NULL; ++ ++ server_objectclass = dsdb_class_by_lDAPDisplayName(schema, "server"); ++ if (server_objectclass == NULL) { ++ talloc_free(tmp_ctx); ++ return ldb_operr(ldb); ++ } ++ ++ is_subclass = dsdb_is_subclass_of(schema, objectclass, server_objectclass); ++ if (!is_subclass) { ++ /* Not a computer or server, so no need to validate. */ ++ talloc_free(tmp_ctx); ++ return LDB_SUCCESS; ++ } ++ } ++ ++ samAccountName = ldb_msg_find_ldb_val(search_res, "sAMAccountName"); ++ ++ ret = dsdb_msg_get_single_value(msg, ++ "sAMAccountName", ++ samAccountName, ++ &samAccountName, ++ req->operation); ++ if (ret != LDB_SUCCESS) { ++ talloc_free(tmp_ctx); ++ return ret; ++ } ++ ++ account_name_len = samAccountName->length; ++ if (account_name_len && samAccountName->data[account_name_len - 1] == '$') { ++ /* Account for the '$' character. */ ++ --account_name_len; ++ } ++ ++ dnsHostName_str = (const char *)dnsHostName->data; ++ dns_host_name_len = dnsHostName->length; ++ ++ /* Check that sAMAccountName matches the new dNSHostName. */ ++ ++ if (dns_host_name_len < account_name_len) { ++ goto fail; ++ } ++ if (strncasecmp(dnsHostName_str, ++ (const char *)samAccountName->data, ++ account_name_len) != 0) ++ { ++ goto fail; ++ } ++ ++ dnsHostName_str += account_name_len; ++ dns_host_name_len -= account_name_len; ++ ++ /* Check the '.' character */ ++ ++ if (dns_host_name_len == 0 || *dnsHostName_str != '.') { ++ goto fail; ++ } ++ ++ ++dnsHostName_str; ++ --dns_host_name_len; ++ ++ /* Now we check the suffix. */ ++ ++ ret = dsdb_find_nc_root(ldb, ++ tmp_ctx, ++ search_res->dn, ++ &nc_root); ++ if (ret != LDB_SUCCESS) { ++ talloc_free(tmp_ctx); ++ return ret; ++ } ++ ++ nc_dns_name = samdb_dn_to_dns_domain(tmp_ctx, nc_root); ++ if (nc_dns_name == NULL) { ++ talloc_free(tmp_ctx); ++ return ldb_operr(ldb); ++ } ++ ++ if (strlen(nc_dns_name) == dns_host_name_len && ++ strncasecmp(dnsHostName_str, ++ nc_dns_name, ++ dns_host_name_len) == 0) ++ { ++ /* It matches -- success. */ ++ talloc_free(tmp_ctx); ++ return LDB_SUCCESS; ++ } ++ ++ /* We didn't get a match, so now try msDS-AllowedDNSSuffixes. */ ++ ++ ret = dsdb_module_search_dn(module, tmp_ctx, ++ &nc_res, nc_root, ++ nc_attrs, ++ DSDB_FLAG_NEXT_MODULE | ++ DSDB_FLAG_AS_SYSTEM | ++ DSDB_SEARCH_SHOW_RECYCLED, ++ req); ++ if (ret != LDB_SUCCESS) { ++ talloc_free(tmp_ctx); ++ return ret; ++ } ++ ++ allowed_suffixes = ldb_msg_find_element(nc_res->msgs[0], ++ "msDS-AllowedDNSSuffixes"); ++ if (allowed_suffixes == NULL) { ++ goto fail; ++ } ++ ++ for (i = 0; i < allowed_suffixes->num_values; ++i) { ++ const struct ldb_val *suffix = &allowed_suffixes->values[i]; ++ ++ if (suffix->length == dns_host_name_len && ++ strncasecmp(dnsHostName_str, ++ (const char *)suffix->data, ++ dns_host_name_len) == 0) ++ { ++ /* It matches -- success. */ ++ talloc_free(tmp_ctx); ++ return LDB_SUCCESS; ++ } ++ } ++ ++fail: ++ ldb_debug_set(ldb, LDB_DEBUG_WARNING, ++ "acl: hostname validation failed for " ++ "hostname[%.*s] account[%.*s]\n", ++ (int)dnsHostName->length, dnsHostName->data, ++ (int)samAccountName->length, samAccountName->data); ++ talloc_free(tmp_ctx); ++ return LDB_ERR_CONSTRAINT_VIOLATION; ++} ++ + static int acl_add(struct ldb_module *module, struct ldb_request *req) + { + int ret; +@@ -1536,6 +1807,18 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) + if (ret != LDB_SUCCESS) { + goto fail; + } ++ } else if (ldb_attr_cmp("dnsHostName", el->name) == 0) { ++ ret = acl_check_dns_host_name(tmp_ctx, ++ module, ++ req, ++ el, ++ sd, ++ sid, ++ attr, ++ objectclass); ++ if (ret != LDB_SUCCESS) { ++ goto fail; ++ } + } else if (is_undelete != NULL && (ldb_attr_cmp("isDeleted", el->name) == 0)) { + /* + * in case of undelete op permissions on +-- +1.8.3.1 + diff --git a/backport-0007-CVE-2022-32743-dsdb-common-Add-FORCE_ALLOW_VALIDATED.patch b/backport-0007-CVE-2022-32743-dsdb-common-Add-FORCE_ALLOW_VALIDATED.patch new file mode 100644 index 0000000000000000000000000000000000000000..d9e6bc3596106a3cfe961caae12d70d2b6ea4170 --- /dev/null +++ b/backport-0007-CVE-2022-32743-dsdb-common-Add-FORCE_ALLOW_VALIDATED.patch @@ -0,0 +1,67 @@ +From c2ab1f4696fa3f52918a126d0b37993a07f68bcb Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 7 Jun 2022 17:36:43 +1200 +Subject: [PATCH 07/15] CVE-2022-32743 dsdb/common: Add + FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE control + +Passing this control will grant the right to set validated values for +dNSHostName and servicePrincipalName, and non-validated values for other +attributes. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + source4/dsdb/common/util.c | 7 +++++++ + source4/dsdb/samdb/ldb_modules/util.h | 1 + + source4/dsdb/samdb/samdb.h | 6 ++++++ + 3 files changed, 14 insertions(+) + +diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c +index 1129175..88b0555 100644 +--- a/source4/dsdb/common/util.c ++++ b/source4/dsdb/common/util.c +@@ -4546,6 +4546,13 @@ int dsdb_request_add_controls(struct ldb_request *req, uint32_t dsdb_flags) + } + } + ++ if (dsdb_flags & DSDB_FLAG_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE) { ++ ret = ldb_request_add_control(req, DSDB_CONTROL_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE_OID, true, NULL); ++ if (ret != LDB_SUCCESS) { ++ return ret; ++ } ++ } ++ + return LDB_SUCCESS; + } + +diff --git a/source4/dsdb/samdb/ldb_modules/util.h b/source4/dsdb/samdb/ldb_modules/util.h +index 5ecf0ee..937767a 100644 +--- a/source4/dsdb/samdb/ldb_modules/util.h ++++ b/source4/dsdb/samdb/ldb_modules/util.h +@@ -39,3 +39,4 @@ struct netlogon_samlogon_response; + #define DSDB_FLAG_TOP_MODULE 0x00800000 + #define DSDB_FLAG_TRUSTED 0x01000000 + #define DSDB_FLAG_REPLICATED_UPDATE 0x02000000 ++#define DSDB_FLAG_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE 0x04000000 +diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h +index 286c97f..3db7704 100644 +--- a/source4/dsdb/samdb/samdb.h ++++ b/source4/dsdb/samdb/samdb.h +@@ -226,6 +226,12 @@ struct dsdb_control_transaction_identifier { + struct GUID transaction_guid; + }; + ++/* ++ * passed when we want to allow validated writes to dNSHostName and ++ * servicePrincipalName. ++ */ ++#define DSDB_CONTROL_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE_OID "1.3.6.1.4.1.7165.4.3.35" ++ + #define DSDB_EXTENDED_REPLICATED_OBJECTS_OID "1.3.6.1.4.1.7165.4.4.1" + struct dsdb_extended_replicated_object { + struct ldb_message *msg; +-- +1.8.3.1 + diff --git a/backport-0008-CVE-2022-32743-dsdb-modules-acl-Handle-FORCE_ALLOW_V.patch b/backport-0008-CVE-2022-32743-dsdb-modules-acl-Handle-FORCE_ALLOW_V.patch new file mode 100644 index 0000000000000000000000000000000000000000..583dbbc3bf6992f04ecb9f898521f3ae0d14ea51 --- /dev/null +++ b/backport-0008-CVE-2022-32743-dsdb-modules-acl-Handle-FORCE_ALLOW_V.patch @@ -0,0 +1,240 @@ +From f9831259b9f6a49b9e1a7be75198d60374cdef2f Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 7 Jun 2022 17:39:07 +1200 +Subject: [PATCH 08/15] CVE-2022-32743 dsdb/modules/acl: Handle + FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE control + +When this control is specified, we'll assume we have Validated Write on +dNSHostName and servicePrincipalName, and Write Property on other +attributes. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + source4/dsdb/samdb/ldb_modules/acl.c | 148 +++++++++++++++++++++-------------- + 1 file changed, 91 insertions(+), 57 deletions(-) + +diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c +index 50802ae..a26d0ba 100644 +--- a/source4/dsdb/samdb/ldb_modules/acl.c ++++ b/source4/dsdb/samdb/ldb_modules/acl.c +@@ -667,7 +667,8 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx, + struct security_descriptor *sd, + struct dom_sid *sid, + const struct dsdb_attribute *attr, +- const struct dsdb_class *objectclass) ++ const struct dsdb_class *objectclass, ++ const struct ldb_control *implicit_validated_write_control) + { + int ret; + unsigned int i; +@@ -694,34 +695,44 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx, + NULL + }; + +- /* if we have wp, we can do whatever we like */ +- if (acl_check_access_on_attribute(module, +- tmp_ctx, +- sd, +- sid, +- SEC_ADS_WRITE_PROP, +- attr, objectclass) == LDB_SUCCESS) { +- talloc_free(tmp_ctx); +- return LDB_SUCCESS; +- } ++ if (implicit_validated_write_control != NULL) { ++ /* ++ * The validated write control dispenses with ACL ++ * checks. We act as if we have an implicit Self Write ++ * privilege, but, assuming we don't have Write ++ * Property, still proceed with further validation ++ * checks. ++ */ ++ } else { ++ /* if we have wp, we can do whatever we like */ ++ if (acl_check_access_on_attribute(module, ++ tmp_ctx, ++ sd, ++ sid, ++ SEC_ADS_WRITE_PROP, ++ attr, objectclass) == LDB_SUCCESS) { ++ talloc_free(tmp_ctx); ++ return LDB_SUCCESS; ++ } + +- ret = acl_check_extended_right(tmp_ctx, +- module, +- req, +- objectclass, +- sd, +- acl_user_token(module), +- GUID_DRS_VALIDATE_SPN, +- SEC_ADS_SELF_WRITE, +- sid); ++ ret = acl_check_extended_right(tmp_ctx, ++ module, ++ req, ++ objectclass, ++ sd, ++ acl_user_token(module), ++ GUID_DRS_VALIDATE_SPN, ++ SEC_ADS_SELF_WRITE, ++ sid); + +- if (ret != LDB_SUCCESS) { +- dsdb_acl_debug(sd, acl_user_token(module), +- req->op.mod.message->dn, +- true, +- 10); +- talloc_free(tmp_ctx); +- return ret; ++ if (ret != LDB_SUCCESS) { ++ dsdb_acl_debug(sd, acl_user_token(module), ++ req->op.mod.message->dn, ++ true, ++ 10); ++ talloc_free(tmp_ctx); ++ return ret; ++ } + } + + /* +@@ -809,7 +820,8 @@ static int acl_check_dns_host_name(TALLOC_CTX *mem_ctx, + struct security_descriptor *sd, + struct dom_sid *sid, + const struct dsdb_attribute *attr, +- const struct dsdb_class *objectclass) ++ const struct dsdb_class *objectclass, ++ const struct ldb_control *implicit_validated_write_control) + { + int ret; + unsigned i; +@@ -845,35 +857,45 @@ static int acl_check_dns_host_name(TALLOC_CTX *mem_ctx, + return ldb_oom(ldb); + } + +- /* if we have wp, we can do whatever we like */ +- ret = acl_check_access_on_attribute(module, +- tmp_ctx, +- sd, +- sid, +- SEC_ADS_WRITE_PROP, +- attr, objectclass); +- if (ret == LDB_SUCCESS) { +- talloc_free(tmp_ctx); +- return LDB_SUCCESS; +- } ++ if (implicit_validated_write_control != NULL) { ++ /* ++ * The validated write control dispenses with ACL ++ * checks. We act as if we have an implicit Self Write ++ * privilege, but, assuming we don't have Write ++ * Property, still proceed with further validation ++ * checks. ++ */ ++ } else { ++ /* if we have wp, we can do whatever we like */ ++ ret = acl_check_access_on_attribute(module, ++ tmp_ctx, ++ sd, ++ sid, ++ SEC_ADS_WRITE_PROP, ++ attr, objectclass); ++ if (ret == LDB_SUCCESS) { ++ talloc_free(tmp_ctx); ++ return LDB_SUCCESS; ++ } + +- ret = acl_check_extended_right(tmp_ctx, +- module, +- req, +- objectclass, +- sd, +- acl_user_token(module), +- GUID_DRS_DNS_HOST_NAME, +- SEC_ADS_SELF_WRITE, +- sid); ++ ret = acl_check_extended_right(tmp_ctx, ++ module, ++ req, ++ objectclass, ++ sd, ++ acl_user_token(module), ++ GUID_DRS_DNS_HOST_NAME, ++ SEC_ADS_SELF_WRITE, ++ sid); + +- if (ret != LDB_SUCCESS) { +- dsdb_acl_debug(sd, acl_user_token(module), +- req->op.mod.message->dn, +- true, +- 10); +- talloc_free(tmp_ctx); +- return ret; ++ if (ret != LDB_SUCCESS) { ++ dsdb_acl_debug(sd, acl_user_token(module), ++ req->op.mod.message->dn, ++ true, ++ 10); ++ talloc_free(tmp_ctx); ++ return ret; ++ } + } + + /* +@@ -1621,6 +1643,7 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) + struct dom_sid *sid = NULL; + struct ldb_control *as_system; + struct ldb_control *is_undelete; ++ struct ldb_control *implicit_validated_write_control = NULL; + bool userPassword; + bool password_rights_checked = false; + TALLOC_CTX *tmp_ctx; +@@ -1647,6 +1670,12 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) + + is_undelete = ldb_request_get_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID); + ++ implicit_validated_write_control = ldb_request_get_control( ++ req, DSDB_CONTROL_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE_OID); ++ if (implicit_validated_write_control != NULL) { ++ implicit_validated_write_control->critical = 0; ++ } ++ + /* Don't print this debug statement if elements[0].name is going to be NULL */ + if (msg->num_elements > 0) { + DEBUG(10, ("ldb:acl_modify: %s\n", msg->elements[0].name)); +@@ -1803,7 +1832,8 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) + sd, + sid, + attr, +- objectclass); ++ objectclass, ++ implicit_validated_write_control); + if (ret != LDB_SUCCESS) { + goto fail; + } +@@ -1815,7 +1845,8 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) + sd, + sid, + attr, +- objectclass); ++ objectclass, ++ implicit_validated_write_control); + if (ret != LDB_SUCCESS) { + goto fail; + } +@@ -1827,6 +1858,9 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) + * tombstone_reanimate module + */ + continue; ++ } else if (implicit_validated_write_control != NULL) { ++ /* Allow the update. */ ++ continue; + } else { + ret = acl_check_access_on_attribute(module, + tmp_ctx, +-- +1.8.3.1 + diff --git a/backport-0009-CVE-2022-32743-s4-rpc_server-netlogon-Remove-dNSHost.patch b/backport-0009-CVE-2022-32743-s4-rpc_server-netlogon-Remove-dNSHost.patch new file mode 100644 index 0000000000000000000000000000000000000000..d6005f6dbd7d5c6dfd5455254d678dad9ee3a5f2 --- /dev/null +++ b/backport-0009-CVE-2022-32743-s4-rpc_server-netlogon-Remove-dNSHost.patch @@ -0,0 +1,82 @@ +From d07641fc5a7d2fa323e6d6fe3223da3a6d682405 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Thu, 2 Jun 2022 17:11:08 +1200 +Subject: [PATCH 09/15] CVE-2022-32743 s4:rpc_server/netlogon: Remove + dNSHostName prefix check + +This check is not exhaustive (it does not check the suffix of the +dNSHostName), and should be covered by a validated write check in +acl_modify(). + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + selftest/knownfail.d/netlogon-dns-host-name | 5 +++++ + source4/rpc_server/netlogon/dcerpc_netlogon.c | 21 ++------------------- + 2 files changed, 7 insertions(+), 19 deletions(-) + +diff --git a/selftest/knownfail.d/netlogon-dns-host-name b/selftest/knownfail.d/netlogon-dns-host-name +index 0164a7c..d6a8aa2 100644 +--- a/selftest/knownfail.d/netlogon-dns-host-name ++++ b/selftest/knownfail.d/netlogon-dns-host-name +@@ -1,4 +1,6 @@ + ^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_invalid_suffix\( ++^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_invalid_validated_write\( ++^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_invalid_write_property\( + ^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_with_flag\( + ^samba4.rpc.netlogon on ncacn_ip_tcp with bigendian.netlogon.GetDomainInfo\( + ^samba4.rpc.netlogon on ncacn_ip_tcp with seal,padcheck.netlogon.GetDomainInfo\( +@@ -6,6 +8,9 @@ + ^samba4.rpc.netlogon on ncacn_np with bigendian.netlogon.GetDomainInfo\( + ^samba4.rpc.netlogon on ncacn_np with seal,padcheck.netlogon.GetDomainInfo\( + ^samba4.rpc.netlogon on ncacn_np with validate.netlogon.GetDomainInfo\( ++^samba4.rpc.netlogon on ncalrpc with bigendian.netlogon.GetDomainInfo\( ++^samba4.rpc.netlogon on ncalrpc with seal,padcheck.netlogon.GetDomainInfo\( ++^samba4.rpc.netlogon on ncalrpc with validate.netlogon.GetDomainInfo\( + ^samba4.rpc.netlogon with bigendian.netlogon.GetDomainInfo\( + ^samba4.rpc.netlogon with seal,padcheck.netlogon.GetDomainInfo\( + ^samba4.rpc.netlogon with validate.netlogon.GetDomainInfo\( +diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c +index eab57da..2d5fc8b 100644 +--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c ++++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c +@@ -2413,7 +2413,7 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal + }; + const char * const attrs2[] = { "sAMAccountName", "dNSHostName", + "msDS-SupportedEncryptionTypes", NULL }; +- const char *sam_account_name, *old_dns_hostname, *prefix1, *prefix2; ++ const char *sam_account_name, *old_dns_hostname; + struct ldb_context *sam_ctx; + const struct GUID *our_domain_guid = NULL; + struct lsa_TrustDomainInfoInfoEx *our_tdo = NULL; +@@ -2483,24 +2483,7 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + +- /* +- * Checks that the sam account name without a possible "$" +- * matches as prefix with the DNS hostname in the workstation +- * info structure. +- */ +- prefix1 = talloc_strndup(mem_ctx, sam_account_name, +- strcspn(sam_account_name, "$")); +- NT_STATUS_HAVE_NO_MEMORY(prefix1); +- if (r->in.query->workstation_info->dns_hostname != NULL) { +- prefix2 = talloc_strndup(mem_ctx, +- r->in.query->workstation_info->dns_hostname, +- strcspn(r->in.query->workstation_info->dns_hostname, ".")); +- NT_STATUS_HAVE_NO_MEMORY(prefix2); +- +- if (strcasecmp(prefix1, prefix2) != 0) { +- update_dns_hostname = false; +- } +- } else { ++ if (r->in.query->workstation_info->dns_hostname == NULL) { + update_dns_hostname = false; + } + +-- +1.8.3.1 + diff --git a/backport-0010-CVE-2022-32743-s4-rpc_server-netlogon-Always-observe.patch b/backport-0010-CVE-2022-32743-s4-rpc_server-netlogon-Always-observe.patch new file mode 100644 index 0000000000000000000000000000000000000000..be6b1f03c8b269f43d8135e058c1972b7104d9f3 --- /dev/null +++ b/backport-0010-CVE-2022-32743-s4-rpc_server-netlogon-Always-observe.patch @@ -0,0 +1,52 @@ +From 02c2a8c7b01d6412393423813b710c88b20fb97f Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 7 Jun 2022 17:25:28 +1200 +Subject: [PATCH 10/15] CVE-2022-32743 s4:rpc_server/netlogon: Always observe + NETR_WS_FLAG_HANDLES_SPN_UPDATE flag + +Even when there is no old DNS hostname present. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + selftest/knownfail.d/netlogon-dns-host-name | 1 - + source4/rpc_server/netlogon/dcerpc_netlogon.c | 7 ++----- + 2 files changed, 2 insertions(+), 6 deletions(-) + +diff --git a/selftest/knownfail.d/netlogon-dns-host-name b/selftest/knownfail.d/netlogon-dns-host-name +index d6a8aa2..30c157f 100644 +--- a/selftest/knownfail.d/netlogon-dns-host-name ++++ b/selftest/knownfail.d/netlogon-dns-host-name +@@ -1,7 +1,6 @@ + ^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_invalid_suffix\( + ^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_invalid_validated_write\( + ^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_invalid_write_property\( +-^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_with_flag\( + ^samba4.rpc.netlogon on ncacn_ip_tcp with bigendian.netlogon.GetDomainInfo\( + ^samba4.rpc.netlogon on ncacn_ip_tcp with seal,padcheck.netlogon.GetDomainInfo\( + ^samba4.rpc.netlogon on ncacn_ip_tcp with validate.netlogon.GetDomainInfo\( +diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c +index 2d5fc8b..efba013 100644 +--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c ++++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c +@@ -2495,13 +2495,10 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal + /* + * Updates the DNS hostname when the client wishes that the + * server should handle this for him +- * ("NETR_WS_FLAG_HANDLES_SPN_UPDATE" not set). And this is +- * obviously only checked when we do already have a +- * "dNSHostName". ++ * ("NETR_WS_FLAG_HANDLES_SPN_UPDATE" not set). + * See MS-NRPC section 3.5.4.3.9 + */ +- if ((old_dns_hostname != NULL) && +- (r->in.query->workstation_info->workstation_flags ++ if ((r->in.query->workstation_info->workstation_flags + & NETR_WS_FLAG_HANDLES_SPN_UPDATE) != 0) { + update_dns_hostname = false; + } +-- +1.8.3.1 + diff --git a/backport-0011-CVE-2022-32743-s4-rpc_server-netlogon-Connect-to-sam.patch b/backport-0011-CVE-2022-32743-s4-rpc_server-netlogon-Connect-to-sam.patch new file mode 100644 index 0000000000000000000000000000000000000000..59505bc9839d8028c19733741204aedc18d300d1 --- /dev/null +++ b/backport-0011-CVE-2022-32743-s4-rpc_server-netlogon-Connect-to-sam.patch @@ -0,0 +1,71 @@ +From f545142380151a626848dbae9ee746167f3299fa Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 7 Jun 2022 17:29:02 +1200 +Subject: [PATCH 11/15] CVE-2022-32743 s4:rpc_server/netlogon: Connect to samdb + as a user, rather than as system + +This allows us to perform validation on a client-specified dNSHostName +value, to ensure that it matches the sAMAccountName. + +We might not have any rights to modify the account, so pass the control +FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE which allows us to perform +a validated write to dNSHostName and servicePrincipalName (and +unvalidated writes to other attributes, such as operatingSystem). + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + selftest/knownfail.d/netlogon-dns-host-name | 17 ++--------------- + source4/rpc_server/netlogon/dcerpc_netlogon.c | 5 +++-- + 2 files changed, 5 insertions(+), 17 deletions(-) + +diff --git a/selftest/knownfail.d/netlogon-dns-host-name b/selftest/knownfail.d/netlogon-dns-host-name +index 30c157f..3eca0cd 100644 +--- a/selftest/knownfail.d/netlogon-dns-host-name ++++ b/selftest/knownfail.d/netlogon-dns-host-name +@@ -1,15 +1,2 @@ +-^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_invalid_suffix\( +-^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_invalid_validated_write\( +-^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_invalid_write_property\( +-^samba4.rpc.netlogon on ncacn_ip_tcp with bigendian.netlogon.GetDomainInfo\( +-^samba4.rpc.netlogon on ncacn_ip_tcp with seal,padcheck.netlogon.GetDomainInfo\( +-^samba4.rpc.netlogon on ncacn_ip_tcp with validate.netlogon.GetDomainInfo\( +-^samba4.rpc.netlogon on ncacn_np with bigendian.netlogon.GetDomainInfo\( +-^samba4.rpc.netlogon on ncacn_np with seal,padcheck.netlogon.GetDomainInfo\( +-^samba4.rpc.netlogon on ncacn_np with validate.netlogon.GetDomainInfo\( +-^samba4.rpc.netlogon on ncalrpc with bigendian.netlogon.GetDomainInfo\( +-^samba4.rpc.netlogon on ncalrpc with seal,padcheck.netlogon.GetDomainInfo\( +-^samba4.rpc.netlogon on ncalrpc with validate.netlogon.GetDomainInfo\( +-^samba4.rpc.netlogon with bigendian.netlogon.GetDomainInfo\( +-^samba4.rpc.netlogon with seal,padcheck.netlogon.GetDomainInfo\( +-^samba4.rpc.netlogon with validate.netlogon.GetDomainInfo\( ++^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_valid\( ++^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_valid_denied\( +diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c +index efba013..15cd27b 100644 +--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c ++++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c +@@ -2450,7 +2450,8 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal + } + NT_STATUS_NOT_OK_RETURN(status); + +- sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call); ++ /* We want to avoid connecting as system. */ ++ sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call); + if (sam_ctx == NULL) { + return NT_STATUS_INVALID_SYSTEM_SERVICE; + } +@@ -2607,7 +2608,7 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal + } + } + +- if (dsdb_replace(sam_ctx, new_msg, 0) != LDB_SUCCESS) { ++ if (dsdb_replace(sam_ctx, new_msg, DSDB_FLAG_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE) != LDB_SUCCESS) { + DEBUG(3,("Impossible to update samdb: %s\n", + ldb_errstring(sam_ctx))); + } +-- +1.8.3.1 + diff --git a/backport-0012-CVE-2022-32743-dsdb-modules-acl-Account-for-sAMAccou.patch b/backport-0012-CVE-2022-32743-dsdb-modules-acl-Account-for-sAMAccou.patch new file mode 100644 index 0000000000000000000000000000000000000000..056319f52cadc80adf345af01921a9f82a0a4660 --- /dev/null +++ b/backport-0012-CVE-2022-32743-dsdb-modules-acl-Account-for-sAMAccou.patch @@ -0,0 +1,56 @@ +From 7638abd38a13f9d2b5c769eb12c70eacf49b3806 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 7 Jun 2022 17:37:34 +1200 +Subject: [PATCH 12/15] CVE-2022-32743 dsdb/modules/acl: Account for + sAMAccountName without $ + +If we have an account without a trailing $, we should ensure the +servicePrincipalName matches the entire sAMAccountName. We should not +allow a match against the sAMAccountName prefix of length +strlen(samAccountName) - 1, as that could conflict with a different +account. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + source4/dsdb/samdb/ldb_modules/acl.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c +index a26d0ba..82f6ec3 100644 +--- a/source4/dsdb/samdb/ldb_modules/acl.c ++++ b/source4/dsdb/samdb/ldb_modules/acl.c +@@ -543,6 +543,7 @@ static int acl_validate_spn_value(TALLOC_CTX *mem_ctx, + char *instanceName; + char *serviceType; + char *serviceName; ++ size_t account_name_len; + const char *forest_name = samdb_forest_name(ldb, mem_ctx); + const char *base_domain = samdb_default_domain_name(ldb, mem_ctx); + struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"), +@@ -616,11 +617,18 @@ static int acl_validate_spn_value(TALLOC_CTX *mem_ctx, + } + } + } ++ ++ account_name_len = strlen(samAccountName); ++ if (account_name_len && samAccountName[account_name_len - 1] == '$') { ++ /* Account for the '$' character. */ ++ --account_name_len; ++ } ++ + /* instanceName can be samAccountName without $ or dnsHostName + * or "ntds_guid._msdcs.forest_domain for DC objects */ +- if (strlen(instanceName) == (strlen(samAccountName) - 1) ++ if (strlen(instanceName) == account_name_len + && strncasecmp(instanceName, samAccountName, +- strlen(samAccountName) - 1) == 0) { ++ account_name_len) == 0) { + goto success; + } + if ((dnsHostName != NULL) && +-- +1.8.3.1 + diff --git a/backport-0013-CVE-2022-32743-dsdb-modules-acl-Allow-simultaneous-s.patch b/backport-0013-CVE-2022-32743-dsdb-modules-acl-Allow-simultaneous-s.patch new file mode 100644 index 0000000000000000000000000000000000000000..7a79af13242e1a6835a7ba6e4744cf34b90b87b8 --- /dev/null +++ b/backport-0013-CVE-2022-32743-dsdb-modules-acl-Allow-simultaneous-s.patch @@ -0,0 +1,217 @@ +From e1c52ac05a9ff505d2e5eac2f1ece4e95844ee71 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Tue, 7 Jun 2022 17:38:55 +1200 +Subject: [PATCH 13/15] CVE-2022-32743 dsdb/modules/acl: Allow simultaneous + sAMAccountName, dNSHostName, and servicePrincipalName change + +If the message changes the sAMAccountName, we'll check dNSHostName and +servicePrincipalName values against the new value of sAMAccountName, +rather than the account's current value. Similarly, if the message +changes the dNSHostName, we'll check servicePrincipalName values against +the new dNSHostName. This allows setting more than one of these +attributes simultaneously with validated write rights. + +We now pass 'struct ldb_val' to acl_validate_spn_value() instead of +simple strings. Previously, we were relying on the data inside 'struct +ldb_val' having a terminating zero byte, even though this is not +guaranteed. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + selftest/knownfail.d/netlogon-dns-host-name | 2 - + selftest/knownfail.d/validated-dns-host-name | 3 - + source4/dsdb/samdb/ldb_modules/acl.c | 85 +++++++++++++++++++++------- + 3 files changed, 65 insertions(+), 25 deletions(-) + delete mode 100644 selftest/knownfail.d/netlogon-dns-host-name + delete mode 100644 selftest/knownfail.d/validated-dns-host-name + +diff --git a/selftest/knownfail.d/netlogon-dns-host-name b/selftest/knownfail.d/netlogon-dns-host-name +deleted file mode 100644 +index 3eca0cd..0000000 +--- a/selftest/knownfail.d/netlogon-dns-host-name ++++ /dev/null +@@ -1,2 +0,0 @@ +-^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_valid\( +-^samba.tests.py_credentials.samba.tests.py_credentials.PyCredentialsTests.test_set_dns_hostname_valid_denied\( +diff --git a/selftest/knownfail.d/validated-dns-host-name b/selftest/knownfail.d/validated-dns-host-name +deleted file mode 100644 +index 4b61658..0000000 +--- a/selftest/knownfail.d/validated-dns-host-name ++++ /dev/null +@@ -1,3 +0,0 @@ +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_spn\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_dns_host_name_spn_matching_account_name_new\( +-^samba4.ldap.acl.python.*__main__.AclModifyTests.test_modify_spn_matching_dns_host_name_invalid\( +diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c +index 82f6ec3..4098ae2 100644 +--- a/source4/dsdb/samdb/ldb_modules/acl.c ++++ b/source4/dsdb/samdb/ldb_modules/acl.c +@@ -529,10 +529,10 @@ static int acl_sDRightsEffective(struct ldb_module *module, + + static int acl_validate_spn_value(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb, +- const char *spn_value, ++ const struct ldb_val *spn_value, + uint32_t userAccountControl, +- const char *samAccountName, +- const char *dnsHostName, ++ const struct ldb_val *samAccountName, ++ const struct ldb_val *dnsHostName, + const char *netbios_name, + const char *ntds_guid) + { +@@ -543,6 +543,7 @@ static int acl_validate_spn_value(TALLOC_CTX *mem_ctx, + char *instanceName; + char *serviceType; + char *serviceName; ++ const char *spn_value_str = NULL; + size_t account_name_len; + const char *forest_name = samdb_forest_name(ldb, mem_ctx); + const char *base_domain = samdb_default_domain_name(ldb, mem_ctx); +@@ -551,7 +552,18 @@ static int acl_validate_spn_value(TALLOC_CTX *mem_ctx, + bool is_dc = (userAccountControl & UF_SERVER_TRUST_ACCOUNT) || + (userAccountControl & UF_PARTIAL_SECRETS_ACCOUNT); + +- if (strcasecmp_m(spn_value, samAccountName) == 0) { ++ spn_value_str = talloc_strndup(mem_ctx, ++ (const char *)spn_value->data, ++ spn_value->length); ++ if (spn_value_str == NULL) { ++ return ldb_oom(ldb); ++ } ++ ++ if (spn_value->length == samAccountName->length && ++ strncasecmp((const char *)spn_value->data, ++ (const char *)samAccountName->data, ++ spn_value->length) == 0) ++ { + /* MacOS X sets this value, and setting an SPN of your + * own samAccountName is both pointless and safe */ + return LDB_SUCCESS; +@@ -565,7 +577,7 @@ static int acl_validate_spn_value(TALLOC_CTX *mem_ctx, + "Could not initialize kerberos context."); + } + +- ret = krb5_parse_name(krb_ctx, spn_value, &principal); ++ ret = krb5_parse_name(krb_ctx, spn_value_str, &principal); + if (ret) { + krb5_free_context(krb_ctx); + return LDB_ERR_CONSTRAINT_VIOLATION; +@@ -618,8 +630,10 @@ static int acl_validate_spn_value(TALLOC_CTX *mem_ctx, + } + } + +- account_name_len = strlen(samAccountName); +- if (account_name_len && samAccountName[account_name_len - 1] == '$') { ++ account_name_len = samAccountName->length; ++ if (account_name_len && ++ samAccountName->data[account_name_len - 1] == '$') ++ { + /* Account for the '$' character. */ + --account_name_len; + } +@@ -627,12 +641,18 @@ static int acl_validate_spn_value(TALLOC_CTX *mem_ctx, + /* instanceName can be samAccountName without $ or dnsHostName + * or "ntds_guid._msdcs.forest_domain for DC objects */ + if (strlen(instanceName) == account_name_len +- && strncasecmp(instanceName, samAccountName, +- account_name_len) == 0) { ++ && strncasecmp(instanceName, ++ (const char *)samAccountName->data, ++ account_name_len) == 0) ++ { + goto success; + } + if ((dnsHostName != NULL) && +- (strcasecmp(instanceName, dnsHostName) == 0)) { ++ strlen(instanceName) == dnsHostName->length && ++ (strncasecmp(instanceName, ++ (const char *)dnsHostName->data, ++ dnsHostName->length) == 0)) ++ { + goto success; + } + if (is_dc) { +@@ -650,10 +670,13 @@ fail: + krb5_free_context(krb_ctx); + ldb_debug_set(ldb, LDB_DEBUG_WARNING, + "acl: spn validation failed for " +- "spn[%s] uac[0x%x] account[%s] hostname[%s] " ++ "spn[%.*s] uac[0x%x] account[%.*s] hostname[%.*s] " + "nbname[%s] ntds[%s] forest[%s] domain[%s]\n", +- spn_value, (unsigned)userAccountControl, +- samAccountName, dnsHostName, ++ (int)spn_value->length, spn_value->data, ++ (unsigned)userAccountControl, ++ (int)samAccountName->length, samAccountName->data, ++ dnsHostName != NULL ? (int)dnsHostName->length : 0, ++ dnsHostName != NULL ? (const char *)dnsHostName->data : "", + netbios_name, ntds_guid, + forest_name, base_domain); + return LDB_ERR_CONSTRAINT_VIOLATION; +@@ -686,9 +709,9 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx, + struct ldb_result *netbios_res; + struct ldb_dn *partitions_dn = samdb_partitions_dn(ldb, tmp_ctx); + uint32_t userAccountControl; +- const char *samAccountName; +- const char *dnsHostName; + const char *netbios_name; ++ const struct ldb_val *dns_host_name_val = NULL; ++ const struct ldb_val *sam_account_name_val = NULL; + struct GUID ntds; + char *ntds_guid = NULL; + +@@ -773,9 +796,31 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx, + return ret; + } + ++ dns_host_name_val = ldb_msg_find_ldb_val(acl_res->msgs[0], "dNSHostName"); ++ ++ ret = dsdb_msg_get_single_value(req->op.mod.message, ++ "dNSHostName", ++ dns_host_name_val, ++ &dns_host_name_val, ++ req->operation); ++ if (ret != LDB_SUCCESS) { ++ talloc_free(tmp_ctx); ++ return ret; ++ } ++ + userAccountControl = ldb_msg_find_attr_as_uint(acl_res->msgs[0], "userAccountControl", 0); +- dnsHostName = ldb_msg_find_attr_as_string(acl_res->msgs[0], "dnsHostName", NULL); +- samAccountName = ldb_msg_find_attr_as_string(acl_res->msgs[0], "samAccountName", NULL); ++ ++ sam_account_name_val = ldb_msg_find_ldb_val(acl_res->msgs[0], "sAMAccountName"); ++ ++ ret = dsdb_msg_get_single_value(req->op.mod.message, ++ "sAMAccountName", ++ sam_account_name_val, ++ &sam_account_name_val, ++ req->operation); ++ if (ret != LDB_SUCCESS) { ++ talloc_free(tmp_ctx); ++ return ret; ++ } + + ret = dsdb_module_search(module, tmp_ctx, + &netbios_res, partitions_dn, +@@ -806,10 +851,10 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx, + for (i=0; i < el->num_values; i++) { + ret = acl_validate_spn_value(tmp_ctx, + ldb, +- (char *)el->values[i].data, ++ &el->values[i], + userAccountControl, +- samAccountName, +- dnsHostName, ++ sam_account_name_val, ++ dns_host_name_val, + netbios_name, + ntds_guid); + if (ret != LDB_SUCCESS) { +-- +1.8.3.1 + diff --git a/backport-0014-CVE-2022-32743-s4-rpc_server-common-Add-dcesrv_samdb.patch b/backport-0014-CVE-2022-32743-s4-rpc_server-common-Add-dcesrv_samdb.patch new file mode 100644 index 0000000000000000000000000000000000000000..300b077857e2f01c06f0a8c5664e6935410ed64a --- /dev/null +++ b/backport-0014-CVE-2022-32743-s4-rpc_server-common-Add-dcesrv_samdb.patch @@ -0,0 +1,159 @@ +From 6b76bc7339addb14884c2d6ddb20c559c7fbe07d Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Thu, 9 Jun 2022 19:32:30 +1200 +Subject: [PATCH 14/15] CVE-2022-32743 s4:rpc_server/common: Add + dcesrv_samdb_connect_session_info() + +This function allows us to connect to samdb as a particular user by +passing in that user's session info. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall +--- + source4/rpc_server/common/common.h | 1 + + source4/rpc_server/common/server_info.c | 65 ++++++++++++++++++++------------- + 2 files changed, 40 insertions(+), 26 deletions(-) + +diff --git a/source4/rpc_server/common/common.h b/source4/rpc_server/common/common.h +index 7d2f8c5..b57ddf2 100644 +--- a/source4/rpc_server/common/common.h ++++ b/source4/rpc_server/common/common.h +@@ -30,6 +30,7 @@ struct dcesrv_context; + struct dcesrv_call_state; + struct ndr_interface_table; + struct ncacn_packet; ++struct auth_session_info; + + struct dcerpc_server_info { + const char *domain_name; +diff --git a/source4/rpc_server/common/server_info.c b/source4/rpc_server/common/server_info.c +index a2af376..34228c3 100644 +--- a/source4/rpc_server/common/server_info.c ++++ b/source4/rpc_server/common/server_info.c +@@ -190,48 +190,44 @@ bool dcesrv_common_validate_share_name(TALLOC_CTX *mem_ctx, const char *share_na + return true; + } + +-static struct ldb_context *dcesrv_samdb_connect_common( ++/* ++ * call_session_info is session info for samdb. call_audit_session_info is for ++ * auditing and may be NULL. ++ */ ++struct ldb_context *dcesrv_samdb_connect_session_info( + TALLOC_CTX *mem_ctx, + struct dcesrv_call_state *dce_call, +- bool as_system) ++ const struct auth_session_info *call_session_info, ++ const struct auth_session_info *call_audit_session_info) + { + struct ldb_context *samdb = NULL; +- struct auth_session_info *system_session_info = NULL; +- const struct auth_session_info *call_session_info = +- dcesrv_call_session_info(dce_call); + struct auth_session_info *user_session_info = NULL; +- struct auth_session_info *ldb_session_info = NULL; + struct auth_session_info *audit_session_info = NULL; + struct tsocket_address *remote_address = NULL; + +- if (as_system) { +- system_session_info = system_session(dce_call->conn->dce_ctx->lp_ctx); +- if (system_session_info == NULL) { +- return NULL; +- } +- } +- + user_session_info = copy_session_info(mem_ctx, call_session_info); + if (user_session_info == NULL) { + return NULL; + } + ++ if (call_audit_session_info != NULL) { ++ audit_session_info = copy_session_info(mem_ctx, call_audit_session_info); ++ if (audit_session_info == NULL) { ++ talloc_free(user_session_info); ++ return NULL; ++ } ++ } ++ + if (dce_call->conn->remote_address != NULL) { + remote_address = tsocket_address_copy(dce_call->conn->remote_address, + user_session_info); + if (remote_address == NULL) { ++ TALLOC_FREE(audit_session_info); ++ talloc_free(user_session_info); + return NULL; + } + } + +- if (system_session_info != NULL) { +- ldb_session_info = system_session_info; +- audit_session_info = user_session_info; +- } else { +- ldb_session_info = user_session_info; +- audit_session_info = NULL; +- } +- + /* + * We need to make sure every argument + * stays arround for the lifetime of 'samdb', +@@ -253,10 +249,11 @@ static struct ldb_context *dcesrv_samdb_connect_common( + mem_ctx, + dce_call->event_ctx, + dce_call->conn->dce_ctx->lp_ctx, +- ldb_session_info, ++ user_session_info, + remote_address, + 0); + if (samdb == NULL) { ++ TALLOC_FREE(audit_session_info); + talloc_free(user_session_info); + return NULL; + } +@@ -265,6 +262,8 @@ static struct ldb_context *dcesrv_samdb_connect_common( + if (audit_session_info != NULL) { + int ret; + ++ talloc_steal(samdb, audit_session_info); ++ + ret = ldb_set_opaque(samdb, + DSDB_NETWORK_SESSION_INFO, + audit_session_info); +@@ -288,8 +287,18 @@ struct ldb_context *dcesrv_samdb_connect_as_system( + TALLOC_CTX *mem_ctx, + struct dcesrv_call_state *dce_call) + { +- return dcesrv_samdb_connect_common(mem_ctx, dce_call, +- true /* as_system */); ++ const struct auth_session_info *system_session_info = NULL; ++ const struct auth_session_info *call_session_info = NULL; ++ ++ system_session_info = system_session(dce_call->conn->dce_ctx->lp_ctx); ++ if (system_session_info == NULL) { ++ return NULL; ++ } ++ ++ call_session_info = dcesrv_call_session_info(dce_call); ++ ++ return dcesrv_samdb_connect_session_info(mem_ctx, dce_call, ++ system_session_info, call_session_info); + } + + /* +@@ -301,6 +310,10 @@ struct ldb_context *dcesrv_samdb_connect_as_user( + TALLOC_CTX *mem_ctx, + struct dcesrv_call_state *dce_call) + { +- return dcesrv_samdb_connect_common(mem_ctx, dce_call, +- false /* not as_system */); ++ const struct auth_session_info *call_session_info = NULL; ++ ++ call_session_info = dcesrv_call_session_info(dce_call); ++ ++ return dcesrv_samdb_connect_session_info(mem_ctx, dce_call, ++ call_session_info, NULL); + } +-- +1.8.3.1 + diff --git a/backport-0015-CVE-2022-32743-s4-rpc_server-netlogon-Reconnect-to-s.patch b/backport-0015-CVE-2022-32743-s4-rpc_server-netlogon-Reconnect-to-s.patch new file mode 100644 index 0000000000000000000000000000000000000000..22ff1b2bcaf0ed4309cc14fe1d3ec45479fa1eca --- /dev/null +++ b/backport-0015-CVE-2022-32743-s4-rpc_server-netlogon-Reconnect-to-s.patch @@ -0,0 +1,70 @@ +From 15c86028a861139cee4560fe093c965ffc30eb13 Mon Sep 17 00:00:00 2001 +From: Joseph Sutton +Date: Thu, 9 Jun 2022 19:46:07 +1200 +Subject: [PATCH 15/15] CVE-2022-32743 s4:rpc_server/netlogon: Reconnect to + samdb as workstation account + +This ensures that the database update can be attributed to the +workstation account, rather than to the anonymous SID, in the audit +logs. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 + +Signed-off-by: Joseph Sutton +Reviewed-by: Douglas Bagnall + +Autobuild-User(master): Douglas Bagnall +Autobuild-Date(master): Thu Jul 28 23:41:27 UTC 2022 on sn-devel-184 +--- + source4/rpc_server/netlogon/dcerpc_netlogon.c | 28 +++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c +index 15cd27b..12ad780 100644 +--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c ++++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c +@@ -2422,6 +2422,7 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal + struct ldb_dn *workstation_dn; + struct netr_DomainInformation *domain_info; + struct netr_LsaPolicyInformation *lsa_policy_info; ++ struct auth_session_info *workstation_session_info = NULL; + uint32_t default_supported_enc_types = 0xFFFFFFFF; + bool update_dns_hostname = true; + int ret, i; +@@ -2468,6 +2469,33 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal + dom_sid_string(mem_ctx, creds->sid)); + NT_STATUS_HAVE_NO_MEMORY(workstation_dn); + ++ /* Get the workstation's session info from the database. */ ++ status = authsam_get_session_info_principal(mem_ctx, ++ dce_call->conn->dce_ctx->lp_ctx, ++ sam_ctx, ++ NULL, /* principal */ ++ workstation_dn, ++ 0, /* session_info_flags */ ++ &workstation_session_info); ++ if (!NT_STATUS_IS_OK(status)) { ++ return status; ++ } ++ ++ /* ++ * Reconnect to samdb as the workstation, now that we have its ++ * session info. We do this so the database update can be ++ * attributed to the workstation account in the audit logs -- ++ * otherwise it might be incorrectly attributed to ++ * SID_NT_ANONYMOUS. ++ */ ++ sam_ctx = dcesrv_samdb_connect_session_info(mem_ctx, ++ dce_call, ++ workstation_session_info, ++ workstation_session_info); ++ if (sam_ctx == NULL) { ++ return NT_STATUS_INVALID_SYSTEM_SERVICE; ++ } ++ + /* Lookup for attributes in workstation object */ + ret = gendb_search_dn(sam_ctx, mem_ctx, workstation_dn, &res1, + attrs2); +-- +1.8.3.1 + diff --git a/samba.spec b/samba.spec index 8734f9f76390b739888fe9eb8a8b3775059010e3..a7ec3f6ba5d948f15a9e81b1fea32cf0ed9d2a5e 100644 --- a/samba.spec +++ b/samba.spec @@ -49,7 +49,7 @@ Name: samba Version: 4.11.12 -Release: 15 +Release: 16 Summary: A suite for Linux to interoperate with Windows License: GPLv3+ and LGPLv3+ @@ -264,6 +264,21 @@ Patch6334: backport-0001-CVE-2022-32745.patch Patch6335: backport-0002-CVE-2022-32745.patch Patch6336: backport-0003-CVE-2022-32745.patch Patch6337: backport-0004-CVE-2022-32745.patch +Patch6338: backport-0001-CVE-2022-32743-s4-acl-Add-tests-for-validated-dNSHos.patch +Patch6339: backport-0002-CVE-2022-32743-tests-py_credentials-Add-tests-for-se.patch +Patch6340: backport-0003-CVE-2022-32743-s4-torture-rpc-Fix-tests-to-match-Win.patch +Patch6341: backport-0004-CVE-2022-32743-s4-dsdb-util-Add-dsdb_msg_get_single_.patch +Patch6342: backport-0005-CVE-2022-32743-s4-dsdb-util-Add-function-to-check-fo.patch +Patch6343: backport-0006-CVE-2022-32743-dsdb-Implement-validated-dNSHostName-.patch +Patch6344: backport-0007-CVE-2022-32743-dsdb-common-Add-FORCE_ALLOW_VALIDATED.patch +Patch6345: backport-0008-CVE-2022-32743-dsdb-modules-acl-Handle-FORCE_ALLOW_V.patch +Patch6346: backport-0009-CVE-2022-32743-s4-rpc_server-netlogon-Remove-dNSHost.patch +Patch6347: backport-0010-CVE-2022-32743-s4-rpc_server-netlogon-Always-observe.patch +Patch6348: backport-0011-CVE-2022-32743-s4-rpc_server-netlogon-Connect-to-sam.patch +Patch6349: backport-0012-CVE-2022-32743-dsdb-modules-acl-Account-for-sAMAccou.patch +Patch6350: backport-0013-CVE-2022-32743-dsdb-modules-acl-Allow-simultaneous-s.patch +Patch6351: backport-0014-CVE-2022-32743-s4-rpc_server-common-Add-dcesrv_samdb.patch +Patch6352: backport-0015-CVE-2022-32743-s4-rpc_server-netlogon-Reconnect-to-s.patch BuildRequires: avahi-devel cups-devel dbus-devel docbook-style-xsl e2fsprogs-devel gawk gnupg2 gnutls-devel >= 3.4.7 gpgme-devel @@ -3253,6 +3268,12 @@ fi %{_mandir}/man* %changelog +* Mon Aug 29 2022 zhouyh - 4.11.12-16 +- Type:cves +- CVE:CVE-2022-32743 +- SUG:NA +- DESC:fix CVE-2022-32743 + * Mon Aug 15 2022 xinghe - 4.11.12-15 - Type:cves - CVE:CVE-2022-32745