provision: Changes to support encrypted_secrets module
[vlendec/samba-autobuild/.git] / python / samba / join.py
1 # python join code
2 # Copyright Andrew Tridgell 2010
3 # Copyright Andrew Bartlett 2010
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18
19 """Joining a domain."""
20
21 from samba.auth import system_session
22 from samba.samdb import SamDB
23 from samba import gensec, Ldb, drs_utils, arcfour_encrypt, string_to_byte_array
24 import ldb, samba, sys, uuid
25 from samba.ndr import ndr_pack, ndr_unpack
26 from samba.dcerpc import security, drsuapi, misc, nbt, lsa, drsblobs, dnsserver, dnsp
27 from samba.dsdb import DS_DOMAIN_FUNCTION_2003
28 from samba.credentials import Credentials, DONT_USE_KERBEROS
29 from samba.provision import secretsdb_self_join, provision, provision_fill, FILL_DRS, FILL_SUBDOMAIN
30 from samba.provision.common import setup_path
31 from samba.schema import Schema
32 from samba import descriptor
33 from samba.net import Net
34 from samba.provision.sambadns import setup_bind9_dns
35 from samba import read_and_sub_file
36 from samba import werror
37 from base64 import b64encode
38 from samba import WERRORError
39 from samba.dnsserver import ARecord, AAAARecord, PTRRecord, CNameRecord, NSRecord, MXRecord, SOARecord, SRVRecord, TXTRecord
40 from samba import sd_utils
41 import logging
42 import talloc
43 import random
44 import time
45
46 class DCJoinException(Exception):
47
48     def __init__(self, msg):
49         super(DCJoinException, self).__init__("Can't join, error: %s" % msg)
50
51
52 class dc_join(object):
53     """Perform a DC join."""
54
55     def __init__(ctx, logger=None, server=None, creds=None, lp=None, site=None,
56                  netbios_name=None, targetdir=None, domain=None,
57                  machinepass=None, use_ntvfs=False, dns_backend=None,
58                  promote_existing=False, clone_only=False,
59                  plaintext_secrets=False):
60         if site is None:
61             site = "Default-First-Site-Name"
62
63         ctx.clone_only=clone_only
64
65         ctx.logger = logger
66         ctx.creds = creds
67         ctx.lp = lp
68         ctx.site = site
69         ctx.targetdir = targetdir
70         ctx.use_ntvfs = use_ntvfs
71         ctx.plaintext_secrets = plaintext_secrets
72
73         ctx.promote_existing = promote_existing
74         ctx.promote_from_dn = None
75
76         ctx.nc_list = []
77         ctx.full_nc_list = []
78
79         ctx.creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
80         ctx.net = Net(creds=ctx.creds, lp=ctx.lp)
81
82         if server is not None:
83             ctx.server = server
84         else:
85             ctx.logger.info("Finding a writeable DC for domain '%s'" % domain)
86             ctx.server = ctx.find_dc(domain)
87             ctx.logger.info("Found DC %s" % ctx.server)
88
89         ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
90                           session_info=system_session(),
91                           credentials=ctx.creds, lp=ctx.lp)
92
93         try:
94             ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL, attrs=["dn"])
95         except ldb.LdbError, (enum, estr):
96             raise DCJoinException(estr)
97
98
99         ctx.base_dn = str(ctx.samdb.get_default_basedn())
100         ctx.root_dn = str(ctx.samdb.get_root_basedn())
101         ctx.schema_dn = str(ctx.samdb.get_schema_basedn())
102         ctx.config_dn = str(ctx.samdb.get_config_basedn())
103         ctx.domsid = security.dom_sid(ctx.samdb.get_domain_sid())
104         ctx.forestsid = ctx.domsid
105         ctx.domain_name = ctx.get_domain_name()
106         ctx.forest_domain_name = ctx.get_forest_domain_name()
107         ctx.invocation_id = misc.GUID(str(uuid.uuid4()))
108
109         ctx.dc_ntds_dn = ctx.samdb.get_dsServiceName()
110         ctx.dc_dnsHostName = ctx.get_dnsHostName()
111         ctx.behavior_version = ctx.get_behavior_version()
112
113         if machinepass is not None:
114             ctx.acct_pass = machinepass
115         else:
116             ctx.acct_pass = samba.generate_random_machine_password(128, 255)
117
118         ctx.dnsdomain = ctx.samdb.domain_dns_name()
119         if clone_only:
120             # As we don't want to create or delete these DNs, we set them to None
121             ctx.server_dn = None
122             ctx.ntds_dn = None
123             ctx.acct_dn = None
124             ctx.myname = ctx.server.split('.')[0]
125             ctx.ntds_guid = None
126             ctx.rid_manager_dn = None
127
128             # Save this early
129             ctx.remote_dc_ntds_guid = ctx.samdb.get_ntds_GUID()
130         else:
131             # work out the DNs of all the objects we will be adding
132             ctx.myname = netbios_name
133             ctx.samname = "%s$" % ctx.myname
134             ctx.server_dn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (ctx.myname, ctx.site, ctx.config_dn)
135             ctx.ntds_dn = "CN=NTDS Settings,%s" % ctx.server_dn
136             ctx.acct_dn = "CN=%s,OU=Domain Controllers,%s" % (ctx.myname, ctx.base_dn)
137             ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain)
138             ctx.dnsforest = ctx.samdb.forest_dns_name()
139
140             topology_base = "CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,CN=System,%s" % ctx.base_dn
141             if ctx.dn_exists(topology_base):
142                 ctx.topology_dn = "CN=%s,%s" % (ctx.myname, topology_base)
143             else:
144                 ctx.topology_dn = None
145
146             ctx.SPNs = [ "HOST/%s" % ctx.myname,
147                          "HOST/%s" % ctx.dnshostname,
148                          "GC/%s/%s" % (ctx.dnshostname, ctx.dnsforest) ]
149
150             res_rid_manager = ctx.samdb.search(scope=ldb.SCOPE_BASE,
151                                                attrs=["rIDManagerReference"],
152                                                base=ctx.base_dn)
153
154             ctx.rid_manager_dn = res_rid_manager[0]["rIDManagerReference"][0]
155
156         ctx.domaindns_zone = 'DC=DomainDnsZones,%s' % ctx.base_dn
157         ctx.forestdns_zone = 'DC=ForestDnsZones,%s' % ctx.root_dn
158
159         expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.domaindns_zone)
160         res_domaindns = ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
161                                          attrs=[],
162                                          base=ctx.samdb.get_partitions_dn(),
163                                          expression=expr)
164         if dns_backend is None:
165             ctx.dns_backend = "NONE"
166         else:
167             if len(res_domaindns) == 0:
168                 ctx.dns_backend = "NONE"
169                 print "NO DNS zone information found in source domain, not replicating DNS"
170             else:
171                 ctx.dns_backend = dns_backend
172
173         ctx.realm = ctx.dnsdomain
174
175         ctx.tmp_samdb = None
176
177         ctx.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC |
178                              drsuapi.DRSUAPI_DRS_PER_SYNC |
179                              drsuapi.DRSUAPI_DRS_GET_ANC |
180                              drsuapi.DRSUAPI_DRS_GET_NC_SIZE |
181                              drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
182
183         # these elements are optional
184         ctx.never_reveal_sid = None
185         ctx.reveal_sid = None
186         ctx.connection_dn = None
187         ctx.RODC = False
188         ctx.krbtgt_dn = None
189         ctx.drsuapi = None
190         ctx.managedby = None
191         ctx.subdomain = False
192         ctx.adminpass = None
193         ctx.partition_dn = None
194
195         ctx.dns_a_dn = None
196         ctx.dns_cname_dn = None
197
198         # Do not normally register 127. addresses but allow override for selftest
199         ctx.force_all_ips = False
200
201     def del_noerror(ctx, dn, recursive=False):
202         if recursive:
203             try:
204                 res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_ONELEVEL, attrs=["dn"])
205             except Exception:
206                 return
207             for r in res:
208                 ctx.del_noerror(r.dn, recursive=True)
209         try:
210             ctx.samdb.delete(dn)
211             print "Deleted %s" % dn
212         except Exception:
213             pass
214
215     def cleanup_old_accounts(ctx, force=False):
216         res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
217                                expression='sAMAccountName=%s' % ldb.binary_encode(ctx.samname),
218                                attrs=["msDS-krbTgtLink", "objectSID"])
219         if len(res) == 0:
220             return
221
222         if not force:
223             creds = Credentials()
224             creds.guess(ctx.lp)
225             try:
226                 creds.set_machine_account(ctx.lp)
227                 creds.set_kerberos_state(ctx.creds.get_kerberos_state())
228                 machine_samdb = SamDB(url="ldap://%s" % ctx.server,
229                                       session_info=system_session(),
230                                     credentials=creds, lp=ctx.lp)
231             except:
232                 pass
233             else:
234                 token_res = machine_samdb.search(scope=ldb.SCOPE_BASE, base="", attrs=["tokenGroups"])
235                 if token_res[0]["tokenGroups"][0] \
236                    == res[0]["objectSID"][0]:
237                     raise DCJoinException("Not removing account %s which "
238                                        "looks like a Samba DC account "
239                                        "maching the password we already have.  "
240                                        "To override, remove secrets.ldb and secrets.tdb"
241                                     % ctx.samname)
242
243         ctx.del_noerror(res[0].dn, recursive=True)
244
245         if "msDS-Krbtgtlink" in res[0]:
246             new_krbtgt_dn = res[0]["msDS-Krbtgtlink"][0]
247             ctx.del_noerror(ctx.new_krbtgt_dn)
248
249         res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
250                                expression='(&(sAMAccountName=%s)(servicePrincipalName=%s))' %
251                                (ldb.binary_encode("dns-%s" % ctx.myname),
252                                 ldb.binary_encode("dns/%s" % ctx.dnshostname)),
253                                attrs=[])
254         if res:
255             ctx.del_noerror(res[0].dn, recursive=True)
256
257         res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
258                                expression='(sAMAccountName=%s)' % ldb.binary_encode("dns-%s" % ctx.myname),
259                             attrs=[])
260         if res:
261             raise DCJoinException("Not removing account %s which looks like "
262                                "a Samba DNS service account but does not "
263                                "have servicePrincipalName=%s" %
264                                (ldb.binary_encode("dns-%s" % ctx.myname),
265                                 ldb.binary_encode("dns/%s" % ctx.dnshostname)))
266
267
268     def cleanup_old_join(ctx, force=False):
269         """Remove any DNs from a previous join."""
270         # find the krbtgt link
271         if not ctx.subdomain:
272             ctx.cleanup_old_accounts(force=force)
273
274         if ctx.connection_dn is not None:
275             ctx.del_noerror(ctx.connection_dn)
276         if ctx.krbtgt_dn is not None:
277             ctx.del_noerror(ctx.krbtgt_dn)
278         ctx.del_noerror(ctx.ntds_dn)
279         ctx.del_noerror(ctx.server_dn, recursive=True)
280         if ctx.topology_dn:
281             ctx.del_noerror(ctx.topology_dn)
282         if ctx.partition_dn:
283             ctx.del_noerror(ctx.partition_dn)
284
285         if ctx.subdomain:
286             binding_options = "sign"
287             lsaconn = lsa.lsarpc("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
288                                  ctx.lp, ctx.creds)
289
290             objectAttr = lsa.ObjectAttribute()
291             objectAttr.sec_qos = lsa.QosInfo()
292
293             pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
294                                              objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
295
296             name = lsa.String()
297             name.string = ctx.realm
298             info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
299
300             lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
301
302             name = lsa.String()
303             name.string = ctx.forest_domain_name
304             info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
305
306             lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
307
308         if ctx.dns_a_dn:
309             ctx.del_noerror(ctx.dns_a_dn)
310
311         if ctx.dns_cname_dn:
312             ctx.del_noerror(ctx.dns_cname_dn)
313
314
315
316     def promote_possible(ctx):
317         """confirm that the account is just a bare NT4 BDC or a member server, so can be safely promoted"""
318         if ctx.subdomain:
319             # This shouldn't happen
320             raise Exception("Can not promote into a subdomain")
321
322         res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
323                                expression='sAMAccountName=%s' % ldb.binary_encode(ctx.samname),
324                                attrs=["msDS-krbTgtLink", "userAccountControl", "serverReferenceBL", "rIDSetReferences"])
325         if len(res) == 0:
326             raise Exception("Could not find domain member account '%s' to promote to a DC, use 'samba-tool domain join' instead'" % ctx.samname)
327         if "msDS-krbTgtLink" in res[0] or "serverReferenceBL" in res[0] or "rIDSetReferences" in res[0]:
328             raise Exception("Account '%s' appears to be an active DC, use 'samba-tool domain join' if you must re-create this account" % ctx.samname)
329         if (int(res[0]["userAccountControl"][0]) & (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT|samba.dsdb.UF_SERVER_TRUST_ACCOUNT) == 0):
330             raise Exception("Account %s is not a domain member or a bare NT4 BDC, use 'samba-tool domain join' instead'" % ctx.samname)
331
332         ctx.promote_from_dn = res[0].dn
333
334
335     def find_dc(ctx, domain):
336         """find a writeable DC for the given domain"""
337         try:
338             ctx.cldap_ret = ctx.net.finddc(domain=domain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS | nbt.NBT_SERVER_WRITABLE)
339         except Exception:
340             raise Exception("Failed to find a writeable DC for domain '%s'" % domain)
341         if ctx.cldap_ret.client_site is not None and ctx.cldap_ret.client_site != "":
342             ctx.site = ctx.cldap_ret.client_site
343         return ctx.cldap_ret.pdc_dns_name
344
345
346     def get_behavior_version(ctx):
347         res = ctx.samdb.search(base=ctx.base_dn, scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
348         if "msDS-Behavior-Version" in res[0]:
349             return int(res[0]["msDS-Behavior-Version"][0])
350         else:
351             return samba.dsdb.DS_DOMAIN_FUNCTION_2000
352
353     def get_dnsHostName(ctx):
354         res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dnsHostName"])
355         return res[0]["dnsHostName"][0]
356
357     def get_domain_name(ctx):
358         '''get netbios name of the domain from the partitions record'''
359         partitions_dn = ctx.samdb.get_partitions_dn()
360         res = ctx.samdb.search(base=partitions_dn, scope=ldb.SCOPE_ONELEVEL, attrs=["nETBIOSName"],
361                                expression='ncName=%s' % ldb.binary_encode(str(ctx.samdb.get_default_basedn())))
362         return res[0]["nETBIOSName"][0]
363
364     def get_forest_domain_name(ctx):
365         '''get netbios name of the domain from the partitions record'''
366         partitions_dn = ctx.samdb.get_partitions_dn()
367         res = ctx.samdb.search(base=partitions_dn, scope=ldb.SCOPE_ONELEVEL, attrs=["nETBIOSName"],
368                                expression='ncName=%s' % ldb.binary_encode(str(ctx.samdb.get_root_basedn())))
369         return res[0]["nETBIOSName"][0]
370
371     def get_parent_partition_dn(ctx):
372         '''get the parent domain partition DN from parent DNS name'''
373         res = ctx.samdb.search(base=ctx.config_dn, attrs=[],
374                                expression='(&(objectclass=crossRef)(dnsRoot=%s)(systemFlags:%s:=%u))' %
375                                (ldb.binary_encode(ctx.parent_dnsdomain),
376                                 ldb.OID_COMPARATOR_AND, samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN))
377         return str(res[0].dn)
378
379     def get_naming_master(ctx):
380         '''get the parent domain partition DN from parent DNS name'''
381         res = ctx.samdb.search(base='CN=Partitions,%s' % ctx.config_dn, attrs=['fSMORoleOwner'],
382                                scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"])
383         if not 'fSMORoleOwner' in res[0]:
384             raise DCJoinException("Can't find naming master on partition DN %s in %s" % (ctx.partition_dn, ctx.samdb.url))
385         try:
386             master_guid = str(misc.GUID(ldb.Dn(ctx.samdb, res[0]['fSMORoleOwner'][0]).get_extended_component('GUID')))
387         except KeyError:
388             raise DCJoinException("Can't find GUID in naming master on partition DN %s" % res[0]['fSMORoleOwner'][0])
389
390         master_host = '%s._msdcs.%s' % (master_guid, ctx.dnsforest)
391         return master_host
392
393     def get_mysid(ctx):
394         '''get the SID of the connected user. Only works with w2k8 and later,
395            so only used for RODC join'''
396         res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["tokenGroups"])
397         binsid = res[0]["tokenGroups"][0]
398         return ctx.samdb.schema_format_value("objectSID", binsid)
399
400     def dn_exists(ctx, dn):
401         '''check if a DN exists'''
402         try:
403             res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[])
404         except ldb.LdbError, (enum, estr):
405             if enum == ldb.ERR_NO_SUCH_OBJECT:
406                 return False
407             raise
408         return True
409
410     def add_krbtgt_account(ctx):
411         '''RODCs need a special krbtgt account'''
412         print "Adding %s" % ctx.krbtgt_dn
413         rec = {
414             "dn" : ctx.krbtgt_dn,
415             "objectclass" : "user",
416             "useraccountcontrol" : str(samba.dsdb.UF_NORMAL_ACCOUNT |
417                                        samba.dsdb.UF_ACCOUNTDISABLE),
418             "showinadvancedviewonly" : "TRUE",
419             "description" : "krbtgt for %s" % ctx.samname}
420         ctx.samdb.add(rec, ["rodc_join:1:1"])
421
422         # now we need to search for the samAccountName attribute on the krbtgt DN,
423         # as this will have been magically set to the krbtgt number
424         res = ctx.samdb.search(base=ctx.krbtgt_dn, scope=ldb.SCOPE_BASE, attrs=["samAccountName"])
425         ctx.krbtgt_name = res[0]["samAccountName"][0]
426
427         print "Got krbtgt_name=%s" % ctx.krbtgt_name
428
429         m = ldb.Message()
430         m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
431         m["msDS-krbTgtLink"] = ldb.MessageElement(ctx.krbtgt_dn,
432                                                   ldb.FLAG_MOD_REPLACE, "msDS-krbTgtLink")
433         ctx.samdb.modify(m)
434
435         ctx.new_krbtgt_dn = "CN=%s,CN=Users,%s" % (ctx.krbtgt_name, ctx.base_dn)
436         print "Renaming %s to %s" % (ctx.krbtgt_dn, ctx.new_krbtgt_dn)
437         ctx.samdb.rename(ctx.krbtgt_dn, ctx.new_krbtgt_dn)
438
439     def drsuapi_connect(ctx):
440         '''make a DRSUAPI connection to the naming master'''
441         binding_options = "seal"
442         if ctx.lp.log_level() >= 9:
443             binding_options += ",print"
444         binding_string = "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options)
445         ctx.drsuapi = drsuapi.drsuapi(binding_string, ctx.lp, ctx.creds)
446         (ctx.drsuapi_handle, ctx.bind_supported_extensions) = drs_utils.drs_DsBind(ctx.drsuapi)
447
448     def create_tmp_samdb(ctx):
449         '''create a temporary samdb object for schema queries'''
450         ctx.tmp_schema = Schema(ctx.domsid,
451                                 schemadn=ctx.schema_dn)
452         ctx.tmp_samdb = SamDB(session_info=system_session(), url=None, auto_connect=False,
453                               credentials=ctx.creds, lp=ctx.lp, global_schema=False,
454                               am_rodc=False)
455         ctx.tmp_samdb.set_schema(ctx.tmp_schema)
456
457     def build_DsReplicaAttribute(ctx, attrname, attrvalue):
458         '''build a DsReplicaAttributeCtr object'''
459         r = drsuapi.DsReplicaAttribute()
460         r.attid = ctx.tmp_samdb.get_attid_from_lDAPDisplayName(attrname)
461         r.value_ctr = 1
462
463
464     def DsAddEntry(ctx, recs):
465         '''add a record via the DRSUAPI DsAddEntry call'''
466         if ctx.drsuapi is None:
467             ctx.drsuapi_connect()
468         if ctx.tmp_samdb is None:
469             ctx.create_tmp_samdb()
470
471         objects = []
472         for rec in recs:
473             id = drsuapi.DsReplicaObjectIdentifier()
474             id.dn = rec['dn']
475
476             attrs = []
477             for a in rec:
478                 if a == 'dn':
479                     continue
480                 if not isinstance(rec[a], list):
481                     v = [rec[a]]
482                 else:
483                     v = rec[a]
484                 rattr = ctx.tmp_samdb.dsdb_DsReplicaAttribute(ctx.tmp_samdb, a, v)
485                 attrs.append(rattr)
486
487             attribute_ctr = drsuapi.DsReplicaAttributeCtr()
488             attribute_ctr.num_attributes = len(attrs)
489             attribute_ctr.attributes = attrs
490
491             object = drsuapi.DsReplicaObject()
492             object.identifier = id
493             object.attribute_ctr = attribute_ctr
494
495             list_object = drsuapi.DsReplicaObjectListItem()
496             list_object.object = object
497             objects.append(list_object)
498
499         req2 = drsuapi.DsAddEntryRequest2()
500         req2.first_object = objects[0]
501         prev = req2.first_object
502         for o in objects[1:]:
503             prev.next_object = o
504             prev = o
505
506         (level, ctr) = ctx.drsuapi.DsAddEntry(ctx.drsuapi_handle, 2, req2)
507         if level == 2:
508             if ctr.dir_err != drsuapi.DRSUAPI_DIRERR_OK:
509                 print("DsAddEntry failed with dir_err %u" % ctr.dir_err)
510                 raise RuntimeError("DsAddEntry failed")
511             if ctr.extended_err[0] != werror.WERR_SUCCESS:
512                 print("DsAddEntry failed with status %s info %s" % (ctr.extended_err))
513                 raise RuntimeError("DsAddEntry failed")
514         if level == 3:
515             if ctr.err_ver != 1:
516                 raise RuntimeError("expected err_ver 1, got %u" % ctr.err_ver)
517             if ctr.err_data.status[0] != werror.WERR_SUCCESS:
518                 if ctr.err_data.info is None:
519                     print("DsAddEntry failed with status %s, info omitted" % (ctr.err_data.status[1]))
520                 else:
521                     print("DsAddEntry failed with status %s info %s" % (ctr.err_data.status[1],
522                                                                         ctr.err_data.info.extended_err))
523                 raise RuntimeError("DsAddEntry failed")
524             if ctr.err_data.dir_err != drsuapi.DRSUAPI_DIRERR_OK:
525                 print("DsAddEntry failed with dir_err %u" % ctr.err_data.dir_err)
526                 raise RuntimeError("DsAddEntry failed")
527
528         return ctr.objects
529
530     def join_ntdsdsa_obj(ctx):
531         '''return the ntdsdsa object to add'''
532
533         print "Adding %s" % ctx.ntds_dn
534         rec = {
535             "dn" : ctx.ntds_dn,
536             "objectclass" : "nTDSDSA",
537             "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
538             "dMDLocation" : ctx.schema_dn}
539
540         nc_list = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
541
542         if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
543             rec["msDS-Behavior-Version"] = str(samba.dsdb.DS_DOMAIN_FUNCTION_2008_R2)
544
545         if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
546             rec["msDS-HasDomainNCs"] = ctx.base_dn
547
548         if ctx.RODC:
549             rec["objectCategory"] = "CN=NTDS-DSA-RO,%s" % ctx.schema_dn
550             rec["msDS-HasFullReplicaNCs"] = ctx.full_nc_list
551             rec["options"] = "37"
552         else:
553             rec["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn
554             rec["HasMasterNCs"]      = []
555             for nc in nc_list:
556                 if nc in ctx.full_nc_list:
557                     rec["HasMasterNCs"].append(nc)
558             if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
559                 rec["msDS-HasMasterNCs"] = ctx.full_nc_list
560             rec["options"] = "1"
561             rec["invocationId"] = ndr_pack(ctx.invocation_id)
562
563         return rec
564
565     def join_add_ntdsdsa(ctx):
566         '''add the ntdsdsa object'''
567
568         rec = ctx.join_ntdsdsa_obj()
569         if ctx.RODC:
570             ctx.samdb.add(rec, ["rodc_join:1:1"])
571         else:
572             ctx.DsAddEntry([rec])
573
574         # find the GUID of our NTDS DN
575         res = ctx.samdb.search(base=ctx.ntds_dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"])
576         ctx.ntds_guid = misc.GUID(ctx.samdb.schema_format_value("objectGUID", res[0]["objectGUID"][0]))
577
578     def join_add_objects(ctx):
579         '''add the various objects needed for the join'''
580         if ctx.acct_dn:
581             print "Adding %s" % ctx.acct_dn
582             rec = {
583                 "dn" : ctx.acct_dn,
584                 "objectClass": "computer",
585                 "displayname": ctx.samname,
586                 "samaccountname" : ctx.samname,
587                 "userAccountControl" : str(ctx.userAccountControl | samba.dsdb.UF_ACCOUNTDISABLE),
588                 "dnshostname" : ctx.dnshostname}
589             if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2008:
590                 rec['msDS-SupportedEncryptionTypes'] = str(samba.dsdb.ENC_ALL_TYPES)
591             elif ctx.promote_existing:
592                 rec['msDS-SupportedEncryptionTypes'] = []
593             if ctx.managedby:
594                 rec["managedby"] = ctx.managedby
595             elif ctx.promote_existing:
596                 rec["managedby"] = []
597
598             if ctx.never_reveal_sid:
599                 rec["msDS-NeverRevealGroup"] = ctx.never_reveal_sid
600             elif ctx.promote_existing:
601                 rec["msDS-NeverRevealGroup"] = []
602
603             if ctx.reveal_sid:
604                 rec["msDS-RevealOnDemandGroup"] = ctx.reveal_sid
605             elif ctx.promote_existing:
606                 rec["msDS-RevealOnDemandGroup"] = []
607
608             if ctx.promote_existing:
609                 if ctx.promote_from_dn != ctx.acct_dn:
610                     ctx.samdb.rename(ctx.promote_from_dn, ctx.acct_dn)
611                 ctx.samdb.modify(ldb.Message.from_dict(ctx.samdb, rec, ldb.FLAG_MOD_REPLACE))
612             else:
613                 ctx.samdb.add(rec)
614
615         if ctx.krbtgt_dn:
616             ctx.add_krbtgt_account()
617
618         if ctx.server_dn:
619             print "Adding %s" % ctx.server_dn
620             rec = {
621                 "dn": ctx.server_dn,
622                 "objectclass" : "server",
623                 # windows uses 50000000 decimal for systemFlags. A windows hex/decimal mixup bug?
624                 "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_RENAME |
625                                     samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE |
626                                     samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
627                 # windows seems to add the dnsHostName later
628                 "dnsHostName" : ctx.dnshostname}
629
630             if ctx.acct_dn:
631                 rec["serverReference"] = ctx.acct_dn
632
633             ctx.samdb.add(rec)
634
635         if ctx.subdomain:
636             # the rest is done after replication
637             ctx.ntds_guid = None
638             return
639
640         if ctx.ntds_dn:
641             ctx.join_add_ntdsdsa()
642
643             # Add the Replica-Locations or RO-Replica-Locations attributes
644             # TODO Is this supposed to be for the schema partition too?
645             expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.domaindns_zone)
646             domain = (ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
647                                       attrs=[],
648                                       base=ctx.samdb.get_partitions_dn(),
649                                       expression=expr), ctx.domaindns_zone)
650
651             expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.forestdns_zone)
652             forest = (ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
653                                       attrs=[],
654                                       base=ctx.samdb.get_partitions_dn(),
655                                       expression=expr), ctx.forestdns_zone)
656
657             for part, zone in (domain, forest):
658                 if zone not in ctx.nc_list:
659                     continue
660
661                 if len(part) == 1:
662                     m = ldb.Message()
663                     m.dn = part[0].dn
664                     attr = "msDS-NC-Replica-Locations"
665                     if ctx.RODC:
666                         attr = "msDS-NC-RO-Replica-Locations"
667
668                     m[attr] = ldb.MessageElement(ctx.ntds_dn,
669                                                  ldb.FLAG_MOD_ADD, attr)
670                     ctx.samdb.modify(m)
671
672         if ctx.connection_dn is not None:
673             print "Adding %s" % ctx.connection_dn
674             rec = {
675                 "dn" : ctx.connection_dn,
676                 "objectclass" : "nTDSConnection",
677                 "enabledconnection" : "TRUE",
678                 "options" : "65",
679                 "fromServer" : ctx.dc_ntds_dn}
680             ctx.samdb.add(rec)
681
682         if ctx.acct_dn:
683             print "Adding SPNs to %s" % ctx.acct_dn
684             m = ldb.Message()
685             m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
686             for i in range(len(ctx.SPNs)):
687                 ctx.SPNs[i] = ctx.SPNs[i].replace("$NTDSGUID", str(ctx.ntds_guid))
688             m["servicePrincipalName"] = ldb.MessageElement(ctx.SPNs,
689                                                            ldb.FLAG_MOD_REPLACE,
690                                                            "servicePrincipalName")
691             ctx.samdb.modify(m)
692
693             # The account password set operation should normally be done over
694             # LDAP. Windows 2000 DCs however allow this only with SSL
695             # connections which are hard to set up and otherwise refuse with
696             # ERR_UNWILLING_TO_PERFORM. In this case we fall back to libnet
697             # over SAMR.
698             print "Setting account password for %s" % ctx.samname
699             try:
700                 ctx.samdb.setpassword("(&(objectClass=user)(sAMAccountName=%s))"
701                                       % ldb.binary_encode(ctx.samname),
702                                       ctx.acct_pass,
703                                       force_change_at_next_login=False,
704                                       username=ctx.samname)
705             except ldb.LdbError, (num, _):
706                 if num != ldb.ERR_UNWILLING_TO_PERFORM:
707                     pass
708                 ctx.net.set_password(account_name=ctx.samname,
709                                      domain_name=ctx.domain_name,
710                                      newpassword=ctx.acct_pass.encode('utf-8'))
711
712             res = ctx.samdb.search(base=ctx.acct_dn, scope=ldb.SCOPE_BASE,
713                                    attrs=["msDS-KeyVersionNumber",
714                                           "objectSID"])
715             if "msDS-KeyVersionNumber" in res[0]:
716                 ctx.key_version_number = int(res[0]["msDS-KeyVersionNumber"][0])
717             else:
718                 ctx.key_version_number = None
719
720             ctx.new_dc_account_sid = ndr_unpack(security.dom_sid,
721                                                 res[0]["objectSid"][0])
722
723             print("Enabling account")
724             m = ldb.Message()
725             m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
726             m["userAccountControl"] = ldb.MessageElement(str(ctx.userAccountControl),
727                                                          ldb.FLAG_MOD_REPLACE,
728                                                          "userAccountControl")
729             ctx.samdb.modify(m)
730
731         if ctx.dns_backend.startswith("BIND9_"):
732             ctx.dnspass = samba.generate_random_password(128, 255)
733
734             recs = ctx.samdb.parse_ldif(read_and_sub_file(setup_path("provision_dns_add_samba.ldif"),
735                                                                 {"DNSDOMAIN": ctx.dnsdomain,
736                                                                  "DOMAINDN": ctx.base_dn,
737                                                                  "HOSTNAME" : ctx.myname,
738                                                                  "DNSPASS_B64": b64encode(ctx.dnspass.encode('utf-16-le')),
739                                                                  "DNSNAME" : ctx.dnshostname}))
740             for changetype, msg in recs:
741                 assert changetype == ldb.CHANGETYPE_NONE
742                 dns_acct_dn = msg["dn"]
743                 print "Adding DNS account %s with dns/ SPN" % msg["dn"]
744
745                 # Remove dns password (we will set it as a modify, as we can't do clearTextPassword over LDAP)
746                 del msg["clearTextPassword"]
747                 # Remove isCriticalSystemObject for similar reasons, it cannot be set over LDAP
748                 del msg["isCriticalSystemObject"]
749                 # Disable account until password is set
750                 msg["userAccountControl"] = str(samba.dsdb.UF_NORMAL_ACCOUNT |
751                                                 samba.dsdb.UF_ACCOUNTDISABLE)
752                 try:
753                     ctx.samdb.add(msg)
754                 except ldb.LdbError, (num, _):
755                     if num != ldb.ERR_ENTRY_ALREADY_EXISTS:
756                         raise
757
758             # The account password set operation should normally be done over
759             # LDAP. Windows 2000 DCs however allow this only with SSL
760             # connections which are hard to set up and otherwise refuse with
761             # ERR_UNWILLING_TO_PERFORM. In this case we fall back to libnet
762             # over SAMR.
763             print "Setting account password for dns-%s" % ctx.myname
764             try:
765                 ctx.samdb.setpassword("(&(objectClass=user)(samAccountName=dns-%s))"
766                                       % ldb.binary_encode(ctx.myname),
767                                       ctx.dnspass,
768                                       force_change_at_next_login=False,
769                                       username=ctx.samname)
770             except ldb.LdbError, (num, _):
771                 if num != ldb.ERR_UNWILLING_TO_PERFORM:
772                     raise
773                 ctx.net.set_password(account_name="dns-%s" % ctx.myname,
774                                      domain_name=ctx.domain_name,
775                                      newpassword=ctx.dnspass)
776
777             res = ctx.samdb.search(base=dns_acct_dn, scope=ldb.SCOPE_BASE,
778                                    attrs=["msDS-KeyVersionNumber"])
779             if "msDS-KeyVersionNumber" in res[0]:
780                 ctx.dns_key_version_number = int(res[0]["msDS-KeyVersionNumber"][0])
781             else:
782                 ctx.dns_key_version_number = None
783
784     def join_add_objects2(ctx):
785         """add the various objects needed for the join, for subdomains post replication"""
786
787         print "Adding %s" % ctx.partition_dn
788         name_map = {'SubdomainAdmins': "%s-%s" % (str(ctx.domsid), security.DOMAIN_RID_ADMINS)}
789         sd_binary = descriptor.get_paritions_crossref_subdomain_descriptor(ctx.forestsid, name_map=name_map)
790         rec = {
791             "dn" : ctx.partition_dn,
792             "objectclass" : "crossRef",
793             "objectCategory" : "CN=Cross-Ref,%s" % ctx.schema_dn,
794             "nCName" : ctx.base_dn,
795             "nETBIOSName" : ctx.domain_name,
796             "dnsRoot": ctx.dnsdomain,
797             "trustParent" : ctx.parent_partition_dn,
798             "systemFlags" : str(samba.dsdb.SYSTEM_FLAG_CR_NTDS_NC|samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN),
799             "ntSecurityDescriptor" : sd_binary,
800         }
801
802         if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
803             rec["msDS-Behavior-Version"] = str(ctx.behavior_version)
804
805         rec2 = ctx.join_ntdsdsa_obj()
806
807         objects = ctx.DsAddEntry([rec, rec2])
808         if len(objects) != 2:
809             raise DCJoinException("Expected 2 objects from DsAddEntry")
810
811         ctx.ntds_guid = objects[1].guid
812
813         print("Replicating partition DN")
814         ctx.repl.replicate(ctx.partition_dn,
815                            misc.GUID("00000000-0000-0000-0000-000000000000"),
816                            ctx.ntds_guid,
817                            exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
818                            replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP)
819
820         print("Replicating NTDS DN")
821         ctx.repl.replicate(ctx.ntds_dn,
822                            misc.GUID("00000000-0000-0000-0000-000000000000"),
823                            ctx.ntds_guid,
824                            exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
825                            replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP)
826
827     def join_provision(ctx):
828         """Provision the local SAM."""
829
830         print "Calling bare provision"
831
832         smbconf = ctx.lp.configfile
833
834         presult = provision(ctx.logger, system_session(), smbconf=smbconf,
835                 targetdir=ctx.targetdir, samdb_fill=FILL_DRS, realm=ctx.realm,
836                 rootdn=ctx.root_dn, domaindn=ctx.base_dn,
837                 schemadn=ctx.schema_dn, configdn=ctx.config_dn,
838                 serverdn=ctx.server_dn, domain=ctx.domain_name,
839                 hostname=ctx.myname, domainsid=ctx.domsid,
840                 machinepass=ctx.acct_pass, serverrole="active directory domain controller",
841                 sitename=ctx.site, lp=ctx.lp, ntdsguid=ctx.ntds_guid,
842                 use_ntvfs=ctx.use_ntvfs, dns_backend=ctx.dns_backend,
843                 plaintext_secrets=ctx.plaintext_secrets)
844         print "Provision OK for domain DN %s" % presult.domaindn
845         ctx.local_samdb = presult.samdb
846         ctx.lp          = presult.lp
847         ctx.paths       = presult.paths
848         ctx.names       = presult.names
849
850         # Fix up the forestsid, it may be different if we are joining as a subdomain
851         ctx.names.forestsid = ctx.forestsid
852
853     def join_provision_own_domain(ctx):
854         """Provision the local SAM."""
855
856         # we now operate exclusively on the local database, which
857         # we need to reopen in order to get the newly created schema
858         print("Reconnecting to local samdb")
859         ctx.samdb = SamDB(url=ctx.local_samdb.url,
860                           session_info=system_session(),
861                           lp=ctx.local_samdb.lp,
862                           global_schema=False)
863         ctx.samdb.set_invocation_id(str(ctx.invocation_id))
864         ctx.local_samdb = ctx.samdb
865
866         ctx.logger.info("Finding domain GUID from ncName")
867         res = ctx.local_samdb.search(base=ctx.partition_dn, scope=ldb.SCOPE_BASE, attrs=['ncName'],
868                                      controls=["extended_dn:1:1", "reveal_internals:0"])
869
870         if 'nCName' not in res[0]:
871             raise DCJoinException("Can't find naming context on partition DN %s in %s" % (ctx.partition_dn, ctx.samdb.url))
872
873         try:
874             ctx.names.domainguid = str(misc.GUID(ldb.Dn(ctx.samdb, res[0]['ncName'][0]).get_extended_component('GUID')))
875         except KeyError:
876             raise DCJoinException("Can't find GUID in naming master on partition DN %s" % res[0]['ncName'][0])
877
878         ctx.logger.info("Got domain GUID %s" % ctx.names.domainguid)
879
880         ctx.logger.info("Calling own domain provision")
881
882         secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
883
884         presult = provision_fill(ctx.local_samdb, secrets_ldb,
885                                  ctx.logger, ctx.names, ctx.paths,
886                                  dom_for_fun_level=DS_DOMAIN_FUNCTION_2003,
887                                  targetdir=ctx.targetdir, samdb_fill=FILL_SUBDOMAIN,
888                                  machinepass=ctx.acct_pass, serverrole="active directory domain controller",
889                                  lp=ctx.lp, hostip=ctx.names.hostip, hostip6=ctx.names.hostip6,
890                                  dns_backend=ctx.dns_backend, adminpass=ctx.adminpass)
891         print("Provision OK for domain %s" % ctx.names.dnsdomain)
892
893     def join_replicate(ctx):
894         """Replicate the SAM."""
895
896         print "Starting replication"
897         ctx.local_samdb.transaction_start()
898         try:
899             source_dsa_invocation_id = misc.GUID(ctx.samdb.get_invocation_id())
900             if ctx.ntds_guid is None:
901                 print("Using DS_BIND_GUID_W2K3")
902                 destination_dsa_guid = misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID_W2K3)
903             else:
904                 destination_dsa_guid = ctx.ntds_guid
905
906             if ctx.RODC:
907                 repl_creds = Credentials()
908                 repl_creds.guess(ctx.lp)
909                 repl_creds.set_kerberos_state(DONT_USE_KERBEROS)
910                 repl_creds.set_username(ctx.samname)
911                 repl_creds.set_password(ctx.acct_pass.encode('utf-8'))
912             else:
913                 repl_creds = ctx.creds
914
915             binding_options = "seal"
916             if ctx.lp.log_level() >= 9:
917                 binding_options += ",print"
918             repl = drs_utils.drs_Replicate(
919                 "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
920                 ctx.lp, repl_creds, ctx.local_samdb, ctx.invocation_id)
921
922             repl.replicate(ctx.schema_dn, source_dsa_invocation_id,
923                     destination_dsa_guid, schema=True, rodc=ctx.RODC,
924                     replica_flags=ctx.replica_flags)
925             repl.replicate(ctx.config_dn, source_dsa_invocation_id,
926                     destination_dsa_guid, rodc=ctx.RODC,
927                     replica_flags=ctx.replica_flags)
928             if not ctx.subdomain:
929                 # Replicate first the critical object for the basedn
930                 if not ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
931                     print "Replicating critical objects from the base DN of the domain"
932                     ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
933                     repl.replicate(ctx.base_dn, source_dsa_invocation_id,
934                                 destination_dsa_guid, rodc=ctx.RODC,
935                                 replica_flags=ctx.domain_replica_flags)
936                     ctx.domain_replica_flags ^= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
937                 repl.replicate(ctx.base_dn, source_dsa_invocation_id,
938                                destination_dsa_guid, rodc=ctx.RODC,
939                                replica_flags=ctx.domain_replica_flags)
940             print "Done with always replicated NC (base, config, schema)"
941
942             # At this point we should already have an entry in the ForestDNS
943             # and DomainDNS NC (those under CN=Partions,DC=...) in order to
944             # indicate that we hold a replica for this NC.
945             for nc in (ctx.domaindns_zone, ctx.forestdns_zone):
946                 if nc in ctx.nc_list:
947                     print "Replicating %s" % (str(nc))
948                     repl.replicate(nc, source_dsa_invocation_id,
949                                     destination_dsa_guid, rodc=ctx.RODC,
950                                     replica_flags=ctx.replica_flags)
951
952             if ctx.RODC:
953                 repl.replicate(ctx.acct_dn, source_dsa_invocation_id,
954                         destination_dsa_guid,
955                         exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
956                 repl.replicate(ctx.new_krbtgt_dn, source_dsa_invocation_id,
957                         destination_dsa_guid,
958                         exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
959             elif ctx.rid_manager_dn != None:
960                 # Try and get a RID Set if we can.  This is only possible against the RID Master.  Warn otherwise.
961                 try:
962                     repl.replicate(ctx.rid_manager_dn, source_dsa_invocation_id,
963                                    destination_dsa_guid,
964                                    exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC)
965                 except samba.DsExtendedError, (enum, estr):
966                     if enum == drsuapi.DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER:
967                         print "WARNING: Unable to replicate own RID Set, as server %s (the server we joined) is not the RID Master." % ctx.server
968                         print "NOTE: This is normal and expected, Samba will be able to create users after it contacts the RID Master at first startup."
969                     else:
970                         raise
971
972             ctx.repl = repl
973             ctx.source_dsa_invocation_id = source_dsa_invocation_id
974             ctx.destination_dsa_guid = destination_dsa_guid
975
976             print "Committing SAM database"
977         except:
978             ctx.local_samdb.transaction_cancel()
979             raise
980         else:
981             ctx.local_samdb.transaction_commit()
982
983     def send_DsReplicaUpdateRefs(ctx, dn):
984         r = drsuapi.DsReplicaUpdateRefsRequest1()
985         r.naming_context = drsuapi.DsReplicaObjectIdentifier()
986         r.naming_context.dn = str(dn)
987         r.naming_context.guid = misc.GUID("00000000-0000-0000-0000-000000000000")
988         r.naming_context.sid = security.dom_sid("S-0-0")
989         r.dest_dsa_guid = ctx.ntds_guid
990         r.dest_dsa_dns_name = "%s._msdcs.%s" % (str(ctx.ntds_guid), ctx.dnsforest)
991         r.options = drsuapi.DRSUAPI_DRS_ADD_REF | drsuapi.DRSUAPI_DRS_DEL_REF
992         if not ctx.RODC:
993             r.options |= drsuapi.DRSUAPI_DRS_WRIT_REP
994
995         if ctx.drsuapi is None:
996             ctx.drsuapi_connect()
997
998         ctx.drsuapi.DsReplicaUpdateRefs(ctx.drsuapi_handle, 1, r)
999
1000     def join_add_dns_records(ctx):
1001         """Remotely Add a DNS record to the target DC.  We assume that if we
1002            replicate DNS that the server holds the DNS roles and can accept
1003            updates.
1004
1005            This avoids issues getting replication going after the DC
1006            first starts as the rest of the domain does not have to
1007            wait for samba_dnsupdate to run successfully.
1008
1009            Specifically, we add the records implied by the DsReplicaUpdateRefs
1010            call above.
1011
1012            We do not just run samba_dnsupdate as we want to strictly
1013            operate against the DC we just joined:
1014             - We do not want to query another DNS server
1015             - We do not want to obtain a Kerberos ticket
1016               (as the KDC we select may not be the DC we just joined,
1017               and so may not be in sync with the password we just set)
1018             - We do not wish to set the _ldap records until we have started
1019             - We do not wish to use NTLM (the --use-samba-tool mode forces
1020               NTLM)
1021
1022         """
1023
1024         client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
1025         record_type = dnsp.DNS_TYPE_A
1026         select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA |\
1027                        dnsserver.DNS_RPC_VIEW_NO_CHILDREN
1028
1029         zone = ctx.dnsdomain
1030         msdcs_zone = "_msdcs.%s" % ctx.dnsforest
1031         name = ctx.myname
1032         msdcs_cname = str(ctx.ntds_guid)
1033         cname_target = "%s.%s" % (name, zone)
1034         IPs = samba.interface_ips(ctx.lp, ctx.force_all_ips)
1035
1036         ctx.logger.info("Adding %d remote DNS records for %s.%s" % \
1037                         (len(IPs), name, zone))
1038
1039         binding_options = "sign"
1040         dns_conn = dnsserver.dnsserver("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
1041                                       ctx.lp, ctx.creds)
1042
1043
1044         name_found = True
1045
1046         sd_helper = samba.sd_utils.SDUtils(ctx.samdb)
1047
1048         change_owner_sd = security.descriptor()
1049         change_owner_sd.owner_sid = ctx.new_dc_account_sid
1050         change_owner_sd.group_sid = security.dom_sid("%s-%d" %
1051                                                      (str(ctx.domsid),
1052                                                       security.DOMAIN_RID_DCS))
1053
1054         # TODO: Remove any old records from the primary DNS name
1055         try:
1056             (buflen, res) \
1057                 = dns_conn.DnssrvEnumRecords2(client_version,
1058                                               0,
1059                                               ctx.server,
1060                                               zone,
1061                                               name,
1062                                               None,
1063                                               dnsp.DNS_TYPE_ALL,
1064                                               select_flags,
1065                                               None,
1066                                               None)
1067         except WERRORError as e:
1068             if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1069                 name_found = False
1070                 pass
1071
1072         if name_found:
1073             for rec in res.rec:
1074                 for record in rec.records:
1075                     if record.wType == dnsp.DNS_TYPE_A or \
1076                        record.wType == dnsp.DNS_TYPE_AAAA:
1077                         # delete record
1078                         del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1079                         del_rec_buf.rec = record
1080                         try:
1081                             dns_conn.DnssrvUpdateRecord2(client_version,
1082                                                          0,
1083                                                          ctx.server,
1084                                                          zone,
1085                                                          name,
1086                                                          None,
1087                                                          del_rec_buf)
1088                         except WERRORError as e:
1089                             if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1090                                 pass
1091                             else:
1092                                 raise
1093
1094         for IP in IPs:
1095             if IP.find(':') != -1:
1096                 ctx.logger.info("Adding DNS AAAA record %s.%s for IPv6 IP: %s"
1097                                 % (name, zone, IP))
1098                 rec = AAAARecord(IP)
1099             else:
1100                 ctx.logger.info("Adding DNS A record %s.%s for IPv4 IP: %s"
1101                                 % (name, zone, IP))
1102                 rec = ARecord(IP)
1103
1104             # Add record
1105             add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1106             add_rec_buf.rec = rec
1107             dns_conn.DnssrvUpdateRecord2(client_version,
1108                                          0,
1109                                          ctx.server,
1110                                          zone,
1111                                          name,
1112                                          add_rec_buf,
1113                                          None)
1114
1115         if (len(IPs) > 0):
1116             domaindns_zone_dn = ldb.Dn(ctx.samdb, ctx.domaindns_zone)
1117             (ctx.dns_a_dn, ldap_record) \
1118                 = ctx.samdb.dns_lookup("%s.%s" % (name, zone),
1119                                        dns_partition=domaindns_zone_dn)
1120
1121             # Make the DC own the DNS record, not the administrator
1122             sd_helper.modify_sd_on_dn(ctx.dns_a_dn, change_owner_sd,
1123                                       controls=["sd_flags:1:%d"
1124                                                 % (security.SECINFO_OWNER
1125                                                    | security.SECINFO_GROUP)])
1126
1127
1128             # Add record
1129             ctx.logger.info("Adding DNS CNAME record %s.%s for %s"
1130                             % (msdcs_cname, msdcs_zone, cname_target))
1131
1132             add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1133             rec = CNameRecord(cname_target)
1134             add_rec_buf.rec = rec
1135             dns_conn.DnssrvUpdateRecord2(client_version,
1136                                          0,
1137                                          ctx.server,
1138                                          msdcs_zone,
1139                                          msdcs_cname,
1140                                          add_rec_buf,
1141                                          None)
1142
1143             forestdns_zone_dn = ldb.Dn(ctx.samdb, ctx.forestdns_zone)
1144             (ctx.dns_cname_dn, ldap_record) \
1145                 = ctx.samdb.dns_lookup("%s.%s" % (msdcs_cname, msdcs_zone),
1146                                        dns_partition=forestdns_zone_dn)
1147
1148             # Make the DC own the DNS record, not the administrator
1149             sd_helper.modify_sd_on_dn(ctx.dns_cname_dn, change_owner_sd,
1150                                       controls=["sd_flags:1:%d"
1151                                                 % (security.SECINFO_OWNER
1152                                                    | security.SECINFO_GROUP)])
1153
1154         ctx.logger.info("All other DNS records (like _ldap SRV records) " +
1155                         "will be created samba_dnsupdate on first startup")
1156
1157
1158     def join_replicate_new_dns_records(ctx):
1159         for nc in (ctx.domaindns_zone, ctx.forestdns_zone):
1160             if nc in ctx.nc_list:
1161                 ctx.logger.info("Replicating new DNS records in %s" % (str(nc)))
1162                 ctx.repl.replicate(nc, ctx.source_dsa_invocation_id,
1163                                    ctx.ntds_guid, rodc=ctx.RODC,
1164                                    replica_flags=ctx.replica_flags,
1165                                    full_sync=False)
1166
1167
1168
1169     def join_finalise(ctx):
1170         """Finalise the join, mark us synchronised and setup secrets db."""
1171
1172         # FIXME we shouldn't do this in all cases
1173
1174         # If for some reasons we joined in another site than the one of
1175         # DC we just replicated from then we don't need to send the updatereplicateref
1176         # as replication between sites is time based and on the initiative of the
1177         # requesting DC
1178         if not ctx.clone_only:
1179             ctx.logger.info("Sending DsReplicaUpdateRefs for all the replicated partitions")
1180             for nc in ctx.nc_list:
1181                 ctx.send_DsReplicaUpdateRefs(nc)
1182
1183         if not ctx.clone_only and ctx.RODC:
1184             print "Setting RODC invocationId"
1185             ctx.local_samdb.set_invocation_id(str(ctx.invocation_id))
1186             ctx.local_samdb.set_opaque_integer("domainFunctionality",
1187                                                ctx.behavior_version)
1188             m = ldb.Message()
1189             m.dn = ldb.Dn(ctx.local_samdb, "%s" % ctx.ntds_dn)
1190             m["invocationId"] = ldb.MessageElement(ndr_pack(ctx.invocation_id),
1191                                                    ldb.FLAG_MOD_REPLACE,
1192                                                    "invocationId")
1193             ctx.local_samdb.modify(m)
1194
1195             # Note: as RODC the invocationId is only stored
1196             # on the RODC itself, the other DCs never see it.
1197             #
1198             # Thats is why we fix up the replPropertyMetaData stamp
1199             # for the 'invocationId' attribute, we need to change
1200             # the 'version' to '0', this is what windows 2008r2 does as RODC
1201             #
1202             # This means if the object on a RWDC ever gets a invocationId
1203             # attribute, it will have version '1' (or higher), which will
1204             # will overwrite the RODC local value.
1205             ctx.local_samdb.set_attribute_replmetadata_version(m.dn,
1206                                                                "invocationId",
1207                                                                0)
1208
1209         ctx.logger.info("Setting isSynchronized and dsServiceName")
1210         m = ldb.Message()
1211         m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
1212         m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isSynchronized")
1213
1214         # We want to appear to be the server we just cloned
1215         if ctx.clone_only:
1216             guid = ctx.remote_dc_ntds_guid
1217         else:
1218             guid = ctx.ntds_guid
1219
1220         m["dsServiceName"] = ldb.MessageElement("<GUID=%s>" % str(guid),
1221                                                 ldb.FLAG_MOD_REPLACE, "dsServiceName")
1222         ctx.local_samdb.modify(m)
1223
1224         if ctx.clone_only or ctx.subdomain:
1225             return
1226
1227         secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
1228
1229         ctx.logger.info("Setting up secrets database")
1230         secretsdb_self_join(secrets_ldb, domain=ctx.domain_name,
1231                             realm=ctx.realm,
1232                             dnsdomain=ctx.dnsdomain,
1233                             netbiosname=ctx.myname,
1234                             domainsid=ctx.domsid,
1235                             machinepass=ctx.acct_pass,
1236                             secure_channel_type=ctx.secure_channel_type,
1237                             key_version_number=ctx.key_version_number)
1238
1239         if ctx.dns_backend.startswith("BIND9_"):
1240             setup_bind9_dns(ctx.local_samdb, secrets_ldb,
1241                             ctx.names, ctx.paths, ctx.lp, ctx.logger,
1242                             dns_backend=ctx.dns_backend,
1243                             dnspass=ctx.dnspass, os_level=ctx.behavior_version,
1244                             targetdir=ctx.targetdir,
1245                             key_version_number=ctx.dns_key_version_number)
1246
1247     def join_setup_trusts(ctx):
1248         """provision the local SAM."""
1249
1250         print "Setup domain trusts with server %s" % ctx.server
1251         binding_options = ""  # why doesn't signing work here? w2k8r2 claims no session key
1252         lsaconn = lsa.lsarpc("ncacn_np:%s[%s]" % (ctx.server, binding_options),
1253                              ctx.lp, ctx.creds)
1254
1255         objectAttr = lsa.ObjectAttribute()
1256         objectAttr.sec_qos = lsa.QosInfo()
1257
1258         pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
1259                                          objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
1260
1261         info = lsa.TrustDomainInfoInfoEx()
1262         info.domain_name.string = ctx.dnsdomain
1263         info.netbios_name.string = ctx.domain_name
1264         info.sid = ctx.domsid
1265         info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND
1266         info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
1267         info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST
1268
1269         try:
1270             oldname = lsa.String()
1271             oldname.string = ctx.dnsdomain
1272             oldinfo = lsaconn.QueryTrustedDomainInfoByName(pol_handle, oldname,
1273                                                            lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
1274             print("Removing old trust record for %s (SID %s)" % (ctx.dnsdomain, oldinfo.info_ex.sid))
1275             lsaconn.DeleteTrustedDomain(pol_handle, oldinfo.info_ex.sid)
1276         except RuntimeError:
1277             pass
1278
1279         password_blob = string_to_byte_array(ctx.trustdom_pass.encode('utf-16-le'))
1280
1281         clear_value = drsblobs.AuthInfoClear()
1282         clear_value.size = len(password_blob)
1283         clear_value.password = password_blob
1284
1285         clear_authentication_information = drsblobs.AuthenticationInformation()
1286         clear_authentication_information.LastUpdateTime = samba.unix2nttime(int(time.time()))
1287         clear_authentication_information.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
1288         clear_authentication_information.AuthInfo = clear_value
1289
1290         authentication_information_array = drsblobs.AuthenticationInformationArray()
1291         authentication_information_array.count = 1
1292         authentication_information_array.array = [clear_authentication_information]
1293
1294         outgoing = drsblobs.trustAuthInOutBlob()
1295         outgoing.count = 1
1296         outgoing.current = authentication_information_array
1297
1298         trustpass = drsblobs.trustDomainPasswords()
1299         confounder = [3] * 512
1300
1301         for i in range(512):
1302             confounder[i] = random.randint(0, 255)
1303
1304         trustpass.confounder = confounder
1305
1306         trustpass.outgoing = outgoing
1307         trustpass.incoming = outgoing
1308
1309         trustpass_blob = ndr_pack(trustpass)
1310
1311         encrypted_trustpass = arcfour_encrypt(lsaconn.session_key, trustpass_blob)
1312
1313         auth_blob = lsa.DATA_BUF2()
1314         auth_blob.size = len(encrypted_trustpass)
1315         auth_blob.data = string_to_byte_array(encrypted_trustpass)
1316
1317         auth_info = lsa.TrustDomainInfoAuthInfoInternal()
1318         auth_info.auth_blob = auth_blob
1319
1320         trustdom_handle = lsaconn.CreateTrustedDomainEx2(pol_handle,
1321                                                          info,
1322                                                          auth_info,
1323                                                          security.SEC_STD_DELETE)
1324
1325         rec = {
1326             "dn" : "cn=%s,cn=system,%s" % (ctx.dnsforest, ctx.base_dn),
1327             "objectclass" : "trustedDomain",
1328             "trustType" : str(info.trust_type),
1329             "trustAttributes" : str(info.trust_attributes),
1330             "trustDirection" : str(info.trust_direction),
1331             "flatname" : ctx.forest_domain_name,
1332             "trustPartner" : ctx.dnsforest,
1333             "trustAuthIncoming" : ndr_pack(outgoing),
1334             "trustAuthOutgoing" : ndr_pack(outgoing),
1335             "securityIdentifier" : ndr_pack(ctx.forestsid)
1336             }
1337         ctx.local_samdb.add(rec)
1338
1339         rec = {
1340             "dn" : "cn=%s$,cn=users,%s" % (ctx.forest_domain_name, ctx.base_dn),
1341             "objectclass" : "user",
1342             "userAccountControl" : str(samba.dsdb.UF_INTERDOMAIN_TRUST_ACCOUNT),
1343             "clearTextPassword" : ctx.trustdom_pass.encode('utf-16-le'),
1344             "samAccountName" : "%s$" % ctx.forest_domain_name
1345             }
1346         ctx.local_samdb.add(rec)
1347
1348
1349     def do_join(ctx):
1350         # nc_list is the list of naming context (NC) for which we will
1351         # replicate in and send a updateRef command to the partner DC
1352
1353         # full_nc_list is the list of naming context (NC) we hold
1354         # read/write copies of.  These are not subsets of each other.
1355         ctx.nc_list = [ ctx.config_dn, ctx.schema_dn ]
1356         ctx.full_nc_list = [ ctx.base_dn, ctx.config_dn, ctx.schema_dn ]
1357
1358         if ctx.subdomain and ctx.dns_backend != "NONE":
1359             ctx.full_nc_list += [ctx.domaindns_zone]
1360
1361         elif not ctx.subdomain:
1362             ctx.nc_list += [ctx.base_dn]
1363
1364             if ctx.dns_backend != "NONE":
1365                 ctx.nc_list += [ctx.domaindns_zone]
1366                 ctx.nc_list += [ctx.forestdns_zone]
1367                 ctx.full_nc_list += [ctx.domaindns_zone]
1368                 ctx.full_nc_list += [ctx.forestdns_zone]
1369
1370         if not ctx.clone_only:
1371             if ctx.promote_existing:
1372                 ctx.promote_possible()
1373             else:
1374                 ctx.cleanup_old_join()
1375
1376         try:
1377             if not ctx.clone_only:
1378                 ctx.join_add_objects()
1379             ctx.join_provision()
1380             ctx.join_replicate()
1381             if (not ctx.clone_only and ctx.subdomain):
1382                 ctx.join_add_objects2()
1383                 ctx.join_provision_own_domain()
1384                 ctx.join_setup_trusts()
1385
1386             if not ctx.clone_only and ctx.dns_backend != "NONE":
1387                 ctx.join_add_dns_records()
1388                 ctx.join_replicate_new_dns_records()
1389
1390             ctx.join_finalise()
1391         except:
1392             try:
1393                 print "Join failed - cleaning up"
1394             except IOError:
1395                 pass
1396             if not ctx.clone_only:
1397                 ctx.cleanup_old_join()
1398             raise
1399
1400
1401 def join_RODC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
1402               targetdir=None, domain=None, domain_critical_only=False,
1403               machinepass=None, use_ntvfs=False, dns_backend=None,
1404               promote_existing=False, plaintext_secrets=False):
1405     """Join as a RODC."""
1406
1407     ctx = dc_join(logger, server, creds, lp, site, netbios_name, targetdir, domain,
1408                   machinepass, use_ntvfs, dns_backend, promote_existing,
1409                   plaintext_secrets)
1410
1411     lp.set("workgroup", ctx.domain_name)
1412     logger.info("workgroup is %s" % ctx.domain_name)
1413
1414     lp.set("realm", ctx.realm)
1415     logger.info("realm is %s" % ctx.realm)
1416
1417     ctx.krbtgt_dn = "CN=krbtgt_%s,CN=Users,%s" % (ctx.myname, ctx.base_dn)
1418
1419     # setup some defaults for accounts that should be replicated to this RODC
1420     ctx.never_reveal_sid = [
1421         "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_DENY),
1422         "<SID=%s>" % security.SID_BUILTIN_ADMINISTRATORS,
1423         "<SID=%s>" % security.SID_BUILTIN_SERVER_OPERATORS,
1424         "<SID=%s>" % security.SID_BUILTIN_BACKUP_OPERATORS,
1425         "<SID=%s>" % security.SID_BUILTIN_ACCOUNT_OPERATORS]
1426     ctx.reveal_sid = "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_ALLOW)
1427
1428     mysid = ctx.get_mysid()
1429     admin_dn = "<SID=%s>" % mysid
1430     ctx.managedby = admin_dn
1431
1432     ctx.userAccountControl = (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT |
1433                               samba.dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION |
1434                               samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT)
1435
1436     ctx.SPNs.extend([ "RestrictedKrbHost/%s" % ctx.myname,
1437                       "RestrictedKrbHost/%s" % ctx.dnshostname ])
1438
1439     ctx.connection_dn = "CN=RODC Connection (FRS),%s" % ctx.ntds_dn
1440     ctx.secure_channel_type = misc.SEC_CHAN_RODC
1441     ctx.RODC = True
1442     ctx.replica_flags |= ( drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING |
1443                            drsuapi.DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP)
1444     ctx.domain_replica_flags = ctx.replica_flags
1445     if domain_critical_only:
1446         ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
1447
1448     ctx.do_join()
1449
1450     logger.info("Joined domain %s (SID %s) as an RODC" % (ctx.domain_name, ctx.domsid))
1451
1452
1453 def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
1454             targetdir=None, domain=None, domain_critical_only=False,
1455             machinepass=None, use_ntvfs=False, dns_backend=None,
1456             promote_existing=False, plaintext_secrets=False):
1457     """Join as a DC."""
1458     ctx = dc_join(logger, server, creds, lp, site, netbios_name, targetdir, domain,
1459                   machinepass, use_ntvfs, dns_backend, promote_existing,
1460                   plaintext_secrets)
1461
1462     lp.set("workgroup", ctx.domain_name)
1463     logger.info("workgroup is %s" % ctx.domain_name)
1464
1465     lp.set("realm", ctx.realm)
1466     logger.info("realm is %s" % ctx.realm)
1467
1468     ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
1469
1470     ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
1471     ctx.secure_channel_type = misc.SEC_CHAN_BDC
1472
1473     ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1474                           drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1475     ctx.domain_replica_flags = ctx.replica_flags
1476     if domain_critical_only:
1477         ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
1478
1479     ctx.do_join()
1480     logger.info("Joined domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))
1481
1482 def join_clone(logger=None, server=None, creds=None, lp=None,
1483                targetdir=None, domain=None, include_secrets=False):
1484     """Join as a DC."""
1485     ctx = dc_join(logger, server, creds, lp, site=None, netbios_name=None, targetdir=targetdir, domain=domain,
1486                   machinepass=None, use_ntvfs=False, dns_backend="NONE", promote_existing=False, clone_only=True)
1487
1488     lp.set("workgroup", ctx.domain_name)
1489     logger.info("workgroup is %s" % ctx.domain_name)
1490
1491     lp.set("realm", ctx.realm)
1492     logger.info("realm is %s" % ctx.realm)
1493
1494     ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1495                           drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1496     if not include_secrets:
1497         ctx.replica_flags |= drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
1498     ctx.domain_replica_flags = ctx.replica_flags
1499
1500     ctx.do_join()
1501     logger.info("Cloned domain %s (SID %s)" % (ctx.domain_name, ctx.domsid))
1502
1503 def join_subdomain(logger=None, server=None, creds=None, lp=None, site=None,
1504         netbios_name=None, targetdir=None, parent_domain=None, dnsdomain=None,
1505         netbios_domain=None, machinepass=None, adminpass=None, use_ntvfs=False,
1506         dns_backend=None, plaintext_secrets=False):
1507     """Join as a DC."""
1508     ctx = dc_join(logger, server, creds, lp, site, netbios_name, targetdir, parent_domain,
1509                   machinepass, use_ntvfs, dns_backend, plaintext_secrets)
1510     ctx.subdomain = True
1511     if adminpass is None:
1512         ctx.adminpass = samba.generate_random_password(12, 32)
1513     else:
1514         ctx.adminpass = adminpass
1515     ctx.parent_domain_name = ctx.domain_name
1516     ctx.domain_name = netbios_domain
1517     ctx.realm = dnsdomain
1518     ctx.parent_dnsdomain = ctx.dnsdomain
1519     ctx.parent_partition_dn = ctx.get_parent_partition_dn()
1520     ctx.dnsdomain = dnsdomain
1521     ctx.partition_dn = "CN=%s,CN=Partitions,%s" % (ctx.domain_name, ctx.config_dn)
1522     ctx.naming_master = ctx.get_naming_master()
1523     if ctx.naming_master != ctx.server:
1524         logger.info("Reconnecting to naming master %s" % ctx.naming_master)
1525         ctx.server = ctx.naming_master
1526         ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
1527                           session_info=system_session(),
1528                           credentials=ctx.creds, lp=ctx.lp)
1529         res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=['dnsHostName'],
1530                                controls=[])
1531         ctx.server = res[0]["dnsHostName"]
1532         logger.info("DNS name of new naming master is %s" % ctx.server)
1533
1534     ctx.base_dn = samba.dn_from_dns_name(dnsdomain)
1535     ctx.forestsid = ctx.domsid
1536     ctx.domsid = security.random_sid()
1537     ctx.acct_dn = None
1538     ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain)
1539     # Windows uses 240 bytes as UTF16 so we do
1540     ctx.trustdom_pass = samba.generate_random_machine_password(120, 120)
1541
1542     ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
1543
1544     ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
1545     ctx.secure_channel_type = misc.SEC_CHAN_BDC
1546
1547     ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1548                           drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1549     ctx.domain_replica_flags = ctx.replica_flags
1550
1551     ctx.do_join()
1552     ctx.logger.info("Created domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))