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 or
1035 'NT_STATUS_CONNECTION_RESET' in estr)):
1036 ctx.logger.warning("LDB connection disconnected. Reconnecting")
1037 ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
1038 session_info=system_session(),
1039 credentials=ctx.creds, lp=ctx.lp)
1041 raise DCJoinException(estr)
1043 def send_DsReplicaUpdateRefs(ctx, dn):
1044 r = drsuapi.DsReplicaUpdateRefsRequest1()
1045 r.naming_context = drsuapi.DsReplicaObjectIdentifier()
1046 r.naming_context.dn = str(dn)
1047 r.naming_context.guid = misc.GUID("00000000-0000-0000-0000-000000000000")
1048 r.naming_context.sid = security.dom_sid("S-0-0")
1049 r.dest_dsa_guid = ctx.ntds_guid
1050 r.dest_dsa_dns_name = "%s._msdcs.%s" % (str(ctx.ntds_guid), ctx.dnsforest)
1051 r.options = drsuapi.DRSUAPI_DRS_ADD_REF | drsuapi.DRSUAPI_DRS_DEL_REF
1053 r.options |= drsuapi.DRSUAPI_DRS_WRIT_REP
1055 if ctx.drsuapi is None:
1056 ctx.drsuapi_connect()
1058 ctx.drsuapi.DsReplicaUpdateRefs(ctx.drsuapi_handle, 1, r)
1060 def join_add_dns_records(ctx):
1061 """Remotely Add a DNS record to the target DC. We assume that if we
1062 replicate DNS that the server holds the DNS roles and can accept
1065 This avoids issues getting replication going after the DC
1066 first starts as the rest of the domain does not have to
1067 wait for samba_dnsupdate to run successfully.
1069 Specifically, we add the records implied by the DsReplicaUpdateRefs
1072 We do not just run samba_dnsupdate as we want to strictly
1073 operate against the DC we just joined:
1074 - We do not want to query another DNS server
1075 - We do not want to obtain a Kerberos ticket
1076 (as the KDC we select may not be the DC we just joined,
1077 and so may not be in sync with the password we just set)
1078 - We do not wish to set the _ldap records until we have started
1079 - We do not wish to use NTLM (the --use-samba-tool mode forces
1084 client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
1085 select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA |\
1086 dnsserver.DNS_RPC_VIEW_NO_CHILDREN
1088 zone = ctx.dnsdomain
1089 msdcs_zone = "_msdcs.%s" % ctx.dnsforest
1091 msdcs_cname = str(ctx.ntds_guid)
1092 cname_target = "%s.%s" % (name, zone)
1093 IPs = samba.interface_ips(ctx.lp, ctx.force_all_ips)
1095 ctx.logger.info("Adding %d remote DNS records for %s.%s" %
1096 (len(IPs), name, zone))
1098 binding_options = "sign"
1099 dns_conn = dnsserver.dnsserver("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
1104 sd_helper = sd_utils.SDUtils(ctx.samdb)
1106 change_owner_sd = security.descriptor()
1107 change_owner_sd.owner_sid = ctx.new_dc_account_sid
1108 change_owner_sd.group_sid = security.dom_sid("%s-%d" %
1110 security.DOMAIN_RID_DCS))
1112 # TODO: Remove any old records from the primary DNS name
1115 = dns_conn.DnssrvEnumRecords2(client_version,
1125 except WERRORError as e:
1126 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1132 for record in rec.records:
1133 if record.wType == dnsp.DNS_TYPE_A or \
1134 record.wType == dnsp.DNS_TYPE_AAAA:
1136 del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1137 del_rec_buf.rec = record
1139 dns_conn.DnssrvUpdateRecord2(client_version,
1146 except WERRORError as e:
1147 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1153 if IP.find(':') != -1:
1154 ctx.logger.info("Adding DNS AAAA record %s.%s for IPv6 IP: %s"
1156 rec = AAAARecord(IP)
1158 ctx.logger.info("Adding DNS A record %s.%s for IPv4 IP: %s"
1163 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1164 add_rec_buf.rec = rec
1165 dns_conn.DnssrvUpdateRecord2(client_version,
1174 domaindns_zone_dn = ldb.Dn(ctx.samdb, ctx.domaindns_zone)
1175 (ctx.dns_a_dn, ldap_record) \
1176 = ctx.samdb.dns_lookup("%s.%s" % (name, zone),
1177 dns_partition=domaindns_zone_dn)
1179 # Make the DC own the DNS record, not the administrator
1180 sd_helper.modify_sd_on_dn(ctx.dns_a_dn, change_owner_sd,
1181 controls=["sd_flags:1:%d"
1182 % (security.SECINFO_OWNER
1183 | security.SECINFO_GROUP)])
1186 ctx.logger.info("Adding DNS CNAME record %s.%s for %s"
1187 % (msdcs_cname, msdcs_zone, cname_target))
1189 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1190 rec = CNameRecord(cname_target)
1191 add_rec_buf.rec = rec
1192 dns_conn.DnssrvUpdateRecord2(client_version,
1200 forestdns_zone_dn = ldb.Dn(ctx.samdb, ctx.forestdns_zone)
1201 (ctx.dns_cname_dn, ldap_record) \
1202 = ctx.samdb.dns_lookup("%s.%s" % (msdcs_cname, msdcs_zone),
1203 dns_partition=forestdns_zone_dn)
1205 # Make the DC own the DNS record, not the administrator
1206 sd_helper.modify_sd_on_dn(ctx.dns_cname_dn, change_owner_sd,
1207 controls=["sd_flags:1:%d"
1208 % (security.SECINFO_OWNER
1209 | security.SECINFO_GROUP)])
1211 ctx.logger.info("All other DNS records (like _ldap SRV records) " +
1212 "will be created samba_dnsupdate on first startup")
1214 def join_replicate_new_dns_records(ctx):
1215 for nc in (ctx.domaindns_zone, ctx.forestdns_zone):
1216 if nc in ctx.nc_list:
1217 ctx.logger.info("Replicating new DNS records in %s" % (str(nc)))
1218 ctx.repl.replicate(nc, ctx.source_dsa_invocation_id,
1219 ctx.ntds_guid, rodc=ctx.RODC,
1220 replica_flags=ctx.replica_flags,
1223 def join_finalise(ctx):
1224 """Finalise the join, mark us synchronised and setup secrets db."""
1226 # FIXME we shouldn't do this in all cases
1228 # If for some reasons we joined in another site than the one of
1229 # DC we just replicated from then we don't need to send the updatereplicateref
1230 # as replication between sites is time based and on the initiative of the
1232 ctx.logger.info("Sending DsReplicaUpdateRefs for all the replicated partitions")
1233 for nc in ctx.nc_list:
1234 ctx.send_DsReplicaUpdateRefs(nc)
1237 print("Setting RODC invocationId")
1238 ctx.local_samdb.set_invocation_id(str(ctx.invocation_id))
1239 ctx.local_samdb.set_opaque_integer("domainFunctionality",
1240 ctx.behavior_version)
1242 m.dn = ldb.Dn(ctx.local_samdb, "%s" % ctx.ntds_dn)
1243 m["invocationId"] = ldb.MessageElement(ndr_pack(ctx.invocation_id),
1244 ldb.FLAG_MOD_REPLACE,
1246 ctx.local_samdb.modify(m)
1248 # Note: as RODC the invocationId is only stored
1249 # on the RODC itself, the other DCs never see it.
1251 # Thats is why we fix up the replPropertyMetaData stamp
1252 # for the 'invocationId' attribute, we need to change
1253 # the 'version' to '0', this is what windows 2008r2 does as RODC
1255 # This means if the object on a RWDC ever gets a invocationId
1256 # attribute, it will have version '1' (or higher), which will
1257 # will overwrite the RODC local value.
1258 ctx.local_samdb.set_attribute_replmetadata_version(m.dn,
1262 ctx.logger.info("Setting isSynchronized and dsServiceName")
1264 m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
1265 m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isSynchronized")
1267 guid = ctx.ntds_guid
1268 m["dsServiceName"] = ldb.MessageElement("<GUID=%s>" % str(guid),
1269 ldb.FLAG_MOD_REPLACE, "dsServiceName")
1270 ctx.local_samdb.modify(m)
1275 secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
1277 ctx.logger.info("Setting up secrets database")
1278 secretsdb_self_join(secrets_ldb, domain=ctx.domain_name,
1280 dnsdomain=ctx.dnsdomain,
1281 netbiosname=ctx.myname,
1282 domainsid=ctx.domsid,
1283 machinepass=ctx.acct_pass,
1284 secure_channel_type=ctx.secure_channel_type,
1285 key_version_number=ctx.key_version_number)
1287 if ctx.dns_backend.startswith("BIND9_"):
1288 setup_bind9_dns(ctx.local_samdb, secrets_ldb,
1289 ctx.names, ctx.paths, ctx.lp, ctx.logger,
1290 dns_backend=ctx.dns_backend,
1291 dnspass=ctx.dnspass, os_level=ctx.behavior_version,
1292 targetdir=ctx.targetdir,
1293 key_version_number=ctx.dns_key_version_number)
1295 def join_setup_trusts(ctx):
1296 """provision the local SAM."""
1298 print("Setup domain trusts with server %s" % ctx.server)
1299 binding_options = "" # why doesn't signing work here? w2k8r2 claims no session key
1300 lsaconn = lsa.lsarpc("ncacn_np:%s[%s]" % (ctx.server, binding_options),
1303 objectAttr = lsa.ObjectAttribute()
1304 objectAttr.sec_qos = lsa.QosInfo()
1306 pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
1307 objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
1309 info = lsa.TrustDomainInfoInfoEx()
1310 info.domain_name.string = ctx.dnsdomain
1311 info.netbios_name.string = ctx.domain_name
1312 info.sid = ctx.domsid
1313 info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND
1314 info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
1315 info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST
1318 oldname = lsa.String()
1319 oldname.string = ctx.dnsdomain
1320 oldinfo = lsaconn.QueryTrustedDomainInfoByName(pol_handle, oldname,
1321 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
1322 print("Removing old trust record for %s (SID %s)" % (ctx.dnsdomain, oldinfo.info_ex.sid))
1323 lsaconn.DeleteTrustedDomain(pol_handle, oldinfo.info_ex.sid)
1324 except RuntimeError:
1327 password_blob = string_to_byte_array(ctx.trustdom_pass.encode('utf-16-le'))
1329 clear_value = drsblobs.AuthInfoClear()
1330 clear_value.size = len(password_blob)
1331 clear_value.password = password_blob
1333 clear_authentication_information = drsblobs.AuthenticationInformation()
1334 clear_authentication_information.LastUpdateTime = samba.unix2nttime(int(time.time()))
1335 clear_authentication_information.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
1336 clear_authentication_information.AuthInfo = clear_value
1338 authentication_information_array = drsblobs.AuthenticationInformationArray()
1339 authentication_information_array.count = 1
1340 authentication_information_array.array = [clear_authentication_information]
1342 outgoing = drsblobs.trustAuthInOutBlob()
1344 outgoing.current = authentication_information_array
1346 trustpass = drsblobs.trustDomainPasswords()
1347 confounder = [3] * 512
1349 for i in range(512):
1350 confounder[i] = random.randint(0, 255)
1352 trustpass.confounder = confounder
1354 trustpass.outgoing = outgoing
1355 trustpass.incoming = outgoing
1357 trustpass_blob = ndr_pack(trustpass)
1359 encrypted_trustpass = arcfour_encrypt(lsaconn.session_key, trustpass_blob)
1361 auth_blob = lsa.DATA_BUF2()
1362 auth_blob.size = len(encrypted_trustpass)
1363 auth_blob.data = string_to_byte_array(encrypted_trustpass)
1365 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
1366 auth_info.auth_blob = auth_blob
1368 trustdom_handle = lsaconn.CreateTrustedDomainEx2(pol_handle,
1371 security.SEC_STD_DELETE)
1374 "dn": "cn=%s,cn=system,%s" % (ctx.dnsforest, ctx.base_dn),
1375 "objectclass": "trustedDomain",
1376 "trustType": str(info.trust_type),
1377 "trustAttributes": str(info.trust_attributes),
1378 "trustDirection": str(info.trust_direction),
1379 "flatname": ctx.forest_domain_name,
1380 "trustPartner": ctx.dnsforest,
1381 "trustAuthIncoming": ndr_pack(outgoing),
1382 "trustAuthOutgoing": ndr_pack(outgoing),
1383 "securityIdentifier": ndr_pack(ctx.forestsid)
1385 ctx.local_samdb.add(rec)
1388 "dn": "cn=%s$,cn=users,%s" % (ctx.forest_domain_name, ctx.base_dn),
1389 "objectclass": "user",
1390 "userAccountControl": str(samba.dsdb.UF_INTERDOMAIN_TRUST_ACCOUNT),
1391 "clearTextPassword": ctx.trustdom_pass.encode('utf-16-le'),
1392 "samAccountName": "%s$" % ctx.forest_domain_name
1394 ctx.local_samdb.add(rec)
1396 def build_nc_lists(ctx):
1397 # nc_list is the list of naming context (NC) for which we will
1398 # replicate in and send a updateRef command to the partner DC
1400 # full_nc_list is the list of naming context (NC) we hold
1401 # read/write copies of. These are not subsets of each other.
1402 ctx.nc_list = [ctx.config_dn, ctx.schema_dn]
1403 ctx.full_nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn]
1405 if ctx.subdomain and ctx.dns_backend != "NONE":
1406 ctx.full_nc_list += [ctx.domaindns_zone]
1408 elif not ctx.subdomain:
1409 ctx.nc_list += [ctx.base_dn]
1411 if ctx.dns_backend != "NONE":
1412 ctx.nc_list += [ctx.domaindns_zone]
1413 ctx.nc_list += [ctx.forestdns_zone]
1414 ctx.full_nc_list += [ctx.domaindns_zone]
1415 ctx.full_nc_list += [ctx.forestdns_zone]
1418 ctx.build_nc_lists()
1420 if ctx.promote_existing:
1421 ctx.promote_possible()
1423 ctx.cleanup_old_join()
1426 ctx.join_add_objects()
1427 ctx.join_provision()
1428 ctx.join_replicate()
1430 ctx.join_add_objects2()
1431 ctx.join_provision_own_domain()
1432 ctx.join_setup_trusts()
1434 if ctx.dns_backend != "NONE":
1435 ctx.join_add_dns_records()
1436 ctx.join_replicate_new_dns_records()
1441 print("Join failed - cleaning up")
1445 # cleanup the failed join (checking we still have a live LDB
1446 # connection to the remote DC first)
1447 ctx.refresh_ldb_connection()
1448 ctx.cleanup_old_join()
1452 def join_RODC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
1453 targetdir=None, domain=None, domain_critical_only=False,
1454 machinepass=None, use_ntvfs=False, dns_backend=None,
1455 promote_existing=False, plaintext_secrets=False,
1456 backend_store=None):
1457 """Join as a RODC."""
1459 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1460 targetdir, domain, machinepass, use_ntvfs, dns_backend,
1461 promote_existing, plaintext_secrets,
1462 backend_store=backend_store)
1464 lp.set("workgroup", ctx.domain_name)
1465 logger.info("workgroup is %s" % ctx.domain_name)
1467 lp.set("realm", ctx.realm)
1468 logger.info("realm is %s" % ctx.realm)
1470 ctx.krbtgt_dn = "CN=krbtgt_%s,CN=Users,%s" % (ctx.myname, ctx.base_dn)
1472 # setup some defaults for accounts that should be replicated to this RODC
1473 ctx.never_reveal_sid = [
1474 "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_DENY),
1475 "<SID=%s>" % security.SID_BUILTIN_ADMINISTRATORS,
1476 "<SID=%s>" % security.SID_BUILTIN_SERVER_OPERATORS,
1477 "<SID=%s>" % security.SID_BUILTIN_BACKUP_OPERATORS,
1478 "<SID=%s>" % security.SID_BUILTIN_ACCOUNT_OPERATORS]
1479 ctx.reveal_sid = "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_ALLOW)
1481 mysid = ctx.get_mysid()
1482 admin_dn = "<SID=%s>" % mysid
1483 ctx.managedby = admin_dn
1485 ctx.userAccountControl = (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT |
1486 samba.dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION |
1487 samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT)
1489 ctx.SPNs.extend(["RestrictedKrbHost/%s" % ctx.myname,
1490 "RestrictedKrbHost/%s" % ctx.dnshostname])
1492 ctx.connection_dn = "CN=RODC Connection (FRS),%s" % ctx.ntds_dn
1493 ctx.secure_channel_type = misc.SEC_CHAN_RODC
1495 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING |
1496 drsuapi.DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP)
1497 ctx.domain_replica_flags = ctx.replica_flags
1498 if domain_critical_only:
1499 ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
1503 logger.info("Joined domain %s (SID %s) as an RODC" % (ctx.domain_name, ctx.domsid))
1506 def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
1507 targetdir=None, domain=None, domain_critical_only=False,
1508 machinepass=None, use_ntvfs=False, dns_backend=None,
1509 promote_existing=False, plaintext_secrets=False,
1510 backend_store=None):
1512 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1513 targetdir, domain, machinepass, use_ntvfs, dns_backend,
1514 promote_existing, plaintext_secrets,
1515 backend_store=backend_store)
1517 lp.set("workgroup", ctx.domain_name)
1518 logger.info("workgroup is %s" % ctx.domain_name)
1520 lp.set("realm", ctx.realm)
1521 logger.info("realm is %s" % ctx.realm)
1523 ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
1525 ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
1526 ctx.secure_channel_type = misc.SEC_CHAN_BDC
1528 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1529 drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1530 ctx.domain_replica_flags = ctx.replica_flags
1531 if domain_critical_only:
1532 ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
1535 logger.info("Joined domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))
1538 def join_clone(logger=None, server=None, creds=None, lp=None,
1539 targetdir=None, domain=None, include_secrets=False,
1540 dns_backend="NONE", backend_store=None):
1541 """Creates a local clone of a remote DC."""
1542 ctx = DCCloneContext(logger, server, creds, lp, targetdir=targetdir,
1543 domain=domain, dns_backend=dns_backend,
1544 include_secrets=include_secrets,
1545 backend_store=backend_store)
1547 lp.set("workgroup", ctx.domain_name)
1548 logger.info("workgroup is %s" % ctx.domain_name)
1550 lp.set("realm", ctx.realm)
1551 logger.info("realm is %s" % ctx.realm)
1554 logger.info("Cloned domain %s (SID %s)" % (ctx.domain_name, ctx.domsid))
1558 def join_subdomain(logger=None, server=None, creds=None, lp=None, site=None,
1559 netbios_name=None, targetdir=None, parent_domain=None, dnsdomain=None,
1560 netbios_domain=None, machinepass=None, adminpass=None, use_ntvfs=False,
1561 dns_backend=None, plaintext_secrets=False,
1562 backend_store=None):
1564 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1565 targetdir, parent_domain, machinepass, use_ntvfs,
1566 dns_backend, plaintext_secrets,
1567 backend_store=backend_store)
1568 ctx.subdomain = True
1569 if adminpass is None:
1570 ctx.adminpass = samba.generate_random_password(12, 32)
1572 ctx.adminpass = adminpass
1573 ctx.parent_domain_name = ctx.domain_name
1574 ctx.domain_name = netbios_domain
1575 ctx.realm = dnsdomain
1576 ctx.parent_dnsdomain = ctx.dnsdomain
1577 ctx.parent_partition_dn = ctx.get_parent_partition_dn()
1578 ctx.dnsdomain = dnsdomain
1579 ctx.partition_dn = "CN=%s,CN=Partitions,%s" % (ctx.domain_name, ctx.config_dn)
1580 ctx.naming_master = ctx.get_naming_master()
1581 if ctx.naming_master != ctx.server:
1582 logger.info("Reconnecting to naming master %s" % ctx.naming_master)
1583 ctx.server = ctx.naming_master
1584 ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
1585 session_info=system_session(),
1586 credentials=ctx.creds, lp=ctx.lp)
1587 res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=['dnsHostName'],
1589 ctx.server = res[0]["dnsHostName"]
1590 logger.info("DNS name of new naming master is %s" % ctx.server)
1592 ctx.base_dn = samba.dn_from_dns_name(dnsdomain)
1593 ctx.forestsid = ctx.domsid
1594 ctx.domsid = security.random_sid()
1596 ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain)
1597 # Windows uses 240 bytes as UTF16 so we do
1598 ctx.trustdom_pass = samba.generate_random_machine_password(120, 120)
1600 ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
1602 ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
1603 ctx.secure_channel_type = misc.SEC_CHAN_BDC
1605 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1606 drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1607 ctx.domain_replica_flags = ctx.replica_flags
1610 ctx.logger.info("Created domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))
1613 class DCCloneContext(DCJoinContext):
1614 """Clones a remote DC."""
1616 def __init__(ctx, logger=None, server=None, creds=None, lp=None,
1617 targetdir=None, domain=None, dns_backend=None,
1618 include_secrets=False, backend_store=None):
1619 super(DCCloneContext, ctx).__init__(logger, server, creds, lp,
1620 targetdir=targetdir, domain=domain,
1621 dns_backend=dns_backend,
1622 backend_store=backend_store)
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, backend_store=None):
1673 super(DCCloneAndRenameContext, ctx).__init__(logger, server, creds, lp,
1674 targetdir=targetdir,
1676 dns_backend=dns_backend,
1677 include_secrets=include_secrets,
1678 backend_store=backend_store)
1679 # store the new DN (etc) that we want the cloned DB to use
1680 ctx.new_base_dn = new_base_dn
1681 ctx.new_domain_name = new_domain_name
1682 ctx.new_realm = new_realm
1684 def create_replicator(ctx, repl_creds, binding_options):
1685 """Creates a new DRS object for managing replications"""
1687 # We want to rename all the domain objects, and the simplest way to do
1688 # this is during replication. This is because the base DN of the top-
1689 # level replicated object will flow through to all the objects below it
1690 binding_str = "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options)
1691 return drs_utils.drs_ReplicateRenamer(binding_str, ctx.lp, repl_creds,
1694 ctx.base_dn, ctx.new_base_dn)
1696 def create_non_global_lp(ctx, global_lp):
1697 '''Creates a non-global LoadParm based on the global LP's settings'''
1699 # the samba code shares a global LoadParm by default. Here we create a
1700 # new LoadParm that retains the global settings, but any changes we
1701 # make to it won't automatically affect the rest of the samba code.
1702 # The easiest way to do this is to dump the global settings to a
1703 # temporary smb.conf file, and then load the temp file into a new
1704 # non-global LoadParm
1705 fd, tmp_file = tempfile.mkstemp()
1706 global_lp.dump(False, tmp_file)
1707 local_lp = samba.param.LoadParm(filename_for_non_global_lp=tmp_file)
1711 def rename_dn(ctx, dn_str):
1712 '''Uses string substitution to replace the base DN'''
1713 old_base_dn = ctx.base_dn
1714 return re.sub('%s$' % old_base_dn, ctx.new_base_dn, dn_str)
1716 # we want to override the normal DCCloneContext's join_provision() so that
1717 # use the new domain DNs during the provision. We do this because:
1718 # - it sets up smb.conf/secrets.ldb with the new realm/workgroup values
1719 # - it sets up a default SAM DB that uses the new Schema DNs (without which
1720 # we couldn't apply the renamed DRS objects during replication)
1721 def join_provision(ctx):
1722 """Provision the local (renamed) SAM."""
1724 print("Provisioning the new (renamed) domain...")
1726 # the provision() calls make_smbconf() which uses lp.dump()/lp.load()
1727 # to create a new smb.conf. By default, it uses the global LoadParm to
1728 # do this, and so it would overwrite the realm/domain values globally.
1729 # We still need the global LoadParm to retain the old domain's details,
1730 # so we can connect to (and clone) the existing DC.
1731 # So, copy the global settings into a non-global LoadParm, which we can
1732 # then pass into provision(). This generates a new smb.conf correctly,
1733 # without overwriting the global realm/domain values just yet.
1734 non_global_lp = ctx.create_non_global_lp(ctx.lp)
1736 # do the provision with the new/renamed domain DN values
1737 presult = provision(ctx.logger, system_session(),
1738 targetdir=ctx.targetdir, samdb_fill=FILL_DRS,
1739 realm=ctx.new_realm, lp=non_global_lp,
1740 rootdn=ctx.rename_dn(ctx.root_dn), domaindn=ctx.new_base_dn,
1741 schemadn=ctx.rename_dn(ctx.schema_dn),
1742 configdn=ctx.rename_dn(ctx.config_dn),
1743 domain=ctx.new_domain_name, domainsid=ctx.domsid,
1744 serverrole="active directory domain controller",
1745 dns_backend=ctx.dns_backend,
1746 backend_store=ctx.backend_store)
1748 print("Provision OK for renamed domain DN %s" % presult.domaindn)
1749 ctx.local_samdb = presult.samdb
1750 ctx.paths = presult.paths