2 # Copyright Andrew Tridgell 2010
3 # Copyright Andrew Bartlett 2010
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.
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.
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/>.
19 from __future__ import print_function
20 """Joining a domain."""
22 from samba.auth import system_session
23 from samba.samdb import SamDB
24 from samba import gensec, Ldb, drs_utils, arcfour_encrypt, string_to_byte_array
28 from samba.ndr import ndr_pack, ndr_unpack
29 from samba.dcerpc import security, drsuapi, misc, nbt, lsa, drsblobs, dnsserver, dnsp
30 from samba.dsdb import DS_DOMAIN_FUNCTION_2003
31 from samba.credentials import Credentials, DONT_USE_KERBEROS
32 from samba.provision import (secretsdb_self_join, provision, provision_fill,
33 FILL_DRS, FILL_SUBDOMAIN, DEFAULTSITE)
34 from samba.provision.common import setup_path
35 from samba.schema import Schema
36 from samba import descriptor
37 from samba.net import Net
38 from samba.provision.sambadns import setup_bind9_dns
39 from samba import read_and_sub_file
40 from samba import werror
41 from base64 import b64encode
42 from samba import WERRORError, NTSTATUSError
43 from samba import sd_utils
44 from samba.dnsserver import ARecord, AAAARecord, CNameRecord
51 from samba.compat import text_type
52 from samba.compat import get_string
55 class DCJoinException(Exception):
57 def __init__(self, msg):
58 super(DCJoinException, self).__init__("Can't join, error: %s" % msg)
61 class DCJoinContext(object):
62 """Perform a DC join."""
64 def __init__(ctx, logger=None, server=None, creds=None, lp=None, site=None,
65 netbios_name=None, targetdir=None, domain=None,
66 machinepass=None, use_ntvfs=False, dns_backend=None,
67 promote_existing=False, plaintext_secrets=False,
68 backend_store=None, forced_local_samdb=None):
74 ctx.targetdir = targetdir
75 ctx.use_ntvfs = use_ntvfs
76 ctx.plaintext_secrets = plaintext_secrets
77 ctx.backend_store = backend_store
79 ctx.promote_existing = promote_existing
80 ctx.promote_from_dn = None
85 ctx.creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
86 ctx.net = Net(creds=ctx.creds, lp=ctx.lp)
89 ctx.forced_local_samdb = forced_local_samdb
91 if forced_local_samdb:
92 ctx.samdb = forced_local_samdb
93 ctx.server = ctx.samdb.url
96 # work out the DC's site (if not already specified)
98 ctx.site = ctx.find_dc_site(ctx.server)
100 # work out the Primary DC for the domain (as well as an
101 # appropriate site for the new DC)
102 ctx.logger.info("Finding a writeable DC for domain '%s'" % domain)
103 ctx.server = ctx.find_dc(domain)
104 ctx.logger.info("Found DC %s" % ctx.server)
105 ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
106 session_info=system_session(),
107 credentials=ctx.creds, lp=ctx.lp)
110 ctx.site = DEFAULTSITE
113 ctx.samdb.search(scope=ldb.SCOPE_BASE, attrs=[])
114 except ldb.LdbError as e:
115 (enum, estr) = e.args
116 raise DCJoinException(estr)
118 ctx.base_dn = str(ctx.samdb.get_default_basedn())
119 ctx.root_dn = str(ctx.samdb.get_root_basedn())
120 ctx.schema_dn = str(ctx.samdb.get_schema_basedn())
121 ctx.config_dn = str(ctx.samdb.get_config_basedn())
122 ctx.domsid = security.dom_sid(ctx.samdb.get_domain_sid())
123 ctx.forestsid = ctx.domsid
124 ctx.domain_name = ctx.get_domain_name()
125 ctx.forest_domain_name = ctx.get_forest_domain_name()
126 ctx.invocation_id = misc.GUID(str(uuid.uuid4()))
128 ctx.dc_ntds_dn = ctx.samdb.get_dsServiceName()
129 ctx.dc_dnsHostName = ctx.get_dnsHostName()
130 ctx.behavior_version = ctx.get_behavior_version()
132 if machinepass is not None:
133 ctx.acct_pass = machinepass
135 ctx.acct_pass = samba.generate_random_machine_password(128, 255)
137 ctx.dnsdomain = ctx.samdb.domain_dns_name()
139 # the following are all dependent on the new DC's netbios_name (which
140 # we expect to always be specified, except when cloning a DC)
142 # work out the DNs of all the objects we will be adding
143 ctx.myname = netbios_name
144 ctx.samname = "%s$" % ctx.myname
145 ctx.server_dn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (ctx.myname, ctx.site, ctx.config_dn)
146 ctx.ntds_dn = "CN=NTDS Settings,%s" % ctx.server_dn
147 ctx.acct_dn = "CN=%s,OU=Domain Controllers,%s" % (ctx.myname, ctx.base_dn)
148 ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain)
149 ctx.dnsforest = ctx.samdb.forest_dns_name()
151 topology_base = "CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,CN=System,%s" % ctx.base_dn
152 if ctx.dn_exists(topology_base):
153 ctx.topology_dn = "CN=%s,%s" % (ctx.myname, topology_base)
155 ctx.topology_dn = None
157 ctx.SPNs = ["HOST/%s" % ctx.myname,
158 "HOST/%s" % ctx.dnshostname,
159 "GC/%s/%s" % (ctx.dnshostname, ctx.dnsforest)]
161 res_rid_manager = ctx.samdb.search(scope=ldb.SCOPE_BASE,
162 attrs=["rIDManagerReference"],
165 ctx.rid_manager_dn = res_rid_manager[0]["rIDManagerReference"][0]
167 ctx.domaindns_zone = 'DC=DomainDnsZones,%s' % ctx.base_dn
168 ctx.forestdns_zone = 'DC=ForestDnsZones,%s' % ctx.root_dn
170 expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.domaindns_zone)
171 res_domaindns = ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
173 base=ctx.samdb.get_partitions_dn(),
175 if dns_backend is None:
176 ctx.dns_backend = "NONE"
178 if len(res_domaindns) == 0:
179 ctx.dns_backend = "NONE"
180 print("NO DNS zone information found in source domain, not replicating DNS")
182 ctx.dns_backend = dns_backend
184 ctx.realm = ctx.dnsdomain
188 ctx.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC |
189 drsuapi.DRSUAPI_DRS_PER_SYNC |
190 drsuapi.DRSUAPI_DRS_GET_ANC |
191 drsuapi.DRSUAPI_DRS_GET_NC_SIZE |
192 drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
194 # these elements are optional
195 ctx.never_reveal_sid = None
196 ctx.reveal_sid = None
197 ctx.connection_dn = None
202 ctx.subdomain = False
204 ctx.partition_dn = None
207 ctx.dns_cname_dn = None
209 # Do not normally register 127. addresses but allow override for selftest
210 ctx.force_all_ips = False
212 def del_noerror(ctx, dn, recursive=False):
215 res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_ONELEVEL, attrs=["dn"])
219 ctx.del_noerror(r.dn, recursive=True)
222 print("Deleted %s" % dn)
226 def cleanup_old_accounts(ctx, force=False):
227 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
228 expression='sAMAccountName=%s' % ldb.binary_encode(ctx.samname),
229 attrs=["msDS-krbTgtLink", "objectSID"])
234 creds = Credentials()
237 creds.set_machine_account(ctx.lp)
238 creds.set_kerberos_state(ctx.creds.get_kerberos_state())
239 machine_samdb = SamDB(url="ldap://%s" % ctx.server,
240 session_info=system_session(),
241 credentials=creds, lp=ctx.lp)
245 token_res = machine_samdb.search(scope=ldb.SCOPE_BASE, base="", attrs=["tokenGroups"])
246 if token_res[0]["tokenGroups"][0] \
247 == res[0]["objectSID"][0]:
248 raise DCJoinException("Not removing account %s which "
249 "looks like a Samba DC account "
250 "matching the password we already have. "
251 "To override, remove secrets.ldb and secrets.tdb"
254 ctx.del_noerror(res[0].dn, recursive=True)
256 if "msDS-Krbtgtlink" in res[0]:
257 new_krbtgt_dn = res[0]["msDS-Krbtgtlink"][0]
258 ctx.del_noerror(ctx.new_krbtgt_dn)
260 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
261 expression='(&(sAMAccountName=%s)(servicePrincipalName=%s))' %
262 (ldb.binary_encode("dns-%s" % ctx.myname),
263 ldb.binary_encode("dns/%s" % ctx.dnshostname)),
266 ctx.del_noerror(res[0].dn, recursive=True)
268 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
269 expression='(sAMAccountName=%s)' % ldb.binary_encode("dns-%s" % ctx.myname),
272 raise DCJoinException("Not removing account %s which looks like "
273 "a Samba DNS service account but does not "
274 "have servicePrincipalName=%s" %
275 (ldb.binary_encode("dns-%s" % ctx.myname),
276 ldb.binary_encode("dns/%s" % ctx.dnshostname)))
278 def cleanup_old_join(ctx, force=False):
279 """Remove any DNs from a previous join."""
280 # find the krbtgt link
281 if not ctx.subdomain:
282 ctx.cleanup_old_accounts(force=force)
284 if ctx.connection_dn is not None:
285 ctx.del_noerror(ctx.connection_dn)
286 if ctx.krbtgt_dn is not None:
287 ctx.del_noerror(ctx.krbtgt_dn)
288 ctx.del_noerror(ctx.ntds_dn)
289 ctx.del_noerror(ctx.server_dn, recursive=True)
291 ctx.del_noerror(ctx.topology_dn)
293 ctx.del_noerror(ctx.partition_dn)
296 binding_options = "sign"
297 lsaconn = lsa.lsarpc("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
300 objectAttr = lsa.ObjectAttribute()
301 objectAttr.sec_qos = lsa.QosInfo()
303 pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
304 objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
307 name.string = ctx.realm
308 info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
310 lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
313 name.string = ctx.forest_domain_name
314 info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
316 lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
319 ctx.del_noerror(ctx.dns_a_dn)
322 ctx.del_noerror(ctx.dns_cname_dn)
324 def promote_possible(ctx):
325 """confirm that the account is just a bare NT4 BDC or a member server, so can be safely promoted"""
327 # This shouldn't happen
328 raise Exception("Can not promote into a subdomain")
330 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
331 expression='sAMAccountName=%s' % ldb.binary_encode(ctx.samname),
332 attrs=["msDS-krbTgtLink", "userAccountControl", "serverReferenceBL", "rIDSetReferences"])
334 raise Exception("Could not find domain member account '%s' to promote to a DC, use 'samba-tool domain join' instead'" % ctx.samname)
335 if "msDS-krbTgtLink" in res[0] or "serverReferenceBL" in res[0] or "rIDSetReferences" in res[0]:
336 raise Exception("Account '%s' appears to be an active DC, use 'samba-tool domain join' if you must re-create this account" % ctx.samname)
337 if (int(res[0]["userAccountControl"][0]) & (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT |
338 samba.dsdb.UF_SERVER_TRUST_ACCOUNT) == 0):
339 raise Exception("Account %s is not a domain member or a bare NT4 BDC, use 'samba-tool domain join' instead'" % ctx.samname)
341 ctx.promote_from_dn = res[0].dn
343 def find_dc(ctx, domain):
344 """find a writeable DC for the given domain"""
346 ctx.cldap_ret = ctx.net.finddc(domain=domain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS | nbt.NBT_SERVER_WRITABLE)
347 except NTSTATUSError as error:
348 raise Exception("Failed to find a writeable DC for domain '%s': %s" %
351 raise Exception("Failed to find a writeable DC for domain '%s'" % domain)
352 if ctx.cldap_ret.client_site is not None and ctx.cldap_ret.client_site != "":
353 ctx.site = ctx.cldap_ret.client_site
354 return ctx.cldap_ret.pdc_dns_name
356 def find_dc_site(ctx, server):
358 cldap_ret = ctx.net.finddc(address=server,
359 flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
360 if cldap_ret.client_site is not None and cldap_ret.client_site != "":
361 site = cldap_ret.client_site
364 def get_behavior_version(ctx):
365 res = ctx.samdb.search(base=ctx.base_dn, scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
366 if "msDS-Behavior-Version" in res[0]:
367 return int(res[0]["msDS-Behavior-Version"][0])
369 return samba.dsdb.DS_DOMAIN_FUNCTION_2000
371 def get_dnsHostName(ctx):
372 res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dnsHostName"])
373 return str(res[0]["dnsHostName"][0])
375 def get_domain_name(ctx):
376 '''get netbios name of the domain from the partitions record'''
377 partitions_dn = ctx.samdb.get_partitions_dn()
378 res = ctx.samdb.search(base=partitions_dn, scope=ldb.SCOPE_ONELEVEL, attrs=["nETBIOSName"],
379 expression='ncName=%s' % ldb.binary_encode(str(ctx.samdb.get_default_basedn())))
380 return str(res[0]["nETBIOSName"][0])
382 def get_forest_domain_name(ctx):
383 '''get netbios name of the domain from the partitions record'''
384 partitions_dn = ctx.samdb.get_partitions_dn()
385 res = ctx.samdb.search(base=partitions_dn, scope=ldb.SCOPE_ONELEVEL, attrs=["nETBIOSName"],
386 expression='ncName=%s' % ldb.binary_encode(str(ctx.samdb.get_root_basedn())))
387 return str(res[0]["nETBIOSName"][0])
389 def get_parent_partition_dn(ctx):
390 '''get the parent domain partition DN from parent DNS name'''
391 res = ctx.samdb.search(base=ctx.config_dn, attrs=[],
392 expression='(&(objectclass=crossRef)(dnsRoot=%s)(systemFlags:%s:=%u))' %
393 (ldb.binary_encode(ctx.parent_dnsdomain),
394 ldb.OID_COMPARATOR_AND, samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN))
395 return str(res[0].dn)
397 def get_naming_master(ctx):
398 '''get the parent domain partition DN from parent DNS name'''
399 res = ctx.samdb.search(base='CN=Partitions,%s' % ctx.config_dn, attrs=['fSMORoleOwner'],
400 scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"])
401 if 'fSMORoleOwner' not in res[0]:
402 raise DCJoinException("Can't find naming master on partition DN %s in %s" % (ctx.partition_dn, ctx.samdb.url))
404 master_guid = str(misc.GUID(ldb.Dn(ctx.samdb, res[0]['fSMORoleOwner'][0].decode('utf8')).get_extended_component('GUID')))
406 raise DCJoinException("Can't find GUID in naming master on partition DN %s" % res[0]['fSMORoleOwner'][0])
408 master_host = '%s._msdcs.%s' % (master_guid, ctx.dnsforest)
412 '''get the SID of the connected user. Only works with w2k8 and later,
413 so only used for RODC join'''
414 res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["tokenGroups"])
415 binsid = res[0]["tokenGroups"][0]
416 return get_string(ctx.samdb.schema_format_value("objectSID", binsid))
418 def dn_exists(ctx, dn):
419 '''check if a DN exists'''
421 res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[])
422 except ldb.LdbError as e5:
423 (enum, estr) = e5.args
424 if enum == ldb.ERR_NO_SUCH_OBJECT:
429 def add_krbtgt_account(ctx):
430 '''RODCs need a special krbtgt account'''
431 print("Adding %s" % ctx.krbtgt_dn)
434 "objectclass": "user",
435 "useraccountcontrol": str(samba.dsdb.UF_NORMAL_ACCOUNT |
436 samba.dsdb.UF_ACCOUNTDISABLE),
437 "showinadvancedviewonly": "TRUE",
438 "description": "krbtgt for %s" % ctx.samname}
439 ctx.samdb.add(rec, ["rodc_join:1:1"])
441 # now we need to search for the samAccountName attribute on the krbtgt DN,
442 # as this will have been magically set to the krbtgt number
443 res = ctx.samdb.search(base=ctx.krbtgt_dn, scope=ldb.SCOPE_BASE, attrs=["samAccountName"])
444 ctx.krbtgt_name = res[0]["samAccountName"][0]
446 print("Got krbtgt_name=%s" % ctx.krbtgt_name)
449 m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
450 m["msDS-krbTgtLink"] = ldb.MessageElement(ctx.krbtgt_dn,
451 ldb.FLAG_MOD_REPLACE, "msDS-krbTgtLink")
454 ctx.new_krbtgt_dn = "CN=%s,CN=Users,%s" % (ctx.krbtgt_name, ctx.base_dn)
455 print("Renaming %s to %s" % (ctx.krbtgt_dn, ctx.new_krbtgt_dn))
456 ctx.samdb.rename(ctx.krbtgt_dn, ctx.new_krbtgt_dn)
458 def drsuapi_connect(ctx):
459 '''make a DRSUAPI connection to the naming master'''
460 binding_options = "seal"
461 if ctx.lp.log_level() >= 9:
462 binding_options += ",print"
463 binding_string = "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options)
464 ctx.drsuapi = drsuapi.drsuapi(binding_string, ctx.lp, ctx.creds)
465 (ctx.drsuapi_handle, ctx.bind_supported_extensions) = drs_utils.drs_DsBind(ctx.drsuapi)
467 def create_tmp_samdb(ctx):
468 '''create a temporary samdb object for schema queries'''
469 ctx.tmp_schema = Schema(ctx.domsid,
470 schemadn=ctx.schema_dn)
471 ctx.tmp_samdb = SamDB(session_info=system_session(), url=None, auto_connect=False,
472 credentials=ctx.creds, lp=ctx.lp, global_schema=False,
474 ctx.tmp_samdb.set_schema(ctx.tmp_schema)
476 def build_DsReplicaAttribute(ctx, attrname, attrvalue):
477 '''build a DsReplicaAttributeCtr object'''
478 r = drsuapi.DsReplicaAttribute()
479 r.attid = ctx.tmp_samdb.get_attid_from_lDAPDisplayName(attrname)
482 def DsAddEntry(ctx, recs):
483 '''add a record via the DRSUAPI DsAddEntry call'''
484 if ctx.drsuapi is None:
485 ctx.drsuapi_connect()
486 if ctx.tmp_samdb is None:
487 ctx.create_tmp_samdb()
491 id = drsuapi.DsReplicaObjectIdentifier()
498 if not isinstance(rec[a], list):
502 v = [x.encode('utf8') if isinstance(x, text_type) else x for x in v]
503 rattr = ctx.tmp_samdb.dsdb_DsReplicaAttribute(ctx.tmp_samdb, a, v)
506 attribute_ctr = drsuapi.DsReplicaAttributeCtr()
507 attribute_ctr.num_attributes = len(attrs)
508 attribute_ctr.attributes = attrs
510 object = drsuapi.DsReplicaObject()
511 object.identifier = id
512 object.attribute_ctr = attribute_ctr
514 list_object = drsuapi.DsReplicaObjectListItem()
515 list_object.object = object
516 objects.append(list_object)
518 req2 = drsuapi.DsAddEntryRequest2()
519 req2.first_object = objects[0]
520 prev = req2.first_object
521 for o in objects[1:]:
525 (level, ctr) = ctx.drsuapi.DsAddEntry(ctx.drsuapi_handle, 2, req2)
527 if ctr.dir_err != drsuapi.DRSUAPI_DIRERR_OK:
528 print("DsAddEntry failed with dir_err %u" % ctr.dir_err)
529 raise RuntimeError("DsAddEntry failed")
530 if ctr.extended_err[0] != werror.WERR_SUCCESS:
531 print("DsAddEntry failed with status %s info %s" % (ctr.extended_err))
532 raise RuntimeError("DsAddEntry failed")
535 raise RuntimeError("expected err_ver 1, got %u" % ctr.err_ver)
536 if ctr.err_data.status[0] != werror.WERR_SUCCESS:
537 if ctr.err_data.info is None:
538 print("DsAddEntry failed with status %s, info omitted" % (ctr.err_data.status[1]))
540 print("DsAddEntry failed with status %s info %s" % (ctr.err_data.status[1],
541 ctr.err_data.info.extended_err))
542 raise RuntimeError("DsAddEntry failed")
543 if ctr.err_data.dir_err != drsuapi.DRSUAPI_DIRERR_OK:
544 print("DsAddEntry failed with dir_err %u" % ctr.err_data.dir_err)
545 raise RuntimeError("DsAddEntry failed")
549 def join_ntdsdsa_obj(ctx):
550 '''return the ntdsdsa object to add'''
552 print("Adding %s" % ctx.ntds_dn)
555 "objectclass": "nTDSDSA",
556 "systemFlags": str(samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
557 "dMDLocation": ctx.schema_dn}
559 nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn]
561 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
562 rec["msDS-Behavior-Version"] = str(samba.dsdb.DS_DOMAIN_FUNCTION_2008_R2)
564 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
565 rec["msDS-HasDomainNCs"] = ctx.base_dn
568 rec["objectCategory"] = "CN=NTDS-DSA-RO,%s" % ctx.schema_dn
569 rec["msDS-HasFullReplicaNCs"] = ctx.full_nc_list
570 rec["options"] = "37"
572 rec["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn
573 rec["HasMasterNCs"] = []
575 if nc in ctx.full_nc_list:
576 rec["HasMasterNCs"].append(nc)
577 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
578 rec["msDS-HasMasterNCs"] = ctx.full_nc_list
580 rec["invocationId"] = ndr_pack(ctx.invocation_id)
584 def join_add_ntdsdsa(ctx):
585 '''add the ntdsdsa object'''
587 rec = ctx.join_ntdsdsa_obj()
588 if ctx.forced_local_samdb:
589 ctx.samdb.add(rec, controls=["relax:0"])
591 ctx.samdb.add(rec, ["rodc_join:1:1"])
593 ctx.DsAddEntry([rec])
595 # find the GUID of our NTDS DN
596 res = ctx.samdb.search(base=ctx.ntds_dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"])
597 ctx.ntds_guid = misc.GUID(ctx.samdb.schema_format_value("objectGUID", res[0]["objectGUID"][0]))
599 def join_add_objects(ctx, specified_sid=None):
600 '''add the various objects needed for the join'''
602 print("Adding %s" % ctx.acct_dn)
605 "objectClass": "computer",
606 "displayname": ctx.samname,
607 "samaccountname": ctx.samname,
608 "userAccountControl": str(ctx.userAccountControl | samba.dsdb.UF_ACCOUNTDISABLE),
609 "dnshostname": ctx.dnshostname}
610 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2008:
611 rec['msDS-SupportedEncryptionTypes'] = str(samba.dsdb.ENC_ALL_TYPES)
612 elif ctx.promote_existing:
613 rec['msDS-SupportedEncryptionTypes'] = []
615 rec["managedby"] = ctx.managedby
616 elif ctx.promote_existing:
617 rec["managedby"] = []
619 if ctx.never_reveal_sid:
620 rec["msDS-NeverRevealGroup"] = ctx.never_reveal_sid
621 elif ctx.promote_existing:
622 rec["msDS-NeverRevealGroup"] = []
625 rec["msDS-RevealOnDemandGroup"] = ctx.reveal_sid
626 elif ctx.promote_existing:
627 rec["msDS-RevealOnDemandGroup"] = []
630 rec["objectSid"] = ndr_pack(specified_sid)
632 if ctx.promote_existing:
633 if ctx.promote_from_dn != ctx.acct_dn:
634 ctx.samdb.rename(ctx.promote_from_dn, ctx.acct_dn)
635 ctx.samdb.modify(ldb.Message.from_dict(ctx.samdb, rec, ldb.FLAG_MOD_REPLACE))
638 if specified_sid is not None:
639 controls = ["relax:0"]
640 ctx.samdb.add(rec, controls=controls)
643 ctx.add_krbtgt_account()
646 print("Adding %s" % ctx.server_dn)
649 "objectclass": "server",
650 # windows uses 50000000 decimal for systemFlags. A windows hex/decimal mixup bug?
651 "systemFlags": str(samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_RENAME |
652 samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE |
653 samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
654 # windows seems to add the dnsHostName later
655 "dnsHostName": ctx.dnshostname}
658 rec["serverReference"] = ctx.acct_dn
663 # the rest is done after replication
668 ctx.join_add_ntdsdsa()
670 # Add the Replica-Locations or RO-Replica-Locations attributes
671 # TODO Is this supposed to be for the schema partition too?
672 expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.domaindns_zone)
673 domain = (ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
675 base=ctx.samdb.get_partitions_dn(),
676 expression=expr), ctx.domaindns_zone)
678 expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.forestdns_zone)
679 forest = (ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
681 base=ctx.samdb.get_partitions_dn(),
682 expression=expr), ctx.forestdns_zone)
684 for part, zone in (domain, forest):
685 if zone not in ctx.nc_list:
691 attr = "msDS-NC-Replica-Locations"
693 attr = "msDS-NC-RO-Replica-Locations"
695 m[attr] = ldb.MessageElement(ctx.ntds_dn,
696 ldb.FLAG_MOD_ADD, attr)
699 if ctx.connection_dn is not None:
700 print("Adding %s" % ctx.connection_dn)
702 "dn": ctx.connection_dn,
703 "objectclass": "nTDSConnection",
704 "enabledconnection": "TRUE",
706 "fromServer": ctx.dc_ntds_dn}
710 print("Adding SPNs to %s" % ctx.acct_dn)
712 m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
713 for i in range(len(ctx.SPNs)):
714 ctx.SPNs[i] = ctx.SPNs[i].replace("$NTDSGUID", str(ctx.ntds_guid))
715 m["servicePrincipalName"] = ldb.MessageElement(ctx.SPNs,
716 ldb.FLAG_MOD_REPLACE,
717 "servicePrincipalName")
720 # The account password set operation should normally be done over
721 # LDAP. Windows 2000 DCs however allow this only with SSL
722 # connections which are hard to set up and otherwise refuse with
723 # ERR_UNWILLING_TO_PERFORM. In this case we fall back to libnet
725 print("Setting account password for %s" % ctx.samname)
727 ctx.samdb.setpassword("(&(objectClass=user)(sAMAccountName=%s))"
728 % ldb.binary_encode(ctx.samname),
730 force_change_at_next_login=False,
731 username=ctx.samname)
732 except ldb.LdbError as e2:
734 if num != ldb.ERR_UNWILLING_TO_PERFORM:
736 ctx.net.set_password(account_name=ctx.samname,
737 domain_name=ctx.domain_name,
738 newpassword=ctx.acct_pass.encode('utf-8'))
740 res = ctx.samdb.search(base=ctx.acct_dn, scope=ldb.SCOPE_BASE,
741 attrs=["msDS-KeyVersionNumber",
743 if "msDS-KeyVersionNumber" in res[0]:
744 ctx.key_version_number = int(res[0]["msDS-KeyVersionNumber"][0])
746 ctx.key_version_number = None
748 ctx.new_dc_account_sid = ndr_unpack(security.dom_sid,
749 res[0]["objectSid"][0])
751 print("Enabling account")
753 m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
754 m["userAccountControl"] = ldb.MessageElement(str(ctx.userAccountControl),
755 ldb.FLAG_MOD_REPLACE,
756 "userAccountControl")
759 if ctx.dns_backend.startswith("BIND9_"):
760 ctx.dnspass = samba.generate_random_password(128, 255)
762 recs = ctx.samdb.parse_ldif(read_and_sub_file(setup_path("provision_dns_add_samba.ldif"),
763 {"DNSDOMAIN": ctx.dnsdomain,
764 "DOMAINDN": ctx.base_dn,
765 "HOSTNAME": ctx.myname,
766 "DNSPASS_B64": b64encode(ctx.dnspass.encode('utf-16-le')).decode('utf8'),
767 "DNSNAME": ctx.dnshostname}))
768 for changetype, msg in recs:
769 assert changetype == ldb.CHANGETYPE_NONE
770 dns_acct_dn = msg["dn"]
771 print("Adding DNS account %s with dns/ SPN" % msg["dn"])
773 # Remove dns password (we will set it as a modify, as we can't do clearTextPassword over LDAP)
774 del msg["clearTextPassword"]
775 # Remove isCriticalSystemObject for similar reasons, it cannot be set over LDAP
776 del msg["isCriticalSystemObject"]
777 # Disable account until password is set
778 msg["userAccountControl"] = str(samba.dsdb.UF_NORMAL_ACCOUNT |
779 samba.dsdb.UF_ACCOUNTDISABLE)
782 except ldb.LdbError as e:
784 if num != ldb.ERR_ENTRY_ALREADY_EXISTS:
787 # The account password set operation should normally be done over
788 # LDAP. Windows 2000 DCs however allow this only with SSL
789 # connections which are hard to set up and otherwise refuse with
790 # ERR_UNWILLING_TO_PERFORM. In this case we fall back to libnet
792 print("Setting account password for dns-%s" % ctx.myname)
794 ctx.samdb.setpassword("(&(objectClass=user)(samAccountName=dns-%s))"
795 % ldb.binary_encode(ctx.myname),
797 force_change_at_next_login=False,
798 username=ctx.samname)
799 except ldb.LdbError as e3:
801 if num != ldb.ERR_UNWILLING_TO_PERFORM:
803 ctx.net.set_password(account_name="dns-%s" % ctx.myname,
804 domain_name=ctx.domain_name,
805 newpassword=ctx.dnspass)
807 res = ctx.samdb.search(base=dns_acct_dn, scope=ldb.SCOPE_BASE,
808 attrs=["msDS-KeyVersionNumber"])
809 if "msDS-KeyVersionNumber" in res[0]:
810 ctx.dns_key_version_number = int(res[0]["msDS-KeyVersionNumber"][0])
812 ctx.dns_key_version_number = None
814 def join_add_objects2(ctx):
815 """add the various objects needed for the join, for subdomains post replication"""
817 print("Adding %s" % ctx.partition_dn)
818 name_map = {'SubdomainAdmins': "%s-%s" % (str(ctx.domsid), security.DOMAIN_RID_ADMINS)}
819 sd_binary = descriptor.get_paritions_crossref_subdomain_descriptor(ctx.forestsid, name_map=name_map)
821 "dn": ctx.partition_dn,
822 "objectclass": "crossRef",
823 "objectCategory": "CN=Cross-Ref,%s" % ctx.schema_dn,
824 "nCName": ctx.base_dn,
825 "nETBIOSName": ctx.domain_name,
826 "dnsRoot": ctx.dnsdomain,
827 "trustParent": ctx.parent_partition_dn,
828 "systemFlags": str(samba.dsdb.SYSTEM_FLAG_CR_NTDS_NC |samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN),
829 "ntSecurityDescriptor": sd_binary,
832 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
833 rec["msDS-Behavior-Version"] = str(ctx.behavior_version)
835 rec2 = ctx.join_ntdsdsa_obj()
837 objects = ctx.DsAddEntry([rec, rec2])
838 if len(objects) != 2:
839 raise DCJoinException("Expected 2 objects from DsAddEntry")
841 ctx.ntds_guid = objects[1].guid
843 print("Replicating partition DN")
844 ctx.repl.replicate(ctx.partition_dn,
845 misc.GUID("00000000-0000-0000-0000-000000000000"),
847 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
848 replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP)
850 print("Replicating NTDS DN")
851 ctx.repl.replicate(ctx.ntds_dn,
852 misc.GUID("00000000-0000-0000-0000-000000000000"),
854 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
855 replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP)
857 def join_provision(ctx):
858 """Provision the local SAM."""
860 print("Calling bare provision")
862 smbconf = ctx.lp.configfile
864 presult = provision(ctx.logger, system_session(), smbconf=smbconf,
865 targetdir=ctx.targetdir, samdb_fill=FILL_DRS, realm=ctx.realm,
866 rootdn=ctx.root_dn, domaindn=ctx.base_dn,
867 schemadn=ctx.schema_dn, configdn=ctx.config_dn,
868 serverdn=ctx.server_dn, domain=ctx.domain_name,
869 hostname=ctx.myname, domainsid=ctx.domsid,
870 machinepass=ctx.acct_pass, serverrole="active directory domain controller",
871 sitename=ctx.site, lp=ctx.lp, ntdsguid=ctx.ntds_guid,
872 use_ntvfs=ctx.use_ntvfs, dns_backend=ctx.dns_backend,
873 plaintext_secrets=ctx.plaintext_secrets,
874 backend_store=ctx.backend_store
876 print("Provision OK for domain DN %s" % presult.domaindn)
877 ctx.local_samdb = presult.samdb
879 ctx.paths = presult.paths
880 ctx.names = presult.names
882 # Fix up the forestsid, it may be different if we are joining as a subdomain
883 ctx.names.forestsid = ctx.forestsid
885 def join_provision_own_domain(ctx):
886 """Provision the local SAM."""
888 # we now operate exclusively on the local database, which
889 # we need to reopen in order to get the newly created schema
890 print("Reconnecting to local samdb")
891 ctx.samdb = SamDB(url=ctx.local_samdb.url,
892 session_info=system_session(),
893 lp=ctx.local_samdb.lp,
895 ctx.samdb.set_invocation_id(str(ctx.invocation_id))
896 ctx.local_samdb = ctx.samdb
898 ctx.logger.info("Finding domain GUID from ncName")
899 res = ctx.local_samdb.search(base=ctx.partition_dn, scope=ldb.SCOPE_BASE, attrs=['ncName'],
900 controls=["extended_dn:1:1", "reveal_internals:0"])
902 if 'nCName' not in res[0]:
903 raise DCJoinException("Can't find naming context on partition DN %s in %s" % (ctx.partition_dn, ctx.samdb.url))
906 ctx.names.domainguid = str(misc.GUID(ldb.Dn(ctx.samdb, res[0]['ncName'][0].decode('utf8')).get_extended_component('GUID')))
908 raise DCJoinException("Can't find GUID in naming master on partition DN %s" % res[0]['ncName'][0])
910 ctx.logger.info("Got domain GUID %s" % ctx.names.domainguid)
912 ctx.logger.info("Calling own domain provision")
914 secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
916 presult = provision_fill(ctx.local_samdb, secrets_ldb,
917 ctx.logger, ctx.names, ctx.paths,
918 dom_for_fun_level=DS_DOMAIN_FUNCTION_2003,
919 targetdir=ctx.targetdir, samdb_fill=FILL_SUBDOMAIN,
920 machinepass=ctx.acct_pass, serverrole="active directory domain controller",
921 lp=ctx.lp, hostip=ctx.names.hostip, hostip6=ctx.names.hostip6,
922 dns_backend=ctx.dns_backend, adminpass=ctx.adminpass)
923 print("Provision OK for domain %s" % ctx.names.dnsdomain)
925 def create_replicator(ctx, repl_creds, binding_options):
926 '''Creates a new DRS object for managing replications'''
927 return drs_utils.drs_Replicate(
928 "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
929 ctx.lp, repl_creds, ctx.local_samdb, ctx.invocation_id)
931 def join_replicate(ctx):
932 """Replicate the SAM."""
934 print("Starting replication")
935 ctx.local_samdb.transaction_start()
937 source_dsa_invocation_id = misc.GUID(ctx.samdb.get_invocation_id())
938 if ctx.ntds_guid is None:
939 print("Using DS_BIND_GUID_W2K3")
940 destination_dsa_guid = misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID_W2K3)
942 destination_dsa_guid = ctx.ntds_guid
945 repl_creds = Credentials()
946 repl_creds.guess(ctx.lp)
947 repl_creds.set_kerberos_state(DONT_USE_KERBEROS)
948 repl_creds.set_username(ctx.samname)
949 repl_creds.set_password(ctx.acct_pass.encode('utf-8'))
951 repl_creds = ctx.creds
953 binding_options = "seal"
954 if ctx.lp.log_level() >= 9:
955 binding_options += ",print"
957 repl = ctx.create_replicator(repl_creds, binding_options)
959 repl.replicate(ctx.schema_dn, source_dsa_invocation_id,
960 destination_dsa_guid, schema=True, rodc=ctx.RODC,
961 replica_flags=ctx.replica_flags)
962 repl.replicate(ctx.config_dn, source_dsa_invocation_id,
963 destination_dsa_guid, rodc=ctx.RODC,
964 replica_flags=ctx.replica_flags)
965 if not ctx.subdomain:
966 # Replicate first the critical object for the basedn
967 if not ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
968 print("Replicating critical objects from the base DN of the domain")
969 ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
970 repl.replicate(ctx.base_dn, source_dsa_invocation_id,
971 destination_dsa_guid, rodc=ctx.RODC,
972 replica_flags=ctx.domain_replica_flags)
973 ctx.domain_replica_flags ^= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
974 repl.replicate(ctx.base_dn, source_dsa_invocation_id,
975 destination_dsa_guid, rodc=ctx.RODC,
976 replica_flags=ctx.domain_replica_flags)
977 print("Done with always replicated NC (base, config, schema)")
979 # At this point we should already have an entry in the ForestDNS
980 # and DomainDNS NC (those under CN=Partions,DC=...) in order to
981 # indicate that we hold a replica for this NC.
982 for nc in (ctx.domaindns_zone, ctx.forestdns_zone):
983 if nc in ctx.nc_list:
984 print("Replicating %s" % (str(nc)))
985 repl.replicate(nc, source_dsa_invocation_id,
986 destination_dsa_guid, rodc=ctx.RODC,
987 replica_flags=ctx.replica_flags)
990 repl.replicate(ctx.acct_dn, source_dsa_invocation_id,
991 destination_dsa_guid,
992 exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
993 repl.replicate(ctx.new_krbtgt_dn, source_dsa_invocation_id,
994 destination_dsa_guid,
995 exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
996 elif ctx.rid_manager_dn is not None:
997 # Try and get a RID Set if we can. This is only possible against the RID Master. Warn otherwise.
999 repl.replicate(ctx.rid_manager_dn, source_dsa_invocation_id,
1000 destination_dsa_guid,
1001 exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC)
1002 except samba.DsExtendedError as e1:
1003 (enum, estr) = e1.args
1004 if enum == drsuapi.DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER:
1005 print("WARNING: Unable to replicate own RID Set, as server %s (the server we joined) is not the RID Master." % ctx.server)
1006 print("NOTE: This is normal and expected, Samba will be able to create users after it contacts the RID Master at first startup.")
1011 ctx.source_dsa_invocation_id = source_dsa_invocation_id
1012 ctx.destination_dsa_guid = destination_dsa_guid
1014 print("Committing SAM database")
1016 ctx.local_samdb.transaction_cancel()
1019 ctx.local_samdb.transaction_commit()
1021 # A large replication may have caused our LDB connection to the
1022 # remote DC to timeout, so check the connection is still alive
1023 ctx.refresh_ldb_connection()
1025 def refresh_ldb_connection(ctx):
1027 # query the rootDSE to check the connection
1028 ctx.samdb.search(scope=ldb.SCOPE_BASE, attrs=[])
1029 except ldb.LdbError as e:
1030 (enum, estr) = e.args
1032 # if the connection was disconnected, then reconnect
1033 if (enum == ldb.ERR_OPERATIONS_ERROR and
1034 'NT_STATUS_CONNECTION_DISCONNECTED' in estr):
1035 ctx.logger.warning("LDB connection disconnected. Reconnecting")
1036 ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
1037 session_info=system_session(),
1038 credentials=ctx.creds, lp=ctx.lp)
1040 raise DCJoinException(estr)
1042 def send_DsReplicaUpdateRefs(ctx, dn):
1043 r = drsuapi.DsReplicaUpdateRefsRequest1()
1044 r.naming_context = drsuapi.DsReplicaObjectIdentifier()
1045 r.naming_context.dn = str(dn)
1046 r.naming_context.guid = misc.GUID("00000000-0000-0000-0000-000000000000")
1047 r.naming_context.sid = security.dom_sid("S-0-0")
1048 r.dest_dsa_guid = ctx.ntds_guid
1049 r.dest_dsa_dns_name = "%s._msdcs.%s" % (str(ctx.ntds_guid), ctx.dnsforest)
1050 r.options = drsuapi.DRSUAPI_DRS_ADD_REF | drsuapi.DRSUAPI_DRS_DEL_REF
1052 r.options |= drsuapi.DRSUAPI_DRS_WRIT_REP
1054 if ctx.drsuapi is None:
1055 ctx.drsuapi_connect()
1057 ctx.drsuapi.DsReplicaUpdateRefs(ctx.drsuapi_handle, 1, r)
1059 def join_add_dns_records(ctx):
1060 """Remotely Add a DNS record to the target DC. We assume that if we
1061 replicate DNS that the server holds the DNS roles and can accept
1064 This avoids issues getting replication going after the DC
1065 first starts as the rest of the domain does not have to
1066 wait for samba_dnsupdate to run successfully.
1068 Specifically, we add the records implied by the DsReplicaUpdateRefs
1071 We do not just run samba_dnsupdate as we want to strictly
1072 operate against the DC we just joined:
1073 - We do not want to query another DNS server
1074 - We do not want to obtain a Kerberos ticket
1075 (as the KDC we select may not be the DC we just joined,
1076 and so may not be in sync with the password we just set)
1077 - We do not wish to set the _ldap records until we have started
1078 - We do not wish to use NTLM (the --use-samba-tool mode forces
1083 client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
1084 select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA |\
1085 dnsserver.DNS_RPC_VIEW_NO_CHILDREN
1087 zone = ctx.dnsdomain
1088 msdcs_zone = "_msdcs.%s" % ctx.dnsforest
1090 msdcs_cname = str(ctx.ntds_guid)
1091 cname_target = "%s.%s" % (name, zone)
1092 IPs = samba.interface_ips(ctx.lp, ctx.force_all_ips)
1094 ctx.logger.info("Adding %d remote DNS records for %s.%s" %
1095 (len(IPs), name, zone))
1097 binding_options = "sign"
1098 dns_conn = dnsserver.dnsserver("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
1103 sd_helper = sd_utils.SDUtils(ctx.samdb)
1105 change_owner_sd = security.descriptor()
1106 change_owner_sd.owner_sid = ctx.new_dc_account_sid
1107 change_owner_sd.group_sid = security.dom_sid("%s-%d" %
1109 security.DOMAIN_RID_DCS))
1111 # TODO: Remove any old records from the primary DNS name
1114 = dns_conn.DnssrvEnumRecords2(client_version,
1124 except WERRORError as e:
1125 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1131 for record in rec.records:
1132 if record.wType == dnsp.DNS_TYPE_A or \
1133 record.wType == dnsp.DNS_TYPE_AAAA:
1135 del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1136 del_rec_buf.rec = record
1138 dns_conn.DnssrvUpdateRecord2(client_version,
1145 except WERRORError as e:
1146 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1152 if IP.find(':') != -1:
1153 ctx.logger.info("Adding DNS AAAA record %s.%s for IPv6 IP: %s"
1155 rec = AAAARecord(IP)
1157 ctx.logger.info("Adding DNS A record %s.%s for IPv4 IP: %s"
1162 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1163 add_rec_buf.rec = rec
1164 dns_conn.DnssrvUpdateRecord2(client_version,
1173 domaindns_zone_dn = ldb.Dn(ctx.samdb, ctx.domaindns_zone)
1174 (ctx.dns_a_dn, ldap_record) \
1175 = ctx.samdb.dns_lookup("%s.%s" % (name, zone),
1176 dns_partition=domaindns_zone_dn)
1178 # Make the DC own the DNS record, not the administrator
1179 sd_helper.modify_sd_on_dn(ctx.dns_a_dn, change_owner_sd,
1180 controls=["sd_flags:1:%d"
1181 % (security.SECINFO_OWNER
1182 | security.SECINFO_GROUP)])
1185 ctx.logger.info("Adding DNS CNAME record %s.%s for %s"
1186 % (msdcs_cname, msdcs_zone, cname_target))
1188 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1189 rec = CNameRecord(cname_target)
1190 add_rec_buf.rec = rec
1191 dns_conn.DnssrvUpdateRecord2(client_version,
1199 forestdns_zone_dn = ldb.Dn(ctx.samdb, ctx.forestdns_zone)
1200 (ctx.dns_cname_dn, ldap_record) \
1201 = ctx.samdb.dns_lookup("%s.%s" % (msdcs_cname, msdcs_zone),
1202 dns_partition=forestdns_zone_dn)
1204 # Make the DC own the DNS record, not the administrator
1205 sd_helper.modify_sd_on_dn(ctx.dns_cname_dn, change_owner_sd,
1206 controls=["sd_flags:1:%d"
1207 % (security.SECINFO_OWNER
1208 | security.SECINFO_GROUP)])
1210 ctx.logger.info("All other DNS records (like _ldap SRV records) " +
1211 "will be created samba_dnsupdate on first startup")
1213 def join_replicate_new_dns_records(ctx):
1214 for nc in (ctx.domaindns_zone, ctx.forestdns_zone):
1215 if nc in ctx.nc_list:
1216 ctx.logger.info("Replicating new DNS records in %s" % (str(nc)))
1217 ctx.repl.replicate(nc, ctx.source_dsa_invocation_id,
1218 ctx.ntds_guid, rodc=ctx.RODC,
1219 replica_flags=ctx.replica_flags,
1222 def join_finalise(ctx):
1223 """Finalise the join, mark us synchronised and setup secrets db."""
1225 # FIXME we shouldn't do this in all cases
1227 # If for some reasons we joined in another site than the one of
1228 # DC we just replicated from then we don't need to send the updatereplicateref
1229 # as replication between sites is time based and on the initiative of the
1231 ctx.logger.info("Sending DsReplicaUpdateRefs for all the replicated partitions")
1232 for nc in ctx.nc_list:
1233 ctx.send_DsReplicaUpdateRefs(nc)
1236 print("Setting RODC invocationId")
1237 ctx.local_samdb.set_invocation_id(str(ctx.invocation_id))
1238 ctx.local_samdb.set_opaque_integer("domainFunctionality",
1239 ctx.behavior_version)
1241 m.dn = ldb.Dn(ctx.local_samdb, "%s" % ctx.ntds_dn)
1242 m["invocationId"] = ldb.MessageElement(ndr_pack(ctx.invocation_id),
1243 ldb.FLAG_MOD_REPLACE,
1245 ctx.local_samdb.modify(m)
1247 # Note: as RODC the invocationId is only stored
1248 # on the RODC itself, the other DCs never see it.
1250 # Thats is why we fix up the replPropertyMetaData stamp
1251 # for the 'invocationId' attribute, we need to change
1252 # the 'version' to '0', this is what windows 2008r2 does as RODC
1254 # This means if the object on a RWDC ever gets a invocationId
1255 # attribute, it will have version '1' (or higher), which will
1256 # will overwrite the RODC local value.
1257 ctx.local_samdb.set_attribute_replmetadata_version(m.dn,
1261 ctx.logger.info("Setting isSynchronized and dsServiceName")
1263 m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
1264 m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isSynchronized")
1266 guid = ctx.ntds_guid
1267 m["dsServiceName"] = ldb.MessageElement("<GUID=%s>" % str(guid),
1268 ldb.FLAG_MOD_REPLACE, "dsServiceName")
1269 ctx.local_samdb.modify(m)
1274 secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
1276 ctx.logger.info("Setting up secrets database")
1277 secretsdb_self_join(secrets_ldb, domain=ctx.domain_name,
1279 dnsdomain=ctx.dnsdomain,
1280 netbiosname=ctx.myname,
1281 domainsid=ctx.domsid,
1282 machinepass=ctx.acct_pass,
1283 secure_channel_type=ctx.secure_channel_type,
1284 key_version_number=ctx.key_version_number)
1286 if ctx.dns_backend.startswith("BIND9_"):
1287 setup_bind9_dns(ctx.local_samdb, secrets_ldb,
1288 ctx.names, ctx.paths, ctx.lp, ctx.logger,
1289 dns_backend=ctx.dns_backend,
1290 dnspass=ctx.dnspass, os_level=ctx.behavior_version,
1291 targetdir=ctx.targetdir,
1292 key_version_number=ctx.dns_key_version_number)
1294 def join_setup_trusts(ctx):
1295 """provision the local SAM."""
1297 print("Setup domain trusts with server %s" % ctx.server)
1298 binding_options = "" # why doesn't signing work here? w2k8r2 claims no session key
1299 lsaconn = lsa.lsarpc("ncacn_np:%s[%s]" % (ctx.server, binding_options),
1302 objectAttr = lsa.ObjectAttribute()
1303 objectAttr.sec_qos = lsa.QosInfo()
1305 pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
1306 objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
1308 info = lsa.TrustDomainInfoInfoEx()
1309 info.domain_name.string = ctx.dnsdomain
1310 info.netbios_name.string = ctx.domain_name
1311 info.sid = ctx.domsid
1312 info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND
1313 info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
1314 info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST
1317 oldname = lsa.String()
1318 oldname.string = ctx.dnsdomain
1319 oldinfo = lsaconn.QueryTrustedDomainInfoByName(pol_handle, oldname,
1320 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
1321 print("Removing old trust record for %s (SID %s)" % (ctx.dnsdomain, oldinfo.info_ex.sid))
1322 lsaconn.DeleteTrustedDomain(pol_handle, oldinfo.info_ex.sid)
1323 except RuntimeError:
1326 password_blob = string_to_byte_array(ctx.trustdom_pass.encode('utf-16-le'))
1328 clear_value = drsblobs.AuthInfoClear()
1329 clear_value.size = len(password_blob)
1330 clear_value.password = password_blob
1332 clear_authentication_information = drsblobs.AuthenticationInformation()
1333 clear_authentication_information.LastUpdateTime = samba.unix2nttime(int(time.time()))
1334 clear_authentication_information.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
1335 clear_authentication_information.AuthInfo = clear_value
1337 authentication_information_array = drsblobs.AuthenticationInformationArray()
1338 authentication_information_array.count = 1
1339 authentication_information_array.array = [clear_authentication_information]
1341 outgoing = drsblobs.trustAuthInOutBlob()
1343 outgoing.current = authentication_information_array
1345 trustpass = drsblobs.trustDomainPasswords()
1346 confounder = [3] * 512
1348 for i in range(512):
1349 confounder[i] = random.randint(0, 255)
1351 trustpass.confounder = confounder
1353 trustpass.outgoing = outgoing
1354 trustpass.incoming = outgoing
1356 trustpass_blob = ndr_pack(trustpass)
1358 encrypted_trustpass = arcfour_encrypt(lsaconn.session_key, trustpass_blob)
1360 auth_blob = lsa.DATA_BUF2()
1361 auth_blob.size = len(encrypted_trustpass)
1362 auth_blob.data = string_to_byte_array(encrypted_trustpass)
1364 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
1365 auth_info.auth_blob = auth_blob
1367 trustdom_handle = lsaconn.CreateTrustedDomainEx2(pol_handle,
1370 security.SEC_STD_DELETE)
1373 "dn": "cn=%s,cn=system,%s" % (ctx.dnsforest, ctx.base_dn),
1374 "objectclass": "trustedDomain",
1375 "trustType": str(info.trust_type),
1376 "trustAttributes": str(info.trust_attributes),
1377 "trustDirection": str(info.trust_direction),
1378 "flatname": ctx.forest_domain_name,
1379 "trustPartner": ctx.dnsforest,
1380 "trustAuthIncoming": ndr_pack(outgoing),
1381 "trustAuthOutgoing": ndr_pack(outgoing),
1382 "securityIdentifier": ndr_pack(ctx.forestsid)
1384 ctx.local_samdb.add(rec)
1387 "dn": "cn=%s$,cn=users,%s" % (ctx.forest_domain_name, ctx.base_dn),
1388 "objectclass": "user",
1389 "userAccountControl": str(samba.dsdb.UF_INTERDOMAIN_TRUST_ACCOUNT),
1390 "clearTextPassword": ctx.trustdom_pass.encode('utf-16-le'),
1391 "samAccountName": "%s$" % ctx.forest_domain_name
1393 ctx.local_samdb.add(rec)
1395 def build_nc_lists(ctx):
1396 # nc_list is the list of naming context (NC) for which we will
1397 # replicate in and send a updateRef command to the partner DC
1399 # full_nc_list is the list of naming context (NC) we hold
1400 # read/write copies of. These are not subsets of each other.
1401 ctx.nc_list = [ctx.config_dn, ctx.schema_dn]
1402 ctx.full_nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn]
1404 if ctx.subdomain and ctx.dns_backend != "NONE":
1405 ctx.full_nc_list += [ctx.domaindns_zone]
1407 elif not ctx.subdomain:
1408 ctx.nc_list += [ctx.base_dn]
1410 if ctx.dns_backend != "NONE":
1411 ctx.nc_list += [ctx.domaindns_zone]
1412 ctx.nc_list += [ctx.forestdns_zone]
1413 ctx.full_nc_list += [ctx.domaindns_zone]
1414 ctx.full_nc_list += [ctx.forestdns_zone]
1417 ctx.build_nc_lists()
1419 if ctx.promote_existing:
1420 ctx.promote_possible()
1422 ctx.cleanup_old_join()
1425 ctx.join_add_objects()
1426 ctx.join_provision()
1427 ctx.join_replicate()
1429 ctx.join_add_objects2()
1430 ctx.join_provision_own_domain()
1431 ctx.join_setup_trusts()
1433 if ctx.dns_backend != "NONE":
1434 ctx.join_add_dns_records()
1435 ctx.join_replicate_new_dns_records()
1440 print("Join failed - cleaning up")
1444 # cleanup the failed join (checking we still have a live LDB
1445 # connection to the remote DC first)
1446 ctx.refresh_ldb_connection()
1447 ctx.cleanup_old_join()
1451 def join_RODC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
1452 targetdir=None, domain=None, domain_critical_only=False,
1453 machinepass=None, use_ntvfs=False, dns_backend=None,
1454 promote_existing=False, plaintext_secrets=False,
1455 backend_store=None):
1456 """Join as a RODC."""
1458 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1459 targetdir, domain, machinepass, use_ntvfs, dns_backend,
1460 promote_existing, plaintext_secrets,
1461 backend_store=backend_store)
1463 lp.set("workgroup", ctx.domain_name)
1464 logger.info("workgroup is %s" % ctx.domain_name)
1466 lp.set("realm", ctx.realm)
1467 logger.info("realm is %s" % ctx.realm)
1469 ctx.krbtgt_dn = "CN=krbtgt_%s,CN=Users,%s" % (ctx.myname, ctx.base_dn)
1471 # setup some defaults for accounts that should be replicated to this RODC
1472 ctx.never_reveal_sid = [
1473 "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_DENY),
1474 "<SID=%s>" % security.SID_BUILTIN_ADMINISTRATORS,
1475 "<SID=%s>" % security.SID_BUILTIN_SERVER_OPERATORS,
1476 "<SID=%s>" % security.SID_BUILTIN_BACKUP_OPERATORS,
1477 "<SID=%s>" % security.SID_BUILTIN_ACCOUNT_OPERATORS]
1478 ctx.reveal_sid = "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_ALLOW)
1480 mysid = ctx.get_mysid()
1481 admin_dn = "<SID=%s>" % mysid
1482 ctx.managedby = admin_dn
1484 ctx.userAccountControl = (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT |
1485 samba.dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION |
1486 samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT)
1488 ctx.SPNs.extend(["RestrictedKrbHost/%s" % ctx.myname,
1489 "RestrictedKrbHost/%s" % ctx.dnshostname])
1491 ctx.connection_dn = "CN=RODC Connection (FRS),%s" % ctx.ntds_dn
1492 ctx.secure_channel_type = misc.SEC_CHAN_RODC
1494 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING |
1495 drsuapi.DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP)
1496 ctx.domain_replica_flags = ctx.replica_flags
1497 if domain_critical_only:
1498 ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
1502 logger.info("Joined domain %s (SID %s) as an RODC" % (ctx.domain_name, ctx.domsid))
1505 def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
1506 targetdir=None, domain=None, domain_critical_only=False,
1507 machinepass=None, use_ntvfs=False, dns_backend=None,
1508 promote_existing=False, plaintext_secrets=False,
1509 backend_store=None):
1511 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1512 targetdir, domain, machinepass, use_ntvfs, dns_backend,
1513 promote_existing, plaintext_secrets,
1514 backend_store=backend_store)
1516 lp.set("workgroup", ctx.domain_name)
1517 logger.info("workgroup is %s" % ctx.domain_name)
1519 lp.set("realm", ctx.realm)
1520 logger.info("realm is %s" % ctx.realm)
1522 ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
1524 ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
1525 ctx.secure_channel_type = misc.SEC_CHAN_BDC
1527 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1528 drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1529 ctx.domain_replica_flags = ctx.replica_flags
1530 if domain_critical_only:
1531 ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
1534 logger.info("Joined domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))
1537 def join_clone(logger=None, server=None, creds=None, lp=None,
1538 targetdir=None, domain=None, include_secrets=False,
1539 dns_backend="NONE", backend_store=None):
1540 """Creates a local clone of a remote DC."""
1541 ctx = DCCloneContext(logger, server, creds, lp, targetdir=targetdir,
1542 domain=domain, dns_backend=dns_backend,
1543 include_secrets=include_secrets,
1544 backend_store=backend_store)
1546 lp.set("workgroup", ctx.domain_name)
1547 logger.info("workgroup is %s" % ctx.domain_name)
1549 lp.set("realm", ctx.realm)
1550 logger.info("realm is %s" % ctx.realm)
1553 logger.info("Cloned domain %s (SID %s)" % (ctx.domain_name, ctx.domsid))
1557 def join_subdomain(logger=None, server=None, creds=None, lp=None, site=None,
1558 netbios_name=None, targetdir=None, parent_domain=None, dnsdomain=None,
1559 netbios_domain=None, machinepass=None, adminpass=None, use_ntvfs=False,
1560 dns_backend=None, plaintext_secrets=False,
1561 backend_store=None):
1563 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1564 targetdir, parent_domain, machinepass, use_ntvfs,
1565 dns_backend, plaintext_secrets,
1566 backend_store=backend_store)
1567 ctx.subdomain = True
1568 if adminpass is None:
1569 ctx.adminpass = samba.generate_random_password(12, 32)
1571 ctx.adminpass = adminpass
1572 ctx.parent_domain_name = ctx.domain_name
1573 ctx.domain_name = netbios_domain
1574 ctx.realm = dnsdomain
1575 ctx.parent_dnsdomain = ctx.dnsdomain
1576 ctx.parent_partition_dn = ctx.get_parent_partition_dn()
1577 ctx.dnsdomain = dnsdomain
1578 ctx.partition_dn = "CN=%s,CN=Partitions,%s" % (ctx.domain_name, ctx.config_dn)
1579 ctx.naming_master = ctx.get_naming_master()
1580 if ctx.naming_master != ctx.server:
1581 logger.info("Reconnecting to naming master %s" % ctx.naming_master)
1582 ctx.server = ctx.naming_master
1583 ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
1584 session_info=system_session(),
1585 credentials=ctx.creds, lp=ctx.lp)
1586 res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=['dnsHostName'],
1588 ctx.server = res[0]["dnsHostName"]
1589 logger.info("DNS name of new naming master is %s" % ctx.server)
1591 ctx.base_dn = samba.dn_from_dns_name(dnsdomain)
1592 ctx.forestsid = ctx.domsid
1593 ctx.domsid = security.random_sid()
1595 ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain)
1596 # Windows uses 240 bytes as UTF16 so we do
1597 ctx.trustdom_pass = samba.generate_random_machine_password(120, 120)
1599 ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
1601 ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
1602 ctx.secure_channel_type = misc.SEC_CHAN_BDC
1604 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1605 drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1606 ctx.domain_replica_flags = ctx.replica_flags
1609 ctx.logger.info("Created domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))
1612 class DCCloneContext(DCJoinContext):
1613 """Clones a remote DC."""
1615 def __init__(ctx, logger=None, server=None, creds=None, lp=None,
1616 targetdir=None, domain=None, dns_backend=None,
1617 include_secrets=False, backend_store=None):
1618 super(DCCloneContext, ctx).__init__(logger, server, creds, lp,
1619 targetdir=targetdir, domain=domain,
1620 dns_backend=dns_backend,
1621 backend_store=backend_store)
1623 # As we don't want to create or delete these DNs, we set them to None
1624 ctx.server_dn = None
1627 ctx.myname = ctx.server.split('.')[0]
1628 ctx.ntds_guid = None
1629 ctx.rid_manager_dn = None
1632 ctx.remote_dc_ntds_guid = ctx.samdb.get_ntds_GUID()
1634 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1635 drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1636 if not include_secrets:
1637 ctx.replica_flags |= drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
1638 ctx.domain_replica_flags = ctx.replica_flags
1640 def join_finalise(ctx):
1641 ctx.logger.info("Setting isSynchronized and dsServiceName")
1643 m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
1644 m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE,
1647 # We want to appear to be the server we just cloned
1648 guid = ctx.remote_dc_ntds_guid
1649 m["dsServiceName"] = ldb.MessageElement("<GUID=%s>" % str(guid),
1650 ldb.FLAG_MOD_REPLACE,
1652 ctx.local_samdb.modify(m)
1655 ctx.build_nc_lists()
1657 # When cloning a DC, we just want to provision a DC locally, then
1658 # grab the remote DC's entire DB via DRS replication
1659 ctx.join_provision()
1660 ctx.join_replicate()
1664 # Used to create a renamed backup of a DC. Renaming the domain means that the
1665 # cloned/backup DC can be started without interfering with the production DC.
1666 class DCCloneAndRenameContext(DCCloneContext):
1667 """Clones a remote DC, renaming the domain along the way."""
1669 def __init__(ctx, new_base_dn, new_domain_name, new_realm, logger=None,
1670 server=None, creds=None, lp=None, targetdir=None, domain=None,
1671 dns_backend=None, include_secrets=True, backend_store=None):
1672 super(DCCloneAndRenameContext, ctx).__init__(logger, server, creds, lp,
1673 targetdir=targetdir,
1675 dns_backend=dns_backend,
1676 include_secrets=include_secrets,
1677 backend_store=backend_store)
1678 # store the new DN (etc) that we want the cloned DB to use
1679 ctx.new_base_dn = new_base_dn
1680 ctx.new_domain_name = new_domain_name
1681 ctx.new_realm = new_realm
1683 def create_replicator(ctx, repl_creds, binding_options):
1684 """Creates a new DRS object for managing replications"""
1686 # We want to rename all the domain objects, and the simplest way to do
1687 # this is during replication. This is because the base DN of the top-
1688 # level replicated object will flow through to all the objects below it
1689 binding_str = "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options)
1690 return drs_utils.drs_ReplicateRenamer(binding_str, ctx.lp, repl_creds,
1693 ctx.base_dn, ctx.new_base_dn)
1695 def create_non_global_lp(ctx, global_lp):
1696 '''Creates a non-global LoadParm based on the global LP's settings'''
1698 # the samba code shares a global LoadParm by default. Here we create a
1699 # new LoadParm that retains the global settings, but any changes we
1700 # make to it won't automatically affect the rest of the samba code.
1701 # The easiest way to do this is to dump the global settings to a
1702 # temporary smb.conf file, and then load the temp file into a new
1703 # non-global LoadParm
1704 fd, tmp_file = tempfile.mkstemp()
1705 global_lp.dump(False, tmp_file)
1706 local_lp = samba.param.LoadParm(filename_for_non_global_lp=tmp_file)
1710 def rename_dn(ctx, dn_str):
1711 '''Uses string substitution to replace the base DN'''
1712 old_base_dn = ctx.base_dn
1713 return re.sub('%s$' % old_base_dn, ctx.new_base_dn, dn_str)
1715 # we want to override the normal DCCloneContext's join_provision() so that
1716 # use the new domain DNs during the provision. We do this because:
1717 # - it sets up smb.conf/secrets.ldb with the new realm/workgroup values
1718 # - it sets up a default SAM DB that uses the new Schema DNs (without which
1719 # we couldn't apply the renamed DRS objects during replication)
1720 def join_provision(ctx):
1721 """Provision the local (renamed) SAM."""
1723 print("Provisioning the new (renamed) domain...")
1725 # the provision() calls make_smbconf() which uses lp.dump()/lp.load()
1726 # to create a new smb.conf. By default, it uses the global LoadParm to
1727 # do this, and so it would overwrite the realm/domain values globally.
1728 # We still need the global LoadParm to retain the old domain's details,
1729 # so we can connect to (and clone) the existing DC.
1730 # So, copy the global settings into a non-global LoadParm, which we can
1731 # then pass into provision(). This generates a new smb.conf correctly,
1732 # without overwriting the global realm/domain values just yet.
1733 non_global_lp = ctx.create_non_global_lp(ctx.lp)
1735 # do the provision with the new/renamed domain DN values
1736 presult = provision(ctx.logger, system_session(),
1737 targetdir=ctx.targetdir, samdb_fill=FILL_DRS,
1738 realm=ctx.new_realm, lp=non_global_lp,
1739 rootdn=ctx.rename_dn(ctx.root_dn), domaindn=ctx.new_base_dn,
1740 schemadn=ctx.rename_dn(ctx.schema_dn),
1741 configdn=ctx.rename_dn(ctx.config_dn),
1742 domain=ctx.new_domain_name, domainsid=ctx.domsid,
1743 serverrole="active directory domain controller",
1744 dns_backend=ctx.dns_backend,
1745 backend_store=ctx.backend_store)
1747 print("Provision OK for renamed domain DN %s" % presult.domaindn)
1748 ctx.local_samdb = presult.samdb
1749 ctx.paths = presult.paths