The branch, master has been updated via 15c86028a86 CVE-2022-32743 s4:rpc_server/netlogon: Reconnect to samdb as workstation account via 6b76bc7339a CVE-2022-32743 s4:rpc_server/common: Add dcesrv_samdb_connect_session_info() via e1c52ac05a9 CVE-2022-32743 dsdb/modules/acl: Allow simultaneous sAMAccountName, dNSHostName, and servicePrincipalName change via 7638abd38a1 CVE-2022-32743 dsdb/modules/acl: Account for sAMAccountName without $ via f5451423801 CVE-2022-32743 s4:rpc_server/netlogon: Connect to samdb as a user, rather than as system via 02c2a8c7b01 CVE-2022-32743 s4:rpc_server/netlogon: Always observe NETR_WS_FLAG_HANDLES_SPN_UPDATE flag via d07641fc5a7 CVE-2022-32743 s4:rpc_server/netlogon: Remove dNSHostName prefix check via f9831259b9f CVE-2022-32743 dsdb/modules/acl: Handle FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE control via c2ab1f4696f CVE-2022-32743 dsdb/common: Add FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE control via b95431ab230 CVE-2022-32743 dsdb: Implement validated dNSHostName write via 0d888f0c902 CVE-2022-32743 s4/dsdb/util: Add function to check for a subclass relationship via 49ac07e786d CVE-2022-32743 s4/dsdb/util: Add dsdb_msg_get_single_value() via e38b75a50f7 CVE-2022-32743 s4:torture/rpc: Fix tests to match Windows via b41691d0e54 CVE-2022-32743 tests/py_credentials: Add tests for setting dNSHostName with LogonGetDomainInfo() via d277700710d CVE-2022-32743 s4-acl: Add tests for validated dNSHostName write from ab3d2379415 examples/winexe: fix fetching return code of the remote command
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 15c86028a861139cee4560fe093c965ffc30eb13 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Thu Jun 9 19:46:07 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Autobuild-User(master): Douglas Bagnall <dbagn...@samba.org> Autobuild-Date(master): Thu Jul 28 23:41:27 UTC 2022 on sn-devel-184 commit 6b76bc7339addb14884c2d6ddb20c559c7fbe07d Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Thu Jun 9 19:32:30 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit e1c52ac05a9ff505d2e5eac2f1ece4e95844ee71 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Jun 7 17:38:55 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit 7638abd38a13f9d2b5c769eb12c70eacf49b3806 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Jun 7 17:37:34 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit f545142380151a626848dbae9ee746167f3299fa Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Jun 7 17:29:02 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit 02c2a8c7b01d6412393423813b710c88b20fb97f Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Jun 7 17:25:28 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit d07641fc5a7d2fa323e6d6fe3223da3a6d682405 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Thu Jun 2 17:11:08 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit f9831259b9f6a49b9e1a7be75198d60374cdef2f Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Jun 7 17:39:07 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit c2ab1f4696fa3f52918a126d0b37993a07f68bcb Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Jun 7 17:36:43 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit b95431ab2303eb258e37e88d8841f2fb79fc4af5 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Wed Jun 1 16:08:42 2022 +1200 CVE-2022-32743 dsdb: Implement validated dNSHostName write BUG: https://bugzilla.samba.org/show_bug.cgi?id=14833 Signed-off-by: Joseph Sutton <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit 0d888f0c902ebd98cfb82d50ab8b8b3928341ee2 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Jun 14 14:16:10 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit 49ac07e786df58b914ee85e2db773c0ba8d4e171 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Jun 7 17:36:56 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit e38b75a50f79c1d1ea2d7d4489896ca5aa16d9d9 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Jun 14 17:19:00 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit b41691d0e546795bda994d94091b8e0a03ab96d6 Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Tue Jun 7 17:35:35 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit d277700710dc118f61065ed9e16e08e76820b66a Author: Joseph Sutton <josephsut...@catalyst.net.nz> Date: Wed Jun 1 16:07:17 2022 +1200 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 <josephsut...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> ----------------------------------------------------------------------- Summary of changes: python/samba/tests/py_credentials.py | 281 +++++++++- source4/dsdb/common/util.c | 7 + source4/dsdb/samdb/ldb_modules/acl.c | 464 ++++++++++++++-- source4/dsdb/samdb/ldb_modules/util.c | 145 +++++ source4/dsdb/samdb/ldb_modules/util.h | 1 + source4/dsdb/samdb/samdb.h | 6 + source4/dsdb/tests/python/acl.py | 757 ++++++++++++++++++++++++++ source4/rpc_server/common/common.h | 1 + source4/rpc_server/common/server_info.c | 65 ++- source4/rpc_server/netlogon/dcerpc_netlogon.c | 61 ++- source4/torture/rpc/netlogon.c | 12 +- 11 files changed, 1694 insertions(+), 106 deletions(-) Changeset truncated at 500 lines: diff --git a/python/samba/tests/py_credentials.py b/python/samba/tests/py_credentials.py index ecb8271b595..0c442b81f3f 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/source4/dsdb/common/util.c b/source4/dsdb/common/util.c index 112917544a3..88b05555b96 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/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c index 1fc6dbffc84..4098ae2d671 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,8 @@ 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); struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"), @@ -550,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; @@ -564,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; @@ -616,15 +629,30 @@ static int acl_validate_spn_value(TALLOC_CTX *mem_ctx, } } } + + account_name_len = samAccountName->length; + if (account_name_len && + samAccountName->data[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) - && strncasecmp(instanceName, samAccountName, - strlen(samAccountName) - 1) == 0) { + if (strlen(instanceName) == account_name_len + && 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) { @@ -642,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; @@ -667,7 +698,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; @@ -677,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; @@ -694,34 +726,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, -- Samba Shared Repository