!109 [sync] PR-108: fix CVE-2022-32743
From: @openeuler-sync-bot Reviewed-by: @seuzw Signed-off-by: @seuzw
This commit is contained in:
commit
c0626ee444
815
0001-CVE-2022-32743-s4-acl-Add-tests-for-validated-dNSHos.patch
Normal file
815
0001-CVE-2022-32743-s4-acl-Add-tests-for-validated-dNSHos.patch
Normal file
@ -0,0 +1,815 @@
|
||||
From d277700710dc118f61065ed9e16e08e76820b66a Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
346
0002-CVE-2022-32743-tests-py_credentials-Add-tests-for-se.patch
Normal file
346
0002-CVE-2022-32743-tests-py_credentials-Add-tests-for-se.patch
Normal file
@ -0,0 +1,346 @@
|
||||
From b41691d0e546795bda994d94091b8e0a03ab96d6 Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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.common 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
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
From e38b75a50f79c1d1ea2d7d4489896ca5aa16d9d9 Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
140
0004-CVE-2022-32743-s4-dsdb-util-Add-dsdb_msg_get_single_.patch
Normal file
140
0004-CVE-2022-32743-s4-dsdb-util-Add-dsdb_msg_get_single_.patch
Normal file
@ -0,0 +1,140 @@
|
||||
From 49ac07e786df58b914ee85e2db773c0ba8d4e171 Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* This function determines the (last) structural or 88 object class of a passed
|
||||
* "objectClass" attribute - per MS-ADTS 3.1.1.1.4 this is the last value.
|
||||
--
|
||||
1.8.3.1
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
From 0d888f0c902ebd98cfb82d50ab8b8b3928341ee2 Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
339
0006-CVE-2022-32743-dsdb-Implement-validated-dNSHostName-.patch
Normal file
339
0006-CVE-2022-32743-dsdb-Implement-validated-dNSHostName-.patch
Normal file
@ -0,0 +1,339 @@
|
||||
From b95431ab2303eb258e37e88d8841f2fb79fc4af5 Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
From c2ab1f4696fa3f52918a126d0b37993a07f68bcb Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
240
0008-CVE-2022-32743-dsdb-modules-acl-Handle-FORCE_ALLOW_V.patch
Normal file
240
0008-CVE-2022-32743-dsdb-modules-acl-Handle-FORCE_ALLOW_V.patch
Normal file
@ -0,0 +1,240 @@
|
||||
From f9831259b9f6a49b9e1a7be75198d60374cdef2f Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
From d07641fc5a7d2fa323e6d6fe3223da3a6d682405 Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
From 02c2a8c7b01d6412393423813b710c88b20fb97f Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
From f545142380151a626848dbae9ee746167f3299fa Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
From 7638abd38a13f9d2b5c769eb12c70eacf49b3806 Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
217
0013-CVE-2022-32743-dsdb-modules-acl-Allow-simultaneous-s.patch
Normal file
217
0013-CVE-2022-32743-dsdb-modules-acl-Allow-simultaneous-s.patch
Normal file
@ -0,0 +1,217 @@
|
||||
From e1c52ac05a9ff505d2e5eac2f1ece4e95844ee71 Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
159
0014-CVE-2022-32743-s4-rpc_server-common-Add-dcesrv_samdb.patch
Normal file
159
0014-CVE-2022-32743-s4-rpc_server-common-Add-dcesrv_samdb.patch
Normal file
@ -0,0 +1,159 @@
|
||||
From 6b76bc7339addb14884c2d6ddb20c559c7fbe07d Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
---
|
||||
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
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
From 15c86028a861139cee4560fe093c965ffc30eb13 Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Sutton <josephsutton@catalyst.net.nz>
|
||||
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 <josephsutton@catalyst.net.nz>
|
||||
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
||||
|
||||
Autobuild-User(master): Douglas Bagnall <dbagnall@samba.org>
|
||||
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
|
||||
|
||||
23
samba.spec
23
samba.spec
@ -49,7 +49,7 @@
|
||||
|
||||
Name: samba
|
||||
Version: 4.15.3
|
||||
Release: 8
|
||||
Release: 9
|
||||
|
||||
Summary: A suite for Linux to interoperate with Windows
|
||||
License: GPLv3+ and LGPLv3+
|
||||
@ -78,6 +78,21 @@ Patch8: backport-CVE-2022-32746.patch
|
||||
Patch9: backport-CVE-2022-32745.patch
|
||||
Patch10: backport-CVE-2022-2031-CVE-2022-32744.patch
|
||||
Patch11: backport-CVE-2022-32742.patch
|
||||
Patch12: 0001-CVE-2022-32743-s4-acl-Add-tests-for-validated-dNSHos.patch
|
||||
Patch13: 0002-CVE-2022-32743-tests-py_credentials-Add-tests-for-se.patch
|
||||
Patch14: 0003-CVE-2022-32743-s4-torture-rpc-Fix-tests-to-match-Win.patch
|
||||
Patch15: 0004-CVE-2022-32743-s4-dsdb-util-Add-dsdb_msg_get_single_.patch
|
||||
Patch16: 0005-CVE-2022-32743-s4-dsdb-util-Add-function-to-check-fo.patch
|
||||
Patch17: 0006-CVE-2022-32743-dsdb-Implement-validated-dNSHostName-.patch
|
||||
Patch18: 0007-CVE-2022-32743-dsdb-common-Add-FORCE_ALLOW_VALIDATED.patch
|
||||
Patch19: 0008-CVE-2022-32743-dsdb-modules-acl-Handle-FORCE_ALLOW_V.patch
|
||||
Patch20: 0009-CVE-2022-32743-s4-rpc_server-netlogon-Remove-dNSHost.patch
|
||||
Patch21: 0010-CVE-2022-32743-s4-rpc_server-netlogon-Always-observe.patch
|
||||
Patch22: 0011-CVE-2022-32743-s4-rpc_server-netlogon-Connect-to-sam.patch
|
||||
Patch23: 0012-CVE-2022-32743-dsdb-modules-acl-Account-for-sAMAccou.patch
|
||||
Patch24: 0013-CVE-2022-32743-dsdb-modules-acl-Allow-simultaneous-s.patch
|
||||
Patch25: 0014-CVE-2022-32743-s4-rpc_server-common-Add-dcesrv_samdb.patch
|
||||
Patch26: 0015-CVE-2022-32743-s4-rpc_server-netlogon-Reconnect-to-s.patch
|
||||
|
||||
BuildRequires: avahi-devel bison dbus-devel docbook-style-xsl e2fsprogs-devel flex gawk gnupg2 gnutls-devel >= 3.4.7 gpgme-devel
|
||||
BuildRequires: jansson-devel krb5-devel >= %{required_mit_krb5} libacl-devel libaio-devel libarchive-devel libattr-devel
|
||||
@ -3401,6 +3416,12 @@ fi
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Fri Aug 26 2022 zhouyihang <zhouyihang3@h-partners.com> - 4.15.3-9
|
||||
- Type:cves
|
||||
- ID:CVE-2022-32743
|
||||
- SUG:NA
|
||||
- DESC:fix CVE-2022-32743
|
||||
|
||||
* Fri Aug 12 2022 xinghe <xinghe2@h-partners.com> - 4.15.3-8
|
||||
- Type:bugfix
|
||||
- ID:NA
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user