s4-subdomain more work on sub-domain join
authorAndrew Bartlett <abartlet@samba.org>
Fri, 26 Aug 2011 03:23:41 +0000 (13:23 +1000)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 13 Sep 2011 05:38:02 +0000 (15:38 +1000)
we can now create a subdomain of an existing windows domain using:

  samba-tool domain join sub.domain.dns.name subdomain

The ordering of the creation of the key records is quite tricky,
especially for the NTDSDSA object

Pair-Programmed-With: Andrew Tridgell <tridge@samba.org>

selftest/target/Samba4.pm
source4/librpc/rpc/pyrpc.c
source4/scripting/python/samba/join.py

index ff8f5ee84bf294d3aad1a5b1e54debf2213e8760..b356048869f8f62c50931b75d27c3ff3c5e76d25 100644 (file)
@@ -1647,7 +1647,7 @@ sub setup_subdom_dc($$$)
                $cmd .= " KRB5_CONFIG=\"$env->{KRB5_CONFIG}\"";
                $cmd .= " $samba_tool drs kcc $env->{DC_SERVER}";
                $cmd .= " $env->{CONFIGURATION}";
-               $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
+               $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD} --realm=$dc_vars->{DC_REALM}";
                unless (system($cmd) == 0) {
                        warn("Failed to exec kcc\n$cmd");
                        return undef;
@@ -1661,7 +1661,7 @@ sub setup_subdom_dc($$$)
                $cmd .= " KRB5_CONFIG=\"$env->{KRB5_CONFIG}\"";
                $cmd .= " $samba_tool drs replicate $env->{DC_SERVER} $env->{SUBDOM_DC_SERVER}";
                $cmd .= " $dc_vars->{CONFIGURATION}";
-               $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD}";
+               $cmd .= " -U$dc_vars->{DC_USERNAME}\%$dc_vars->{DC_PASSWORD} --realm=$dc_vars->{DC_REALM}";
                # replicate Configuration NC
                my $cmd_repl = "$cmd \"$config_dn\"";
                unless(system($cmd_repl) == 0) {
index 2d6e1f6c616f7236f589b3b8ff839307a6a41b7c..7aa5ff586823a5919327745e3a542266ebf57cb1 100644 (file)
@@ -117,6 +117,17 @@ static PyObject *py_iface_transfer_syntax(PyObject *obj, void *closure)
        return py_ndr_syntax_id(&iface->pipe->transfer_syntax);
 }
 
+static PyObject *py_iface_session_key(PyObject *obj, void *closure)
+{
+       dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
+       DATA_BLOB session_key;
+
+       NTSTATUS status = dcerpc_fetch_session_key(iface->pipe, &session_key);
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       return PyString_FromStringAndSize((const char *)session_key.data, session_key.length);
+}
+
 static PyGetSetDef dcerpc_interface_getsetters[] = {
        { discard_const_p(char, "server_name"), py_iface_server_name, NULL,
          discard_const_p(char, "name of the server, if connected over SMB") },
@@ -124,6 +135,8 @@ static PyGetSetDef dcerpc_interface_getsetters[] = {
          discard_const_p(char, "syntax id of the abstract syntax") },
        { discard_const_p(char, "transfer_syntax"), py_iface_transfer_syntax, NULL, 
          discard_const_p(char, "syntax id of the transfersyntax") },
+       { discard_const_p(char, "session_key"), py_iface_session_key, NULL,
+         discard_const_p(char, "session key (as used for blob encryption on LSA and SAMR)") },
        { NULL }
 };
 
index cf2033481eb76247e64c36f212a3518a7fdac111..533c88dd6249481e36b458649b6b439d8b4658ef 100644 (file)
@@ -25,7 +25,7 @@ from samba.samdb import SamDB
 from samba import gensec, Ldb, drs_utils
 import ldb, samba, sys, os, uuid
 from samba.ndr import ndr_pack
-from samba.dcerpc import security, drsuapi, misc, nbt
+from samba.dcerpc import security, drsuapi, misc, nbt, lsa, drsblobs
 from samba.credentials import Credentials, DONT_USE_KERBEROS
 from samba.provision import secretsdb_self_join, provision, provision_fill, FILL_DRS, FILL_SUBDOMAIN
 from samba.schema import Schema
@@ -33,6 +33,7 @@ from samba.net import Net
 from samba.dcerpc import security
 import logging
 import talloc
+import random
 
 # this makes debugging easier
 talloc.enable_null_tracking()
@@ -159,6 +160,30 @@ class dc_join(object):
             if res:
                 ctx.new_krbtgt_dn = res[0]["msDS-Krbtgtlink"][0]
                 ctx.del_noerror(ctx.new_krbtgt_dn)
+
+            if ctx.subdomain:
+                binding_options = "sign"
+                lsaconn = lsa.lsarpc("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
+                                     ctx.lp, ctx.creds)
+
+                objectAttr = lsa.ObjectAttribute()
+                objectAttr.sec_qos = lsa.QosInfo()
+
+                pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
+                                                 objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
+
+                name = lsa.String()
+                name.string = ctx.realm
+                info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
+
+                lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
+
+                name = lsa.String()
+                name.string = ctx.domain_name
+                info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
+
+                lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
+
         except Exception:
             pass
 
@@ -195,6 +220,13 @@ class dc_join(object):
                                expression='ncName=%s' % ctx.samdb.get_default_basedn())
         return res[0]["nETBIOSName"][0]
 
+    def get_parent_partition_dn(ctx):
+        '''get the parent domain partition DN from parent DNS name'''
+        res = ctx.samdb.search(base=ctx.config_dn, attrs=[],
+                               expression='(&(objectclass=crossRef)(dnsRoot=%s)(systemFlags:%s:=%u))' %
+                               (ctx.parent_dnsdomain, ldb.OID_COMPARATOR_AND, samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN))
+        return str(res[0].dn)
+
     def get_mysid(ctx):
         '''get the SID of the connected user. Only works with w2k8 and later,
            so only used for RODC join'''
@@ -309,6 +341,67 @@ class dc_join(object):
                                                                 ctr.err_data.info.extended_err))
             raise RuntimeError("DsAddEntry failed")
 
+    def join_add_ntdsdsa(ctx):
+        '''add the ntdsdsa object'''
+        # FIXME: the partition (NC) assignment has to be made dynamic
+        print "Adding %s" % ctx.ntds_dn
+        rec = {
+            "dn" : ctx.ntds_dn,
+            "objectclass" : "nTDSDSA",
+            "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
+            "dMDLocation" : ctx.schema_dn}
+
+        if ctx.subdomain:
+            # the local subdomain NC doesn't exist at this time
+            # so we have to add the base_dn NC later
+            nc_list = [ ctx.config_dn, ctx.schema_dn ]
+        else:
+            nc_list = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
+
+        if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
+            rec["msDS-Behavior-Version"] = str(ctx.behavior_version)
+
+        if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003 and not ctx.subdomain:
+            rec["msDS-HasDomainNCs"] = ctx.base_dn
+
+        if ctx.RODC:
+            rec["objectCategory"] = "CN=NTDS-DSA-RO,%s" % ctx.schema_dn
+            rec["msDS-HasFullReplicaNCs"] = nc_list
+            rec["options"] = "37"
+            ctx.samdb.add(rec, ["rodc_join:1:1"])
+        else:
+            rec["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn
+            rec["HasMasterNCs"]      = nc_list
+            if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
+                rec["msDS-HasMasterNCs"] = nc_list
+            rec["options"] = "1"
+            rec["invocationId"] = ndr_pack(misc.GUID(str(uuid.uuid4())))
+            if ctx.subdomain:
+                ctx.samdb.add(rec, ['relax:0'])
+            else:
+                ctx.DsAddEntry(rec)
+
+        # find the GUID of our NTDS DN
+        res = ctx.samdb.search(base=ctx.ntds_dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"])
+        ctx.ntds_guid = misc.GUID(ctx.samdb.schema_format_value("objectGUID", res[0]["objectGUID"][0]))
+
+
+    def join_modify_ntdsdsa(ctx):
+        '''modify the ntdsdsa object to add local partitions'''
+        print "Modifying %s using system privileges" % ctx.ntds_dn
+
+        # this works around the Enterprise Admins ACL on the NTDSDSA object
+        system_session_info = system_session()
+        ctx.samdb.set_session_info(system_session_info)
+
+        m = ldb.Message()
+        m.dn = ldb.Dn(ctx.samdb, ctx.ntds_dn)
+        m["HasMasterNCs"] = ldb.MessageElement(ctx.base_dn, ldb.FLAG_MOD_ADD, "HasMasterNCs")
+        if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
+            m["msDS-HasDomainNCs"] = ldb.MessageElement(ctx.base_dn, ldb.FLAG_MOD_ADD, "msDS-HasDomainNCs")
+            m["msDS-HasMasterNCs"] = ldb.MessageElement(ctx.base_dn, ldb.FLAG_MOD_ADD, "msDS-HasMasterNCs")
+        ctx.samdb.modify(m, controls=['relax:0'])
+
     def join_add_objects(ctx):
         '''add the various objects needed for the join'''
         if ctx.acct_dn:
@@ -347,50 +440,12 @@ class dc_join(object):
 
         ctx.samdb.add(rec)
 
-        # FIXME: the partition (NC) assignment has to be made dynamic
-        print "Adding %s" % ctx.ntds_dn
-        rec = {
-            "dn" : ctx.ntds_dn,
-            "objectclass" : "nTDSDSA",
-            "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
-            "dMDLocation" : ctx.schema_dn}
-
-        if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
-            rec["msDS-Behavior-Version"] = str(ctx.behavior_version)
-
-        if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
-            rec["msDS-HasDomainNCs"] = ctx.base_dn
-
-        if ctx.RODC:
-            rec["objectCategory"] = "CN=NTDS-DSA-RO,%s" % ctx.schema_dn
-            rec["msDS-HasFullReplicaNCs"] = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
-            rec["options"] = "37"
-            ctx.samdb.add(rec, ["rodc_join:1:1"])
-        else:
-            rec["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn
-            rec["HasMasterNCs"]      = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
-            if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
-                rec["msDS-HasMasterNCs"] = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
-            rec["options"] = "1"
-            rec["invocationId"] = ndr_pack(misc.GUID(str(uuid.uuid4())))
-            ctx.DsAddEntry(rec)
-
         if ctx.subdomain:
-            print "Adding %s" % ctx.partition_dn
-            rec = {
-                "dn" : ctx.partition_dn,
-                "objectclass" : "crossRef",
-                "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_CR_NTDS_NC|samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN),
-                "dnsRoot": ctx.dnsdomain,
-                "nCName" : extended_base_dn,
-                "nETBIOSName" : ctx.domain_name,
-                "ntMixedDomain": str(0),
-                "msDS-Behavior-Version" : str(ctx.behavior_version)}
-            ctx.DsAddEntry(rec)
+            # the rest is done after replication
+            ctx.ntds_guid = None
+            return
 
-        # find the GUID of our NTDS DN
-        res = ctx.samdb.search(base=ctx.ntds_dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"])
-        ctx.ntds_guid = misc.GUID(ctx.samdb.schema_format_value("objectGUID", res[0]["objectGUID"][0]))
+        ctx.join_add_ntdsdsa()
 
         if ctx.connection_dn is not None:
             print "Adding %s" % ctx.connection_dn
@@ -438,6 +493,28 @@ class dc_join(object):
                                                          "userAccountControl")
             ctx.samdb.modify(m)
 
+
+    def join_add_objects2(ctx):
+        '''add the various objects needed for the join, for subdomains post replication'''
+
+        if not ctx.subdomain:
+            return
+
+        print "Adding %s" % ctx.partition_dn
+        # NOTE: windows sends a ntSecurityDescriptor here, we
+        # let it default
+        rec = {
+            "dn" : ctx.partition_dn,
+            "objectclass" : "crossRef",
+            "objectCategory" : "CN=Cross-Ref,%s" % ctx.schema_dn,
+            "nCName" : ctx.base_dn,
+            "nETBIOSName" : ctx.domain_name,
+            "dnsRoot": ctx.dnsdomain,
+            "trustParent" : ctx.parent_partition_dn,
+            "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_CR_NTDS_NC|samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN)}
+        ctx.DsAddEntry(rec)
+
+
     def join_provision(ctx):
         '''provision the local SAM'''
 
@@ -454,6 +531,7 @@ class dc_join(object):
                             configdn=ctx.config_dn,
                             serverdn=ctx.server_dn, domain=ctx.domain_name,
                             hostname=ctx.myname, domainsid=ctx.domsid,
+                            domainguid=ctx.domguid,
                             machinepass=ctx.acct_pass, serverrole="domain controller",
                             sitename=ctx.site, lp=ctx.lp, ntdsguid=ctx.ntds_guid)
         print "Provision OK for domain DN %s" % presult.domaindn
@@ -465,7 +543,12 @@ class dc_join(object):
     def join_provision_own_domain(ctx):
         '''provision the local SAM'''
 
-        print "Calling bare provision"
+        # we now operate exclusively on the local database
+        ctx.samdb = ctx.local_samdb
+
+        ctx.join_add_ntdsdsa()
+
+        print("Calling own domain provision")
 
         logger = logging.getLogger("provision")
         logger.addHandler(logging.StreamHandler(sys.stdout))
@@ -474,10 +557,11 @@ class dc_join(object):
 
         presult = provision_fill(ctx.local_samdb, secrets_ldb,
                                  logger, ctx.names, ctx.paths, domainsid=security.dom_sid(ctx.domsid),
+                                 domainguid=ctx.domguid,
                                  targetdir=ctx.targetdir, samdb_fill=FILL_SUBDOMAIN,
                                  machinepass=ctx.acct_pass, serverrole="domain controller",
                                  lp=ctx.lp)
-        print "Provision OK for domain %s" % ctx.names.dnsdomain
+        print("Provision OK for domain %s" % ctx.names.dnsdomain)
 
 
     def join_replicate(ctx):
@@ -487,7 +571,10 @@ class dc_join(object):
         ctx.local_samdb.transaction_start()
         try:
             source_dsa_invocation_id = misc.GUID(ctx.samdb.get_invocation_id())
-            destination_dsa_guid = ctx.ntds_guid
+            if ctx.ntds_guid is None:
+                destination_dsa_guid = misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID_W2K3)
+            else:
+                destination_dsa_guid = ctx.ntds_guid
 
             if ctx.RODC:
                 repl_creds = Credentials()
@@ -542,6 +629,9 @@ class dc_join(object):
                                                 ldb.FLAG_MOD_REPLACE, "dsServiceName")
         ctx.local_samdb.modify(m)
 
+        if ctx.subdomain:
+            return
+
         secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
 
         print "Setting up secrets database"
@@ -554,19 +644,142 @@ class dc_join(object):
                             secure_channel_type=ctx.secure_channel_type,
                             key_version_number=ctx.key_version_number)
 
+    def join_setup_trusts(ctx):
+        '''provision the local SAM'''
+
+        def arcfour_encrypt(key, data):
+            from Crypto.Cipher import ARC4
+            c = ARC4.new(key)
+            return c.encrypt(data)
+
+        def string_to_array(string):
+            blob = [0] * len(string)
+
+            for i in range(len(string)):
+                blob[i] = ord(string[i])
+
+            return blob
+
+        print "Setup domain trusts with server %s" % ctx.server
+        binding_options = ""  # why doesn't signing work gere? w2k8r2 claims no session key
+        lsaconn = lsa.lsarpc("ncacn_np:%s[%s]" % (ctx.server, binding_options),
+                             ctx.lp, ctx.creds)
+
+        objectAttr = lsa.ObjectAttribute()
+        objectAttr.sec_qos = lsa.QosInfo()
+
+        pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
+                                         objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
+
+        info = lsa.TrustDomainInfoInfoEx()
+        info.domain_name.string = ctx.dnsdomain
+        info.netbios_name.string = ctx.domain_name
+        info.sid = security.dom_sid(ctx.domsid)
+        info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND
+        info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
+        info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST
+
+        try:
+            oldname = lsa.String()
+            oldname.string = ctx.dnsdomain
+            oldinfo = lsaconn.QueryTrustedDomainInfoByName(pol_handle, oldname,
+                                                           lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
+            print("Removing old trust record for %s (SID %s)" % (ctx.dnsdomain, oldinfo.info_ex.sid))
+            lsaconn.DeleteTrustedDomain(pol_handle, oldinfo.info_ex.sid)
+        except RuntimeError:
+            pass
+
+        password_blob = string_to_array(ctx.trustdom_pass.encode('utf-16-le'))
+
+        clear_value = drsblobs.AuthInfoClear()
+        clear_value.size = len(password_blob)
+        clear_value.password = password_blob
+
+        clear_authentication_information = drsblobs.AuthenticationInformation()
+        clear_authentication_information.LastUpdateTime = 0
+        clear_authentication_information.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
+        clear_authentication_information.AuthInfo = clear_value
+
+        version_value = drsblobs.AuthInfoVersion()
+        version_value.version = 1
+
+        version = drsblobs.AuthenticationInformation()
+        version.LastUpdateTime = 0
+        version.AuthType = lsa.TRUST_AUTH_TYPE_VERSION
+        version.AuthInfo = version_value
+
+        authentication_information_array = drsblobs.AuthenticationInformationArray()
+        authentication_information_array.count = 2
+        authentication_information_array.array = [clear_authentication_information, version]
+
+        outgoing = drsblobs.trustAuthInOutBlob()
+        outgoing.count = 1
+        outgoing.current = authentication_information_array
+
+        trustpass = drsblobs.trustDomainPasswords()
+        confounder = [3] * 512
+
+        for i in range(512):
+            confounder[i] = random.randint(0, 255)
+
+        trustpass.confounder = confounder
+
+        trustpass.outgoing = outgoing
+        trustpass.incoming = outgoing
+
+        trustpass_blob = ndr_pack(trustpass)
+
+        encrypted_trustpass = arcfour_encrypt(lsaconn.session_key, trustpass_blob)
+
+        auth_blob = lsa.DATA_BUF2()
+        auth_blob.size = len(encrypted_trustpass)
+        auth_blob.data = string_to_array(encrypted_trustpass)
+
+        auth_info = lsa.TrustDomainInfoAuthInfoInternal()
+        auth_info.auth_blob = auth_blob
+
+        trustdom_handle = lsaconn.CreateTrustedDomainEx2(pol_handle,
+                                                         info,
+                                                         auth_info,
+                                                         security.SEC_STD_DELETE)
+
+        rec = {
+            "dn" : "cn=%s,cn=system,%s" % (ctx.parent_dnsdomain, ctx.base_dn),
+            "objectclass" : "trustedDomain",
+            "trustType" : str(info.trust_type),
+            "trustAttributes" : str(info.trust_attributes),
+            "trustDirection" : str(info.trust_direction),
+            "flatname" : ctx.parent_domain_name,
+            "trustPartner" : ctx.parent_dnsdomain,
+            "trustAuthIncoming" : ndr_pack(outgoing),
+            "trustAuthOutgoing" : ndr_pack(outgoing)
+            }
+        ctx.local_samdb.add(rec)
+
+        rec = {
+            "dn" : "cn=%s$,cn=users,%s" % (ctx.parent_domain_name, ctx.base_dn),
+            "objectclass" : "user",
+            "userAccountControl" : str(samba.dsdb.UF_INTERDOMAIN_TRUST_ACCOUNT),
+            "clearTextPassword" : ctx.trustdom_pass.encode('utf-16-le')
+            }
+        ctx.local_samdb.add(rec)
+
+
     def do_join(ctx):
         ctx.cleanup_old_join()
         try:
             ctx.join_add_objects()
             ctx.join_provision()
             ctx.join_replicate()
+            ctx.join_add_objects2()
             if ctx.subdomain:
                 ctx.join_provision_own_domain()
-            else:
-                ctx.join_finalise()
+                ctx.join_setup_trusts()
+                ctx.join_modify_ntdsdsa()
+            ctx.join_finalise()
         except Exception:
             print "Join failed - cleaning up"
-            ctx.cleanup_old_join()
+            #ctx.cleanup_old_join()
             raise
 
 
@@ -655,14 +868,19 @@ def join_subdomain(server=None, creds=None, lp=None, site=None, netbios_name=Non
     """join as a DC"""
     ctx = dc_join(server, creds, lp, site, netbios_name, targetdir, parent_domain)
     ctx.subdomain = True
+    ctx.parent_domain_name = ctx.domain_name
     ctx.domain_name = netbios_domain
     ctx.realm = dnsdomain
+    ctx.parent_dnsdomain = ctx.dnsdomain
+    ctx.parent_partition_dn = ctx.get_parent_partition_dn()
     ctx.dnsdomain = dnsdomain
     ctx.partition_dn = "CN=%s,CN=Partitions,%s" % (ctx.domain_name, ctx.config_dn)
     ctx.base_dn = samba.dn_from_dns_name(dnsdomain)
     ctx.domsid = str(security.random_sid())
+    ctx.domguid = str(uuid.uuid4())
     ctx.acct_dn = None
     ctx.dnshostname = "%s.%s" % (ctx.myname, ctx.dnsdomain)
+    ctx.trustdom_pass = samba.generate_random_password(32, 40)
 
     ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION