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
29 from samba.ndr import ndr_pack, ndr_unpack
30 from samba.dcerpc import security, drsuapi, misc, nbt, lsa, drsblobs, dnsserver, dnsp
31 from samba.dsdb import DS_DOMAIN_FUNCTION_2003
32 from samba.credentials import Credentials, DONT_USE_KERBEROS
33 from samba.provision import (secretsdb_self_join, provision, provision_fill,
34 FILL_DRS, FILL_SUBDOMAIN, DEFAULTSITE)
35 from samba.provision.common import setup_path
36 from samba.schema import Schema
37 from samba import descriptor
38 from samba.net import Net
39 from samba.provision.sambadns import setup_bind9_dns
40 from samba import read_and_sub_file
41 from samba import werror
42 from base64 import b64encode
43 from samba import WERRORError, NTSTATUSError
44 from samba.dnsserver import ARecord, AAAARecord, PTRRecord, CNameRecord, NSRecord, MXRecord, SOARecord, SRVRecord, TXTRecord
45 from samba import sd_utils
53 from samba.compat import text_type
54 from samba.compat import get_string
57 class DCJoinException(Exception):
59 def __init__(self, msg):
60 super(DCJoinException, self).__init__("Can't join, error: %s" % msg)
63 class DCJoinContext(object):
64 """Perform a DC join."""
66 def __init__(ctx, logger=None, server=None, creds=None, lp=None, site=None,
67 netbios_name=None, targetdir=None, domain=None,
68 machinepass=None, use_ntvfs=False, dns_backend=None,
69 promote_existing=False, plaintext_secrets=False,
70 backend_store=None, forced_local_samdb=None):
76 ctx.targetdir = targetdir
77 ctx.use_ntvfs = use_ntvfs
78 ctx.plaintext_secrets = plaintext_secrets
79 ctx.backend_store = backend_store
81 ctx.promote_existing = promote_existing
82 ctx.promote_from_dn = None
87 ctx.creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
88 ctx.net = Net(creds=ctx.creds, lp=ctx.lp)
91 ctx.forced_local_samdb = forced_local_samdb
93 if forced_local_samdb:
94 ctx.samdb = forced_local_samdb
95 ctx.server = ctx.samdb.url
98 # work out the DC's site (if not already specified)
100 ctx.site = ctx.find_dc_site(ctx.server)
102 # work out the Primary DC for the domain (as well as an
103 # appropriate site for the new DC)
104 ctx.logger.info("Finding a writeable DC for domain '%s'" % domain)
105 ctx.server = ctx.find_dc(domain)
106 ctx.logger.info("Found DC %s" % ctx.server)
107 ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
108 session_info=system_session(),
109 credentials=ctx.creds, lp=ctx.lp)
112 ctx.site = DEFAULTSITE
115 ctx.samdb.search(scope=ldb.SCOPE_BASE, attrs=[])
116 except ldb.LdbError as e:
117 (enum, estr) = e.args
118 raise DCJoinException(estr)
120 ctx.base_dn = str(ctx.samdb.get_default_basedn())
121 ctx.root_dn = str(ctx.samdb.get_root_basedn())
122 ctx.schema_dn = str(ctx.samdb.get_schema_basedn())
123 ctx.config_dn = str(ctx.samdb.get_config_basedn())
124 ctx.domsid = security.dom_sid(ctx.samdb.get_domain_sid())
125 ctx.forestsid = ctx.domsid
126 ctx.domain_name = ctx.get_domain_name()
127 ctx.forest_domain_name = ctx.get_forest_domain_name()
128 ctx.invocation_id = misc.GUID(str(uuid.uuid4()))
130 ctx.dc_ntds_dn = ctx.samdb.get_dsServiceName()
131 ctx.dc_dnsHostName = ctx.get_dnsHostName()
132 ctx.behavior_version = ctx.get_behavior_version()
134 if machinepass is not None:
135 ctx.acct_pass = machinepass
137 ctx.acct_pass = samba.generate_random_machine_password(128, 255)
139 ctx.dnsdomain = ctx.samdb.domain_dns_name()
141 # the following are all dependent on the new DC's netbios_name (which
142 # we expect to always be specified, except when cloning a DC)
144 # work out the DNs of all the objects we will be adding
145 ctx.myname = netbios_name
146 ctx.samname = "%s$" % ctx.myname
147 ctx.server_dn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (ctx.myname, ctx.site, ctx.config_dn)
148 ctx.ntds_dn = "CN=NTDS Settings,%s" % ctx.server_dn
149 ctx.acct_dn = "CN=%s,OU=Domain Controllers,%s" % (ctx.myname, ctx.base_dn)
150 ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain)
151 ctx.dnsforest = ctx.samdb.forest_dns_name()
153 topology_base = "CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,CN=System,%s" % ctx.base_dn
154 if ctx.dn_exists(topology_base):
155 ctx.topology_dn = "CN=%s,%s" % (ctx.myname, topology_base)
157 ctx.topology_dn = None
159 ctx.SPNs = ["HOST/%s" % ctx.myname,
160 "HOST/%s" % ctx.dnshostname,
161 "GC/%s/%s" % (ctx.dnshostname, ctx.dnsforest)]
163 res_rid_manager = ctx.samdb.search(scope=ldb.SCOPE_BASE,
164 attrs=["rIDManagerReference"],
167 ctx.rid_manager_dn = res_rid_manager[0]["rIDManagerReference"][0]
169 ctx.domaindns_zone = 'DC=DomainDnsZones,%s' % ctx.base_dn
170 ctx.forestdns_zone = 'DC=ForestDnsZones,%s' % ctx.root_dn
172 expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.domaindns_zone)
173 res_domaindns = ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
175 base=ctx.samdb.get_partitions_dn(),
177 if dns_backend is None:
178 ctx.dns_backend = "NONE"
180 if len(res_domaindns) == 0:
181 ctx.dns_backend = "NONE"
182 print("NO DNS zone information found in source domain, not replicating DNS")
184 ctx.dns_backend = dns_backend
186 ctx.realm = ctx.dnsdomain
190 ctx.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC |
191 drsuapi.DRSUAPI_DRS_PER_SYNC |
192 drsuapi.DRSUAPI_DRS_GET_ANC |
193 drsuapi.DRSUAPI_DRS_GET_NC_SIZE |
194 drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
196 # these elements are optional
197 ctx.never_reveal_sid = None
198 ctx.reveal_sid = None
199 ctx.connection_dn = None
204 ctx.subdomain = False
206 ctx.partition_dn = None
209 ctx.dns_cname_dn = None
211 # Do not normally register 127. addresses but allow override for selftest
212 ctx.force_all_ips = False
214 def del_noerror(ctx, dn, recursive=False):
217 res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_ONELEVEL, attrs=["dn"])
221 ctx.del_noerror(r.dn, recursive=True)
224 print("Deleted %s" % dn)
228 def cleanup_old_accounts(ctx, force=False):
229 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
230 expression='sAMAccountName=%s' % ldb.binary_encode(ctx.samname),
231 attrs=["msDS-krbTgtLink", "objectSID"])
236 creds = Credentials()
239 creds.set_machine_account(ctx.lp)
240 creds.set_kerberos_state(ctx.creds.get_kerberos_state())
241 machine_samdb = SamDB(url="ldap://%s" % ctx.server,
242 session_info=system_session(),
243 credentials=creds, lp=ctx.lp)
247 token_res = machine_samdb.search(scope=ldb.SCOPE_BASE, base="", attrs=["tokenGroups"])
248 if token_res[0]["tokenGroups"][0] \
249 == res[0]["objectSID"][0]:
250 raise DCJoinException("Not removing account %s which "
251 "looks like a Samba DC account "
252 "matching the password we already have. "
253 "To override, remove secrets.ldb and secrets.tdb"
256 ctx.del_noerror(res[0].dn, recursive=True)
258 if "msDS-Krbtgtlink" in res[0]:
259 new_krbtgt_dn = res[0]["msDS-Krbtgtlink"][0]
260 ctx.del_noerror(ctx.new_krbtgt_dn)
262 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
263 expression='(&(sAMAccountName=%s)(servicePrincipalName=%s))' %
264 (ldb.binary_encode("dns-%s" % ctx.myname),
265 ldb.binary_encode("dns/%s" % ctx.dnshostname)),
268 ctx.del_noerror(res[0].dn, recursive=True)
270 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
271 expression='(sAMAccountName=%s)' % ldb.binary_encode("dns-%s" % ctx.myname),
274 raise DCJoinException("Not removing account %s which looks like "
275 "a Samba DNS service account but does not "
276 "have servicePrincipalName=%s" %
277 (ldb.binary_encode("dns-%s" % ctx.myname),
278 ldb.binary_encode("dns/%s" % ctx.dnshostname)))
280 def cleanup_old_join(ctx, force=False):
281 """Remove any DNs from a previous join."""
282 # find the krbtgt link
283 if not ctx.subdomain:
284 ctx.cleanup_old_accounts(force=force)
286 if ctx.connection_dn is not None:
287 ctx.del_noerror(ctx.connection_dn)
288 if ctx.krbtgt_dn is not None:
289 ctx.del_noerror(ctx.krbtgt_dn)
290 ctx.del_noerror(ctx.ntds_dn)
291 ctx.del_noerror(ctx.server_dn, recursive=True)
293 ctx.del_noerror(ctx.topology_dn)
295 ctx.del_noerror(ctx.partition_dn)
298 binding_options = "sign"
299 lsaconn = lsa.lsarpc("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
302 objectAttr = lsa.ObjectAttribute()
303 objectAttr.sec_qos = lsa.QosInfo()
305 pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
306 objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
309 name.string = ctx.realm
310 info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
312 lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
315 name.string = ctx.forest_domain_name
316 info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
318 lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
321 ctx.del_noerror(ctx.dns_a_dn)
324 ctx.del_noerror(ctx.dns_cname_dn)
326 def promote_possible(ctx):
327 """confirm that the account is just a bare NT4 BDC or a member server, so can be safely promoted"""
329 # This shouldn't happen
330 raise Exception("Can not promote into a subdomain")
332 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
333 expression='sAMAccountName=%s' % ldb.binary_encode(ctx.samname),
334 attrs=["msDS-krbTgtLink", "userAccountControl", "serverReferenceBL", "rIDSetReferences"])
336 raise Exception("Could not find domain member account '%s' to promote to a DC, use 'samba-tool domain join' instead'" % ctx.samname)
337 if "msDS-krbTgtLink" in res[0] or "serverReferenceBL" in res[0] or "rIDSetReferences" in res[0]:
338 raise Exception("Account '%s' appears to be an active DC, use 'samba-tool domain join' if you must re-create this account" % ctx.samname)
339 if (int(res[0]["userAccountControl"][0]) & (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT |
340 samba.dsdb.UF_SERVER_TRUST_ACCOUNT) == 0):
341 raise Exception("Account %s is not a domain member or a bare NT4 BDC, use 'samba-tool domain join' instead'" % ctx.samname)
343 ctx.promote_from_dn = res[0].dn
345 def find_dc(ctx, domain):
346 """find a writeable DC for the given domain"""
348 ctx.cldap_ret = ctx.net.finddc(domain=domain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS | nbt.NBT_SERVER_WRITABLE)
349 except NTSTATUSError as error:
350 raise Exception("Failed to find a writeable DC for domain '%s': %s" %
353 raise Exception("Failed to find a writeable DC for domain '%s'" % domain)
354 if ctx.cldap_ret.client_site is not None and ctx.cldap_ret.client_site != "":
355 ctx.site = ctx.cldap_ret.client_site
356 return ctx.cldap_ret.pdc_dns_name
358 def find_dc_site(ctx, server):
360 cldap_ret = ctx.net.finddc(address=server,
361 flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
362 if cldap_ret.client_site is not None and cldap_ret.client_site != "":
363 site = cldap_ret.client_site
366 def get_behavior_version(ctx):
367 res = ctx.samdb.search(base=ctx.base_dn, scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
368 if "msDS-Behavior-Version" in res[0]:
369 return int(res[0]["msDS-Behavior-Version"][0])
371 return samba.dsdb.DS_DOMAIN_FUNCTION_2000
373 def get_dnsHostName(ctx):
374 res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dnsHostName"])
375 return str(res[0]["dnsHostName"][0])
377 def get_domain_name(ctx):
378 '''get netbios name of the domain from the partitions record'''
379 partitions_dn = ctx.samdb.get_partitions_dn()
380 res = ctx.samdb.search(base=partitions_dn, scope=ldb.SCOPE_ONELEVEL, attrs=["nETBIOSName"],
381 expression='ncName=%s' % ldb.binary_encode(str(ctx.samdb.get_default_basedn())))
382 return str(res[0]["nETBIOSName"][0])
384 def get_forest_domain_name(ctx):
385 '''get netbios name of the domain from the partitions record'''
386 partitions_dn = ctx.samdb.get_partitions_dn()
387 res = ctx.samdb.search(base=partitions_dn, scope=ldb.SCOPE_ONELEVEL, attrs=["nETBIOSName"],
388 expression='ncName=%s' % ldb.binary_encode(str(ctx.samdb.get_root_basedn())))
389 return str(res[0]["nETBIOSName"][0])
391 def get_parent_partition_dn(ctx):
392 '''get the parent domain partition DN from parent DNS name'''
393 res = ctx.samdb.search(base=ctx.config_dn, attrs=[],
394 expression='(&(objectclass=crossRef)(dnsRoot=%s)(systemFlags:%s:=%u))' %
395 (ldb.binary_encode(ctx.parent_dnsdomain),
396 ldb.OID_COMPARATOR_AND, samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN))
397 return str(res[0].dn)
399 def get_naming_master(ctx):
400 '''get the parent domain partition DN from parent DNS name'''
401 res = ctx.samdb.search(base='CN=Partitions,%s' % ctx.config_dn, attrs=['fSMORoleOwner'],
402 scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"])
403 if 'fSMORoleOwner' not in res[0]:
404 raise DCJoinException("Can't find naming master on partition DN %s in %s" % (ctx.partition_dn, ctx.samdb.url))
406 master_guid = str(misc.GUID(ldb.Dn(ctx.samdb, res[0]['fSMORoleOwner'][0].decode('utf8')).get_extended_component('GUID')))
408 raise DCJoinException("Can't find GUID in naming master on partition DN %s" % res[0]['fSMORoleOwner'][0])
410 master_host = '%s._msdcs.%s' % (master_guid, ctx.dnsforest)
414 '''get the SID of the connected user. Only works with w2k8 and later,
415 so only used for RODC join'''
416 res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["tokenGroups"])
417 binsid = res[0]["tokenGroups"][0]
418 return get_string(ctx.samdb.schema_format_value("objectSID", binsid))
420 def dn_exists(ctx, dn):
421 '''check if a DN exists'''
423 res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[])
424 except ldb.LdbError as e5:
425 (enum, estr) = e5.args
426 if enum == ldb.ERR_NO_SUCH_OBJECT:
431 def add_krbtgt_account(ctx):
432 '''RODCs need a special krbtgt account'''
433 print("Adding %s" % ctx.krbtgt_dn)
436 "objectclass": "user",
437 "useraccountcontrol": str(samba.dsdb.UF_NORMAL_ACCOUNT |
438 samba.dsdb.UF_ACCOUNTDISABLE),
439 "showinadvancedviewonly": "TRUE",
440 "description": "krbtgt for %s" % ctx.samname}
441 ctx.samdb.add(rec, ["rodc_join:1:1"])
443 # now we need to search for the samAccountName attribute on the krbtgt DN,
444 # as this will have been magically set to the krbtgt number
445 res = ctx.samdb.search(base=ctx.krbtgt_dn, scope=ldb.SCOPE_BASE, attrs=["samAccountName"])
446 ctx.krbtgt_name = res[0]["samAccountName"][0]
448 print("Got krbtgt_name=%s" % ctx.krbtgt_name)
451 m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
452 m["msDS-krbTgtLink"] = ldb.MessageElement(ctx.krbtgt_dn,
453 ldb.FLAG_MOD_REPLACE, "msDS-krbTgtLink")
456 ctx.new_krbtgt_dn = "CN=%s,CN=Users,%s" % (ctx.krbtgt_name, ctx.base_dn)
457 print("Renaming %s to %s" % (ctx.krbtgt_dn, ctx.new_krbtgt_dn))
458 ctx.samdb.rename(ctx.krbtgt_dn, ctx.new_krbtgt_dn)
460 def drsuapi_connect(ctx):
461 '''make a DRSUAPI connection to the naming master'''
462 binding_options = "seal"
463 if ctx.lp.log_level() >= 9:
464 binding_options += ",print"
465 binding_string = "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options)
466 ctx.drsuapi = drsuapi.drsuapi(binding_string, ctx.lp, ctx.creds)
467 (ctx.drsuapi_handle, ctx.bind_supported_extensions) = drs_utils.drs_DsBind(ctx.drsuapi)
469 def create_tmp_samdb(ctx):
470 '''create a temporary samdb object for schema queries'''
471 ctx.tmp_schema = Schema(ctx.domsid,
472 schemadn=ctx.schema_dn)
473 ctx.tmp_samdb = SamDB(session_info=system_session(), url=None, auto_connect=False,
474 credentials=ctx.creds, lp=ctx.lp, global_schema=False,
476 ctx.tmp_samdb.set_schema(ctx.tmp_schema)
478 def build_DsReplicaAttribute(ctx, attrname, attrvalue):
479 '''build a DsReplicaAttributeCtr object'''
480 r = drsuapi.DsReplicaAttribute()
481 r.attid = ctx.tmp_samdb.get_attid_from_lDAPDisplayName(attrname)
484 def DsAddEntry(ctx, recs):
485 '''add a record via the DRSUAPI DsAddEntry call'''
486 if ctx.drsuapi is None:
487 ctx.drsuapi_connect()
488 if ctx.tmp_samdb is None:
489 ctx.create_tmp_samdb()
493 id = drsuapi.DsReplicaObjectIdentifier()
500 if not isinstance(rec[a], list):
504 v = [x.encode('utf8') if isinstance(x, text_type) else x for x in v]
505 rattr = ctx.tmp_samdb.dsdb_DsReplicaAttribute(ctx.tmp_samdb, a, v)
508 attribute_ctr = drsuapi.DsReplicaAttributeCtr()
509 attribute_ctr.num_attributes = len(attrs)
510 attribute_ctr.attributes = attrs
512 object = drsuapi.DsReplicaObject()
513 object.identifier = id
514 object.attribute_ctr = attribute_ctr
516 list_object = drsuapi.DsReplicaObjectListItem()
517 list_object.object = object
518 objects.append(list_object)
520 req2 = drsuapi.DsAddEntryRequest2()
521 req2.first_object = objects[0]
522 prev = req2.first_object
523 for o in objects[1:]:
527 (level, ctr) = ctx.drsuapi.DsAddEntry(ctx.drsuapi_handle, 2, req2)
529 if ctr.dir_err != drsuapi.DRSUAPI_DIRERR_OK:
530 print("DsAddEntry failed with dir_err %u" % ctr.dir_err)
531 raise RuntimeError("DsAddEntry failed")
532 if ctr.extended_err[0] != werror.WERR_SUCCESS:
533 print("DsAddEntry failed with status %s info %s" % (ctr.extended_err))
534 raise RuntimeError("DsAddEntry failed")
537 raise RuntimeError("expected err_ver 1, got %u" % ctr.err_ver)
538 if ctr.err_data.status[0] != werror.WERR_SUCCESS:
539 if ctr.err_data.info is None:
540 print("DsAddEntry failed with status %s, info omitted" % (ctr.err_data.status[1]))
542 print("DsAddEntry failed with status %s info %s" % (ctr.err_data.status[1],
543 ctr.err_data.info.extended_err))
544 raise RuntimeError("DsAddEntry failed")
545 if ctr.err_data.dir_err != drsuapi.DRSUAPI_DIRERR_OK:
546 print("DsAddEntry failed with dir_err %u" % ctr.err_data.dir_err)
547 raise RuntimeError("DsAddEntry failed")
551 def join_ntdsdsa_obj(ctx):
552 '''return the ntdsdsa object to add'''
554 print("Adding %s" % ctx.ntds_dn)
557 "objectclass": "nTDSDSA",
558 "systemFlags": str(samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
559 "dMDLocation": ctx.schema_dn}
561 nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn]
563 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
564 rec["msDS-Behavior-Version"] = str(samba.dsdb.DS_DOMAIN_FUNCTION_2008_R2)
566 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
567 rec["msDS-HasDomainNCs"] = ctx.base_dn
570 rec["objectCategory"] = "CN=NTDS-DSA-RO,%s" % ctx.schema_dn
571 rec["msDS-HasFullReplicaNCs"] = ctx.full_nc_list
572 rec["options"] = "37"
574 rec["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn
575 rec["HasMasterNCs"] = []
577 if nc in ctx.full_nc_list:
578 rec["HasMasterNCs"].append(nc)
579 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
580 rec["msDS-HasMasterNCs"] = ctx.full_nc_list
582 rec["invocationId"] = ndr_pack(ctx.invocation_id)
586 def join_add_ntdsdsa(ctx):
587 '''add the ntdsdsa object'''
589 rec = ctx.join_ntdsdsa_obj()
590 if ctx.forced_local_samdb:
591 ctx.samdb.add(rec, controls=["relax:0"])
593 ctx.samdb.add(rec, ["rodc_join:1:1"])
595 ctx.DsAddEntry([rec])
597 # find the GUID of our NTDS DN
598 res = ctx.samdb.search(base=ctx.ntds_dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"])
599 ctx.ntds_guid = misc.GUID(ctx.samdb.schema_format_value("objectGUID", res[0]["objectGUID"][0]))
601 def join_add_objects(ctx, specified_sid=None):
602 '''add the various objects needed for the join'''
604 print("Adding %s" % ctx.acct_dn)
607 "objectClass": "computer",
608 "displayname": ctx.samname,
609 "samaccountname": ctx.samname,
610 "userAccountControl": str(ctx.userAccountControl | samba.dsdb.UF_ACCOUNTDISABLE),
611 "dnshostname": ctx.dnshostname}
612 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2008:
613 rec['msDS-SupportedEncryptionTypes'] = str(samba.dsdb.ENC_ALL_TYPES)
614 elif ctx.promote_existing:
615 rec['msDS-SupportedEncryptionTypes'] = []
617 rec["managedby"] = ctx.managedby
618 elif ctx.promote_existing:
619 rec["managedby"] = []
621 if ctx.never_reveal_sid:
622 rec["msDS-NeverRevealGroup"] = ctx.never_reveal_sid
623 elif ctx.promote_existing:
624 rec["msDS-NeverRevealGroup"] = []
627 rec["msDS-RevealOnDemandGroup"] = ctx.reveal_sid
628 elif ctx.promote_existing:
629 rec["msDS-RevealOnDemandGroup"] = []
632 rec["objectSid"] = ndr_pack(specified_sid)
634 if ctx.promote_existing:
635 if ctx.promote_from_dn != ctx.acct_dn:
636 ctx.samdb.rename(ctx.promote_from_dn, ctx.acct_dn)
637 ctx.samdb.modify(ldb.Message.from_dict(ctx.samdb, rec, ldb.FLAG_MOD_REPLACE))
640 if specified_sid is not None:
641 controls = ["relax:0"]
642 ctx.samdb.add(rec, controls=controls)
645 ctx.add_krbtgt_account()
648 print("Adding %s" % ctx.server_dn)
651 "objectclass": "server",
652 # windows uses 50000000 decimal for systemFlags. A windows hex/decimal mixup bug?
653 "systemFlags": str(samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_RENAME |
654 samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE |
655 samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
656 # windows seems to add the dnsHostName later
657 "dnsHostName": ctx.dnshostname}
660 rec["serverReference"] = ctx.acct_dn
665 # the rest is done after replication
670 ctx.join_add_ntdsdsa()
672 # Add the Replica-Locations or RO-Replica-Locations attributes
673 # TODO Is this supposed to be for the schema partition too?
674 expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.domaindns_zone)
675 domain = (ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
677 base=ctx.samdb.get_partitions_dn(),
678 expression=expr), ctx.domaindns_zone)
680 expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.forestdns_zone)
681 forest = (ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
683 base=ctx.samdb.get_partitions_dn(),
684 expression=expr), ctx.forestdns_zone)
686 for part, zone in (domain, forest):
687 if zone not in ctx.nc_list:
693 attr = "msDS-NC-Replica-Locations"
695 attr = "msDS-NC-RO-Replica-Locations"
697 m[attr] = ldb.MessageElement(ctx.ntds_dn,
698 ldb.FLAG_MOD_ADD, attr)
701 if ctx.connection_dn is not None:
702 print("Adding %s" % ctx.connection_dn)
704 "dn": ctx.connection_dn,
705 "objectclass": "nTDSConnection",
706 "enabledconnection": "TRUE",
708 "fromServer": ctx.dc_ntds_dn}
712 print("Adding SPNs to %s" % ctx.acct_dn)
714 m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
715 for i in range(len(ctx.SPNs)):
716 ctx.SPNs[i] = ctx.SPNs[i].replace("$NTDSGUID", str(ctx.ntds_guid))
717 m["servicePrincipalName"] = ldb.MessageElement(ctx.SPNs,
718 ldb.FLAG_MOD_REPLACE,
719 "servicePrincipalName")
722 # The account password set operation should normally be done over
723 # LDAP. Windows 2000 DCs however allow this only with SSL
724 # connections which are hard to set up and otherwise refuse with
725 # ERR_UNWILLING_TO_PERFORM. In this case we fall back to libnet
727 print("Setting account password for %s" % ctx.samname)
729 ctx.samdb.setpassword("(&(objectClass=user)(sAMAccountName=%s))"
730 % ldb.binary_encode(ctx.samname),
732 force_change_at_next_login=False,
733 username=ctx.samname)
734 except ldb.LdbError as e2:
736 if num != ldb.ERR_UNWILLING_TO_PERFORM:
738 ctx.net.set_password(account_name=ctx.samname,
739 domain_name=ctx.domain_name,
740 newpassword=ctx.acct_pass.encode('utf-8'))
742 res = ctx.samdb.search(base=ctx.acct_dn, scope=ldb.SCOPE_BASE,
743 attrs=["msDS-KeyVersionNumber",
745 if "msDS-KeyVersionNumber" in res[0]:
746 ctx.key_version_number = int(res[0]["msDS-KeyVersionNumber"][0])
748 ctx.key_version_number = None
750 ctx.new_dc_account_sid = ndr_unpack(security.dom_sid,
751 res[0]["objectSid"][0])
753 print("Enabling account")
755 m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
756 m["userAccountControl"] = ldb.MessageElement(str(ctx.userAccountControl),
757 ldb.FLAG_MOD_REPLACE,
758 "userAccountControl")
761 if ctx.dns_backend.startswith("BIND9_"):
762 ctx.dnspass = samba.generate_random_password(128, 255)
764 recs = ctx.samdb.parse_ldif(read_and_sub_file(setup_path("provision_dns_add_samba.ldif"),
765 {"DNSDOMAIN": ctx.dnsdomain,
766 "DOMAINDN": ctx.base_dn,
767 "HOSTNAME": ctx.myname,
768 "DNSPASS_B64": b64encode(ctx.dnspass.encode('utf-16-le')).decode('utf8'),
769 "DNSNAME": ctx.dnshostname}))
770 for changetype, msg in recs:
771 assert changetype == ldb.CHANGETYPE_NONE
772 dns_acct_dn = msg["dn"]
773 print("Adding DNS account %s with dns/ SPN" % msg["dn"])
775 # Remove dns password (we will set it as a modify, as we can't do clearTextPassword over LDAP)
776 del msg["clearTextPassword"]
777 # Remove isCriticalSystemObject for similar reasons, it cannot be set over LDAP
778 del msg["isCriticalSystemObject"]
779 # Disable account until password is set
780 msg["userAccountControl"] = str(samba.dsdb.UF_NORMAL_ACCOUNT |
781 samba.dsdb.UF_ACCOUNTDISABLE)
784 except ldb.LdbError as e:
786 if num != ldb.ERR_ENTRY_ALREADY_EXISTS:
789 # The account password set operation should normally be done over
790 # LDAP. Windows 2000 DCs however allow this only with SSL
791 # connections which are hard to set up and otherwise refuse with
792 # ERR_UNWILLING_TO_PERFORM. In this case we fall back to libnet
794 print("Setting account password for dns-%s" % ctx.myname)
796 ctx.samdb.setpassword("(&(objectClass=user)(samAccountName=dns-%s))"
797 % ldb.binary_encode(ctx.myname),
799 force_change_at_next_login=False,
800 username=ctx.samname)
801 except ldb.LdbError as e3:
803 if num != ldb.ERR_UNWILLING_TO_PERFORM:
805 ctx.net.set_password(account_name="dns-%s" % ctx.myname,
806 domain_name=ctx.domain_name,
807 newpassword=ctx.dnspass)
809 res = ctx.samdb.search(base=dns_acct_dn, scope=ldb.SCOPE_BASE,
810 attrs=["msDS-KeyVersionNumber"])
811 if "msDS-KeyVersionNumber" in res[0]:
812 ctx.dns_key_version_number = int(res[0]["msDS-KeyVersionNumber"][0])
814 ctx.dns_key_version_number = None
816 def join_add_objects2(ctx):
817 """add the various objects needed for the join, for subdomains post replication"""
819 print("Adding %s" % ctx.partition_dn)
820 name_map = {'SubdomainAdmins': "%s-%s" % (str(ctx.domsid), security.DOMAIN_RID_ADMINS)}
821 sd_binary = descriptor.get_paritions_crossref_subdomain_descriptor(ctx.forestsid, name_map=name_map)
823 "dn": ctx.partition_dn,
824 "objectclass": "crossRef",
825 "objectCategory": "CN=Cross-Ref,%s" % ctx.schema_dn,
826 "nCName": ctx.base_dn,
827 "nETBIOSName": ctx.domain_name,
828 "dnsRoot": ctx.dnsdomain,
829 "trustParent": ctx.parent_partition_dn,
830 "systemFlags": str(samba.dsdb.SYSTEM_FLAG_CR_NTDS_NC |samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN),
831 "ntSecurityDescriptor": sd_binary,
834 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
835 rec["msDS-Behavior-Version"] = str(ctx.behavior_version)
837 rec2 = ctx.join_ntdsdsa_obj()
839 objects = ctx.DsAddEntry([rec, rec2])
840 if len(objects) != 2:
841 raise DCJoinException("Expected 2 objects from DsAddEntry")
843 ctx.ntds_guid = objects[1].guid
845 print("Replicating partition DN")
846 ctx.repl.replicate(ctx.partition_dn,
847 misc.GUID("00000000-0000-0000-0000-000000000000"),
849 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
850 replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP)
852 print("Replicating NTDS DN")
853 ctx.repl.replicate(ctx.ntds_dn,
854 misc.GUID("00000000-0000-0000-0000-000000000000"),
856 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
857 replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP)
859 def join_provision(ctx):
860 """Provision the local SAM."""
862 print("Calling bare provision")
864 smbconf = ctx.lp.configfile
866 presult = provision(ctx.logger, system_session(), smbconf=smbconf,
867 targetdir=ctx.targetdir, samdb_fill=FILL_DRS, realm=ctx.realm,
868 rootdn=ctx.root_dn, domaindn=ctx.base_dn,
869 schemadn=ctx.schema_dn, configdn=ctx.config_dn,
870 serverdn=ctx.server_dn, domain=ctx.domain_name,
871 hostname=ctx.myname, domainsid=ctx.domsid,
872 machinepass=ctx.acct_pass, serverrole="active directory domain controller",
873 sitename=ctx.site, lp=ctx.lp, ntdsguid=ctx.ntds_guid,
874 use_ntvfs=ctx.use_ntvfs, dns_backend=ctx.dns_backend,
875 plaintext_secrets=ctx.plaintext_secrets,
876 backend_store=ctx.backend_store
878 print("Provision OK for domain DN %s" % presult.domaindn)
879 ctx.local_samdb = presult.samdb
881 ctx.paths = presult.paths
882 ctx.names = presult.names
884 # Fix up the forestsid, it may be different if we are joining as a subdomain
885 ctx.names.forestsid = ctx.forestsid
887 def join_provision_own_domain(ctx):
888 """Provision the local SAM."""
890 # we now operate exclusively on the local database, which
891 # we need to reopen in order to get the newly created schema
892 print("Reconnecting to local samdb")
893 ctx.samdb = SamDB(url=ctx.local_samdb.url,
894 session_info=system_session(),
895 lp=ctx.local_samdb.lp,
897 ctx.samdb.set_invocation_id(str(ctx.invocation_id))
898 ctx.local_samdb = ctx.samdb
900 ctx.logger.info("Finding domain GUID from ncName")
901 res = ctx.local_samdb.search(base=ctx.partition_dn, scope=ldb.SCOPE_BASE, attrs=['ncName'],
902 controls=["extended_dn:1:1", "reveal_internals:0"])
904 if 'nCName' not in res[0]:
905 raise DCJoinException("Can't find naming context on partition DN %s in %s" % (ctx.partition_dn, ctx.samdb.url))
908 ctx.names.domainguid = str(misc.GUID(ldb.Dn(ctx.samdb, res[0]['ncName'][0].decode('utf8')).get_extended_component('GUID')))
910 raise DCJoinException("Can't find GUID in naming master on partition DN %s" % res[0]['ncName'][0])
912 ctx.logger.info("Got domain GUID %s" % ctx.names.domainguid)
914 ctx.logger.info("Calling own domain provision")
916 secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
918 presult = provision_fill(ctx.local_samdb, secrets_ldb,
919 ctx.logger, ctx.names, ctx.paths,
920 dom_for_fun_level=DS_DOMAIN_FUNCTION_2003,
921 targetdir=ctx.targetdir, samdb_fill=FILL_SUBDOMAIN,
922 machinepass=ctx.acct_pass, serverrole="active directory domain controller",
923 lp=ctx.lp, hostip=ctx.names.hostip, hostip6=ctx.names.hostip6,
924 dns_backend=ctx.dns_backend, adminpass=ctx.adminpass)
925 print("Provision OK for domain %s" % ctx.names.dnsdomain)
927 def create_replicator(ctx, repl_creds, binding_options):
928 '''Creates a new DRS object for managing replications'''
929 return drs_utils.drs_Replicate(
930 "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
931 ctx.lp, repl_creds, ctx.local_samdb, ctx.invocation_id)
933 def join_replicate(ctx):
934 """Replicate the SAM."""
936 print("Starting replication")
937 ctx.local_samdb.transaction_start()
939 source_dsa_invocation_id = misc.GUID(ctx.samdb.get_invocation_id())
940 if ctx.ntds_guid is None:
941 print("Using DS_BIND_GUID_W2K3")
942 destination_dsa_guid = misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID_W2K3)
944 destination_dsa_guid = ctx.ntds_guid
947 repl_creds = Credentials()
948 repl_creds.guess(ctx.lp)
949 repl_creds.set_kerberos_state(DONT_USE_KERBEROS)
950 repl_creds.set_username(ctx.samname)
951 repl_creds.set_password(ctx.acct_pass.encode('utf-8'))
953 repl_creds = ctx.creds
955 binding_options = "seal"
956 if ctx.lp.log_level() >= 9:
957 binding_options += ",print"
959 repl = ctx.create_replicator(repl_creds, binding_options)
961 repl.replicate(ctx.schema_dn, source_dsa_invocation_id,
962 destination_dsa_guid, schema=True, rodc=ctx.RODC,
963 replica_flags=ctx.replica_flags)
964 repl.replicate(ctx.config_dn, source_dsa_invocation_id,
965 destination_dsa_guid, rodc=ctx.RODC,
966 replica_flags=ctx.replica_flags)
967 if not ctx.subdomain:
968 # Replicate first the critical object for the basedn
969 if not ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
970 print("Replicating critical objects from the base DN of the domain")
971 ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
972 repl.replicate(ctx.base_dn, source_dsa_invocation_id,
973 destination_dsa_guid, rodc=ctx.RODC,
974 replica_flags=ctx.domain_replica_flags)
975 ctx.domain_replica_flags ^= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
976 repl.replicate(ctx.base_dn, source_dsa_invocation_id,
977 destination_dsa_guid, rodc=ctx.RODC,
978 replica_flags=ctx.domain_replica_flags)
979 print("Done with always replicated NC (base, config, schema)")
981 # At this point we should already have an entry in the ForestDNS
982 # and DomainDNS NC (those under CN=Partions,DC=...) in order to
983 # indicate that we hold a replica for this NC.
984 for nc in (ctx.domaindns_zone, ctx.forestdns_zone):
985 if nc in ctx.nc_list:
986 print("Replicating %s" % (str(nc)))
987 repl.replicate(nc, source_dsa_invocation_id,
988 destination_dsa_guid, rodc=ctx.RODC,
989 replica_flags=ctx.replica_flags)
992 repl.replicate(ctx.acct_dn, source_dsa_invocation_id,
993 destination_dsa_guid,
994 exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
995 repl.replicate(ctx.new_krbtgt_dn, source_dsa_invocation_id,
996 destination_dsa_guid,
997 exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
998 elif ctx.rid_manager_dn is not None:
999 # Try and get a RID Set if we can. This is only possible against the RID Master. Warn otherwise.
1001 repl.replicate(ctx.rid_manager_dn, source_dsa_invocation_id,
1002 destination_dsa_guid,
1003 exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC)
1004 except samba.DsExtendedError as e1:
1005 (enum, estr) = e1.args
1006 if enum == drsuapi.DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER:
1007 print("WARNING: Unable to replicate own RID Set, as server %s (the server we joined) is not the RID Master." % ctx.server)
1008 print("NOTE: This is normal and expected, Samba will be able to create users after it contacts the RID Master at first startup.")
1013 ctx.source_dsa_invocation_id = source_dsa_invocation_id
1014 ctx.destination_dsa_guid = destination_dsa_guid
1016 print("Committing SAM database")
1018 ctx.local_samdb.transaction_cancel()
1021 ctx.local_samdb.transaction_commit()
1023 # A large replication may have caused our LDB connection to the
1024 # remote DC to timeout, so check the connection is still alive
1025 ctx.refresh_ldb_connection()
1027 def refresh_ldb_connection(ctx):
1029 # query the rootDSE to check the connection
1030 ctx.samdb.search(scope=ldb.SCOPE_BASE, attrs=[])
1031 except ldb.LdbError as e:
1032 (enum, estr) = e.args
1034 # if the connection was disconnected, then reconnect
1035 if (enum == ldb.ERR_OPERATIONS_ERROR and
1036 'NT_STATUS_CONNECTION_DISCONNECTED' in estr):
1037 ctx.logger.warning("LDB connection disconnected. Reconnecting")
1038 ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
1039 session_info=system_session(),
1040 credentials=ctx.creds, lp=ctx.lp)
1042 raise DCJoinException(estr)
1044 def send_DsReplicaUpdateRefs(ctx, dn):
1045 r = drsuapi.DsReplicaUpdateRefsRequest1()
1046 r.naming_context = drsuapi.DsReplicaObjectIdentifier()
1047 r.naming_context.dn = str(dn)
1048 r.naming_context.guid = misc.GUID("00000000-0000-0000-0000-000000000000")
1049 r.naming_context.sid = security.dom_sid("S-0-0")
1050 r.dest_dsa_guid = ctx.ntds_guid
1051 r.dest_dsa_dns_name = "%s._msdcs.%s" % (str(ctx.ntds_guid), ctx.dnsforest)
1052 r.options = drsuapi.DRSUAPI_DRS_ADD_REF | drsuapi.DRSUAPI_DRS_DEL_REF
1054 r.options |= drsuapi.DRSUAPI_DRS_WRIT_REP
1056 if ctx.drsuapi is None:
1057 ctx.drsuapi_connect()
1059 ctx.drsuapi.DsReplicaUpdateRefs(ctx.drsuapi_handle, 1, r)
1061 def join_add_dns_records(ctx):
1062 """Remotely Add a DNS record to the target DC. We assume that if we
1063 replicate DNS that the server holds the DNS roles and can accept
1066 This avoids issues getting replication going after the DC
1067 first starts as the rest of the domain does not have to
1068 wait for samba_dnsupdate to run successfully.
1070 Specifically, we add the records implied by the DsReplicaUpdateRefs
1073 We do not just run samba_dnsupdate as we want to strictly
1074 operate against the DC we just joined:
1075 - We do not want to query another DNS server
1076 - We do not want to obtain a Kerberos ticket
1077 (as the KDC we select may not be the DC we just joined,
1078 and so may not be in sync with the password we just set)
1079 - We do not wish to set the _ldap records until we have started
1080 - We do not wish to use NTLM (the --use-samba-tool mode forces
1085 client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
1086 record_type = dnsp.DNS_TYPE_A
1087 select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA |\
1088 dnsserver.DNS_RPC_VIEW_NO_CHILDREN
1090 zone = ctx.dnsdomain
1091 msdcs_zone = "_msdcs.%s" % ctx.dnsforest
1093 msdcs_cname = str(ctx.ntds_guid)
1094 cname_target = "%s.%s" % (name, zone)
1095 IPs = samba.interface_ips(ctx.lp, ctx.force_all_ips)
1097 ctx.logger.info("Adding %d remote DNS records for %s.%s" %
1098 (len(IPs), name, zone))
1100 binding_options = "sign"
1101 dns_conn = dnsserver.dnsserver("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
1106 sd_helper = samba.sd_utils.SDUtils(ctx.samdb)
1108 change_owner_sd = security.descriptor()
1109 change_owner_sd.owner_sid = ctx.new_dc_account_sid
1110 change_owner_sd.group_sid = security.dom_sid("%s-%d" %
1112 security.DOMAIN_RID_DCS))
1114 # TODO: Remove any old records from the primary DNS name
1117 = dns_conn.DnssrvEnumRecords2(client_version,
1127 except WERRORError as e:
1128 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1134 for record in rec.records:
1135 if record.wType == dnsp.DNS_TYPE_A or \
1136 record.wType == dnsp.DNS_TYPE_AAAA:
1138 del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1139 del_rec_buf.rec = record
1141 dns_conn.DnssrvUpdateRecord2(client_version,
1148 except WERRORError as e:
1149 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1155 if IP.find(':') != -1:
1156 ctx.logger.info("Adding DNS AAAA record %s.%s for IPv6 IP: %s"
1158 rec = AAAARecord(IP)
1160 ctx.logger.info("Adding DNS A record %s.%s for IPv4 IP: %s"
1165 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1166 add_rec_buf.rec = rec
1167 dns_conn.DnssrvUpdateRecord2(client_version,
1176 domaindns_zone_dn = ldb.Dn(ctx.samdb, ctx.domaindns_zone)
1177 (ctx.dns_a_dn, ldap_record) \
1178 = ctx.samdb.dns_lookup("%s.%s" % (name, zone),
1179 dns_partition=domaindns_zone_dn)
1181 # Make the DC own the DNS record, not the administrator
1182 sd_helper.modify_sd_on_dn(ctx.dns_a_dn, change_owner_sd,
1183 controls=["sd_flags:1:%d"
1184 % (security.SECINFO_OWNER
1185 | security.SECINFO_GROUP)])
1188 ctx.logger.info("Adding DNS CNAME record %s.%s for %s"
1189 % (msdcs_cname, msdcs_zone, cname_target))
1191 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1192 rec = CNameRecord(cname_target)
1193 add_rec_buf.rec = rec
1194 dns_conn.DnssrvUpdateRecord2(client_version,
1202 forestdns_zone_dn = ldb.Dn(ctx.samdb, ctx.forestdns_zone)
1203 (ctx.dns_cname_dn, ldap_record) \
1204 = ctx.samdb.dns_lookup("%s.%s" % (msdcs_cname, msdcs_zone),
1205 dns_partition=forestdns_zone_dn)
1207 # Make the DC own the DNS record, not the administrator
1208 sd_helper.modify_sd_on_dn(ctx.dns_cname_dn, change_owner_sd,
1209 controls=["sd_flags:1:%d"
1210 % (security.SECINFO_OWNER
1211 | security.SECINFO_GROUP)])
1213 ctx.logger.info("All other DNS records (like _ldap SRV records) " +
1214 "will be created samba_dnsupdate on first startup")
1216 def join_replicate_new_dns_records(ctx):
1217 for nc in (ctx.domaindns_zone, ctx.forestdns_zone):
1218 if nc in ctx.nc_list:
1219 ctx.logger.info("Replicating new DNS records in %s" % (str(nc)))
1220 ctx.repl.replicate(nc, ctx.source_dsa_invocation_id,
1221 ctx.ntds_guid, rodc=ctx.RODC,
1222 replica_flags=ctx.replica_flags,
1225 def join_finalise(ctx):
1226 """Finalise the join, mark us synchronised and setup secrets db."""
1228 # FIXME we shouldn't do this in all cases
1230 # If for some reasons we joined in another site than the one of
1231 # DC we just replicated from then we don't need to send the updatereplicateref
1232 # as replication between sites is time based and on the initiative of the
1234 ctx.logger.info("Sending DsReplicaUpdateRefs for all the replicated partitions")
1235 for nc in ctx.nc_list:
1236 ctx.send_DsReplicaUpdateRefs(nc)
1239 print("Setting RODC invocationId")
1240 ctx.local_samdb.set_invocation_id(str(ctx.invocation_id))
1241 ctx.local_samdb.set_opaque_integer("domainFunctionality",
1242 ctx.behavior_version)
1244 m.dn = ldb.Dn(ctx.local_samdb, "%s" % ctx.ntds_dn)
1245 m["invocationId"] = ldb.MessageElement(ndr_pack(ctx.invocation_id),
1246 ldb.FLAG_MOD_REPLACE,
1248 ctx.local_samdb.modify(m)
1250 # Note: as RODC the invocationId is only stored
1251 # on the RODC itself, the other DCs never see it.
1253 # Thats is why we fix up the replPropertyMetaData stamp
1254 # for the 'invocationId' attribute, we need to change
1255 # the 'version' to '0', this is what windows 2008r2 does as RODC
1257 # This means if the object on a RWDC ever gets a invocationId
1258 # attribute, it will have version '1' (or higher), which will
1259 # will overwrite the RODC local value.
1260 ctx.local_samdb.set_attribute_replmetadata_version(m.dn,
1264 ctx.logger.info("Setting isSynchronized and dsServiceName")
1266 m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
1267 m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isSynchronized")
1269 guid = ctx.ntds_guid
1270 m["dsServiceName"] = ldb.MessageElement("<GUID=%s>" % str(guid),
1271 ldb.FLAG_MOD_REPLACE, "dsServiceName")
1272 ctx.local_samdb.modify(m)
1277 secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
1279 ctx.logger.info("Setting up secrets database")
1280 secretsdb_self_join(secrets_ldb, domain=ctx.domain_name,
1282 dnsdomain=ctx.dnsdomain,
1283 netbiosname=ctx.myname,
1284 domainsid=ctx.domsid,
1285 machinepass=ctx.acct_pass,
1286 secure_channel_type=ctx.secure_channel_type,
1287 key_version_number=ctx.key_version_number)
1289 if ctx.dns_backend.startswith("BIND9_"):
1290 setup_bind9_dns(ctx.local_samdb, secrets_ldb,
1291 ctx.names, ctx.paths, ctx.lp, ctx.logger,
1292 dns_backend=ctx.dns_backend,
1293 dnspass=ctx.dnspass, os_level=ctx.behavior_version,
1294 targetdir=ctx.targetdir,
1295 key_version_number=ctx.dns_key_version_number)
1297 def join_setup_trusts(ctx):
1298 """provision the local SAM."""
1300 print("Setup domain trusts with server %s" % ctx.server)
1301 binding_options = "" # why doesn't signing work here? w2k8r2 claims no session key
1302 lsaconn = lsa.lsarpc("ncacn_np:%s[%s]" % (ctx.server, binding_options),
1305 objectAttr = lsa.ObjectAttribute()
1306 objectAttr.sec_qos = lsa.QosInfo()
1308 pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
1309 objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
1311 info = lsa.TrustDomainInfoInfoEx()
1312 info.domain_name.string = ctx.dnsdomain
1313 info.netbios_name.string = ctx.domain_name
1314 info.sid = ctx.domsid
1315 info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND
1316 info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
1317 info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST
1320 oldname = lsa.String()
1321 oldname.string = ctx.dnsdomain
1322 oldinfo = lsaconn.QueryTrustedDomainInfoByName(pol_handle, oldname,
1323 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
1324 print("Removing old trust record for %s (SID %s)" % (ctx.dnsdomain, oldinfo.info_ex.sid))
1325 lsaconn.DeleteTrustedDomain(pol_handle, oldinfo.info_ex.sid)
1326 except RuntimeError:
1329 password_blob = string_to_byte_array(ctx.trustdom_pass.encode('utf-16-le'))
1331 clear_value = drsblobs.AuthInfoClear()
1332 clear_value.size = len(password_blob)
1333 clear_value.password = password_blob
1335 clear_authentication_information = drsblobs.AuthenticationInformation()
1336 clear_authentication_information.LastUpdateTime = samba.unix2nttime(int(time.time()))
1337 clear_authentication_information.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
1338 clear_authentication_information.AuthInfo = clear_value
1340 authentication_information_array = drsblobs.AuthenticationInformationArray()
1341 authentication_information_array.count = 1
1342 authentication_information_array.array = [clear_authentication_information]
1344 outgoing = drsblobs.trustAuthInOutBlob()
1346 outgoing.current = authentication_information_array
1348 trustpass = drsblobs.trustDomainPasswords()
1349 confounder = [3] * 512
1351 for i in range(512):
1352 confounder[i] = random.randint(0, 255)
1354 trustpass.confounder = confounder
1356 trustpass.outgoing = outgoing
1357 trustpass.incoming = outgoing
1359 trustpass_blob = ndr_pack(trustpass)
1361 encrypted_trustpass = arcfour_encrypt(lsaconn.session_key, trustpass_blob)
1363 auth_blob = lsa.DATA_BUF2()
1364 auth_blob.size = len(encrypted_trustpass)
1365 auth_blob.data = string_to_byte_array(encrypted_trustpass)
1367 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
1368 auth_info.auth_blob = auth_blob
1370 trustdom_handle = lsaconn.CreateTrustedDomainEx2(pol_handle,
1373 security.SEC_STD_DELETE)
1376 "dn": "cn=%s,cn=system,%s" % (ctx.dnsforest, ctx.base_dn),
1377 "objectclass": "trustedDomain",
1378 "trustType": str(info.trust_type),
1379 "trustAttributes": str(info.trust_attributes),
1380 "trustDirection": str(info.trust_direction),
1381 "flatname": ctx.forest_domain_name,
1382 "trustPartner": ctx.dnsforest,
1383 "trustAuthIncoming": ndr_pack(outgoing),
1384 "trustAuthOutgoing": ndr_pack(outgoing),
1385 "securityIdentifier": ndr_pack(ctx.forestsid)
1387 ctx.local_samdb.add(rec)
1390 "dn": "cn=%s$,cn=users,%s" % (ctx.forest_domain_name, ctx.base_dn),
1391 "objectclass": "user",
1392 "userAccountControl": str(samba.dsdb.UF_INTERDOMAIN_TRUST_ACCOUNT),
1393 "clearTextPassword": ctx.trustdom_pass.encode('utf-16-le'),
1394 "samAccountName": "%s$" % ctx.forest_domain_name
1396 ctx.local_samdb.add(rec)
1398 def build_nc_lists(ctx):
1399 # nc_list is the list of naming context (NC) for which we will
1400 # replicate in and send a updateRef command to the partner DC
1402 # full_nc_list is the list of naming context (NC) we hold
1403 # read/write copies of. These are not subsets of each other.
1404 ctx.nc_list = [ctx.config_dn, ctx.schema_dn]
1405 ctx.full_nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn]
1407 if ctx.subdomain and ctx.dns_backend != "NONE":
1408 ctx.full_nc_list += [ctx.domaindns_zone]
1410 elif not ctx.subdomain:
1411 ctx.nc_list += [ctx.base_dn]
1413 if ctx.dns_backend != "NONE":
1414 ctx.nc_list += [ctx.domaindns_zone]
1415 ctx.nc_list += [ctx.forestdns_zone]
1416 ctx.full_nc_list += [ctx.domaindns_zone]
1417 ctx.full_nc_list += [ctx.forestdns_zone]
1420 ctx.build_nc_lists()
1422 if ctx.promote_existing:
1423 ctx.promote_possible()
1425 ctx.cleanup_old_join()
1428 ctx.join_add_objects()
1429 ctx.join_provision()
1430 ctx.join_replicate()
1432 ctx.join_add_objects2()
1433 ctx.join_provision_own_domain()
1434 ctx.join_setup_trusts()
1436 if ctx.dns_backend != "NONE":
1437 ctx.join_add_dns_records()
1438 ctx.join_replicate_new_dns_records()
1443 print("Join failed - cleaning up")
1447 # cleanup the failed join (checking we still have a live LDB
1448 # connection to the remote DC first)
1449 ctx.refresh_ldb_connection()
1450 ctx.cleanup_old_join()
1454 def join_RODC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
1455 targetdir=None, domain=None, domain_critical_only=False,
1456 machinepass=None, use_ntvfs=False, dns_backend=None,
1457 promote_existing=False, plaintext_secrets=False,
1458 backend_store=None):
1459 """Join as a RODC."""
1461 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1462 targetdir, domain, machinepass, use_ntvfs, dns_backend,
1463 promote_existing, plaintext_secrets,
1464 backend_store=backend_store)
1466 lp.set("workgroup", ctx.domain_name)
1467 logger.info("workgroup is %s" % ctx.domain_name)
1469 lp.set("realm", ctx.realm)
1470 logger.info("realm is %s" % ctx.realm)
1472 ctx.krbtgt_dn = "CN=krbtgt_%s,CN=Users,%s" % (ctx.myname, ctx.base_dn)
1474 # setup some defaults for accounts that should be replicated to this RODC
1475 ctx.never_reveal_sid = [
1476 "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_DENY),
1477 "<SID=%s>" % security.SID_BUILTIN_ADMINISTRATORS,
1478 "<SID=%s>" % security.SID_BUILTIN_SERVER_OPERATORS,
1479 "<SID=%s>" % security.SID_BUILTIN_BACKUP_OPERATORS,
1480 "<SID=%s>" % security.SID_BUILTIN_ACCOUNT_OPERATORS]
1481 ctx.reveal_sid = "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_ALLOW)
1483 mysid = ctx.get_mysid()
1484 admin_dn = "<SID=%s>" % mysid
1485 ctx.managedby = admin_dn
1487 ctx.userAccountControl = (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT |
1488 samba.dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION |
1489 samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT)
1491 ctx.SPNs.extend(["RestrictedKrbHost/%s" % ctx.myname,
1492 "RestrictedKrbHost/%s" % ctx.dnshostname])
1494 ctx.connection_dn = "CN=RODC Connection (FRS),%s" % ctx.ntds_dn
1495 ctx.secure_channel_type = misc.SEC_CHAN_RODC
1497 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING |
1498 drsuapi.DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP)
1499 ctx.domain_replica_flags = ctx.replica_flags
1500 if domain_critical_only:
1501 ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
1505 logger.info("Joined domain %s (SID %s) as an RODC" % (ctx.domain_name, ctx.domsid))
1508 def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
1509 targetdir=None, domain=None, domain_critical_only=False,
1510 machinepass=None, use_ntvfs=False, dns_backend=None,
1511 promote_existing=False, plaintext_secrets=False,
1512 backend_store=None):
1514 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1515 targetdir, domain, machinepass, use_ntvfs, dns_backend,
1516 promote_existing, plaintext_secrets,
1517 backend_store=backend_store)
1519 lp.set("workgroup", ctx.domain_name)
1520 logger.info("workgroup is %s" % ctx.domain_name)
1522 lp.set("realm", ctx.realm)
1523 logger.info("realm is %s" % ctx.realm)
1525 ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
1527 ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
1528 ctx.secure_channel_type = misc.SEC_CHAN_BDC
1530 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1531 drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1532 ctx.domain_replica_flags = ctx.replica_flags
1533 if domain_critical_only:
1534 ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
1537 logger.info("Joined domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))
1540 def join_clone(logger=None, server=None, creds=None, lp=None,
1541 targetdir=None, domain=None, include_secrets=False,
1542 dns_backend="NONE"):
1543 """Creates a local clone of a remote DC."""
1544 ctx = DCCloneContext(logger, server, creds, lp, targetdir=targetdir,
1545 domain=domain, dns_backend=dns_backend,
1546 include_secrets=include_secrets)
1548 lp.set("workgroup", ctx.domain_name)
1549 logger.info("workgroup is %s" % ctx.domain_name)
1551 lp.set("realm", ctx.realm)
1552 logger.info("realm is %s" % ctx.realm)
1555 logger.info("Cloned domain %s (SID %s)" % (ctx.domain_name, ctx.domsid))
1559 def join_subdomain(logger=None, server=None, creds=None, lp=None, site=None,
1560 netbios_name=None, targetdir=None, parent_domain=None, dnsdomain=None,
1561 netbios_domain=None, machinepass=None, adminpass=None, use_ntvfs=False,
1562 dns_backend=None, plaintext_secrets=False,
1563 backend_store=None):
1565 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1566 targetdir, parent_domain, machinepass, use_ntvfs,
1567 dns_backend, plaintext_secrets,
1568 backend_store=backend_store)
1569 ctx.subdomain = True
1570 if adminpass is None:
1571 ctx.adminpass = samba.generate_random_password(12, 32)
1573 ctx.adminpass = adminpass
1574 ctx.parent_domain_name = ctx.domain_name
1575 ctx.domain_name = netbios_domain
1576 ctx.realm = dnsdomain
1577 ctx.parent_dnsdomain = ctx.dnsdomain
1578 ctx.parent_partition_dn = ctx.get_parent_partition_dn()
1579 ctx.dnsdomain = dnsdomain
1580 ctx.partition_dn = "CN=%s,CN=Partitions,%s" % (ctx.domain_name, ctx.config_dn)
1581 ctx.naming_master = ctx.get_naming_master()
1582 if ctx.naming_master != ctx.server:
1583 logger.info("Reconnecting to naming master %s" % ctx.naming_master)
1584 ctx.server = ctx.naming_master
1585 ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
1586 session_info=system_session(),
1587 credentials=ctx.creds, lp=ctx.lp)
1588 res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=['dnsHostName'],
1590 ctx.server = res[0]["dnsHostName"]
1591 logger.info("DNS name of new naming master is %s" % ctx.server)
1593 ctx.base_dn = samba.dn_from_dns_name(dnsdomain)
1594 ctx.forestsid = ctx.domsid
1595 ctx.domsid = security.random_sid()
1597 ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain)
1598 # Windows uses 240 bytes as UTF16 so we do
1599 ctx.trustdom_pass = samba.generate_random_machine_password(120, 120)
1601 ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
1603 ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
1604 ctx.secure_channel_type = misc.SEC_CHAN_BDC
1606 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1607 drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1608 ctx.domain_replica_flags = ctx.replica_flags
1611 ctx.logger.info("Created domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))
1614 class DCCloneContext(DCJoinContext):
1615 """Clones a remote DC."""
1617 def __init__(ctx, logger=None, server=None, creds=None, lp=None,
1618 targetdir=None, domain=None, dns_backend=None,
1619 include_secrets=False):
1620 super(DCCloneContext, ctx).__init__(logger, server, creds, lp,
1621 targetdir=targetdir, domain=domain,
1622 dns_backend=dns_backend)
1624 # As we don't want to create or delete these DNs, we set them to None
1625 ctx.server_dn = None
1628 ctx.myname = ctx.server.split('.')[0]
1629 ctx.ntds_guid = None
1630 ctx.rid_manager_dn = None
1633 ctx.remote_dc_ntds_guid = ctx.samdb.get_ntds_GUID()
1635 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1636 drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1637 if not include_secrets:
1638 ctx.replica_flags |= drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
1639 ctx.domain_replica_flags = ctx.replica_flags
1641 def join_finalise(ctx):
1642 ctx.logger.info("Setting isSynchronized and dsServiceName")
1644 m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
1645 m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE,
1648 # We want to appear to be the server we just cloned
1649 guid = ctx.remote_dc_ntds_guid
1650 m["dsServiceName"] = ldb.MessageElement("<GUID=%s>" % str(guid),
1651 ldb.FLAG_MOD_REPLACE,
1653 ctx.local_samdb.modify(m)
1656 ctx.build_nc_lists()
1658 # When cloning a DC, we just want to provision a DC locally, then
1659 # grab the remote DC's entire DB via DRS replication
1660 ctx.join_provision()
1661 ctx.join_replicate()
1665 # Used to create a renamed backup of a DC. Renaming the domain means that the
1666 # cloned/backup DC can be started without interfering with the production DC.
1667 class DCCloneAndRenameContext(DCCloneContext):
1668 """Clones a remote DC, renaming the domain along the way."""
1670 def __init__(ctx, new_base_dn, new_domain_name, new_realm, logger=None,
1671 server=None, creds=None, lp=None, targetdir=None, domain=None,
1672 dns_backend=None, include_secrets=True):
1673 super(DCCloneAndRenameContext, ctx).__init__(logger, server, creds, lp,
1674 targetdir=targetdir,
1676 dns_backend=dns_backend,
1677 include_secrets=include_secrets)
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)
1746 print("Provision OK for renamed domain DN %s" % presult.domaindn)
1747 ctx.local_samdb = presult.samdb
1748 ctx.paths = presult.paths