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