[SCM] Samba Shared Repository - branch master updated

Douglas Bagnall dbagnall at samba.org
Thu Jul 28 23:42:01 UTC 2022


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 <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    
    Autobuild-User(master): Douglas Bagnall <dbagnall at samba.org>
    Autobuild-Date(master): Thu Jul 28 23:41:27 UTC 2022 on sn-devel-184

commit 6b76bc7339addb14884c2d6ddb20c559c7fbe07d
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit e1c52ac05a9ff505d2e5eac2f1ece4e95844ee71
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 7638abd38a13f9d2b5c769eb12c70eacf49b3806
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit f545142380151a626848dbae9ee746167f3299fa
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 02c2a8c7b01d6412393423813b710c88b20fb97f
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit d07641fc5a7d2fa323e6d6fe3223da3a6d682405
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit f9831259b9f6a49b9e1a7be75198d60374cdef2f
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit c2ab1f4696fa3f52918a126d0b37993a07f68bcb
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit b95431ab2303eb258e37e88d8841f2fb79fc4af5
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 0d888f0c902ebd98cfb82d50ab8b8b3928341ee2
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 49ac07e786df58b914ee85e2db773c0ba8d4e171
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit e38b75a50f79c1d1ea2d7d4489896ca5aa16d9d9
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit b41691d0e546795bda994d94091b8e0a03ab96d6
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit d277700710dc118f61065ed9e16e08e76820b66a
Author: Joseph Sutton <josephsutton at 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 <josephsutton at catalyst.net.nz>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at 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



More information about the samba-cvs mailing list