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
53 from samba.netcmd import CommandError
56 class DCJoinException(Exception):
58 def __init__(self, msg):
59 super(DCJoinException, self).__init__("Can't join, error: %s" % msg)
62 class DCJoinContext(object):
63 """Perform a DC join."""
65 def __init__(ctx, logger=None, server=None, creds=None, lp=None, site=None,
66 netbios_name=None, targetdir=None, domain=None,
67 machinepass=None, use_ntvfs=False, dns_backend=None,
68 promote_existing=False, plaintext_secrets=False,
69 backend_store=None, forced_local_samdb=None):
75 ctx.targetdir = targetdir
76 ctx.use_ntvfs = use_ntvfs
77 ctx.plaintext_secrets = plaintext_secrets
78 ctx.backend_store = backend_store
80 ctx.promote_existing = promote_existing
81 ctx.promote_from_dn = None
86 ctx.creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
87 ctx.net = Net(creds=ctx.creds, lp=ctx.lp)
90 ctx.forced_local_samdb = forced_local_samdb
92 if forced_local_samdb:
93 ctx.samdb = forced_local_samdb
94 ctx.server = ctx.samdb.url
97 # work out the DC's site (if not already specified)
99 ctx.site = ctx.find_dc_site(ctx.server)
101 # work out the Primary DC for the domain (as well as an
102 # appropriate site for the new DC)
103 ctx.logger.info("Finding a writeable DC for domain '%s'" % domain)
104 ctx.server = ctx.find_dc(domain)
105 ctx.logger.info("Found DC %s" % ctx.server)
106 ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
107 session_info=system_session(),
108 credentials=ctx.creds, lp=ctx.lp)
111 ctx.site = DEFAULTSITE
114 ctx.samdb.search(scope=ldb.SCOPE_BASE, attrs=[])
115 except ldb.LdbError as e:
116 (enum, estr) = e.args
117 raise DCJoinException(estr)
119 ctx.base_dn = str(ctx.samdb.get_default_basedn())
120 ctx.root_dn = str(ctx.samdb.get_root_basedn())
121 ctx.schema_dn = str(ctx.samdb.get_schema_basedn())
122 ctx.config_dn = str(ctx.samdb.get_config_basedn())
123 ctx.domsid = security.dom_sid(ctx.samdb.get_domain_sid())
124 ctx.forestsid = ctx.domsid
125 ctx.domain_name = ctx.get_domain_name()
126 ctx.forest_domain_name = ctx.get_forest_domain_name()
127 ctx.invocation_id = misc.GUID(str(uuid.uuid4()))
129 ctx.dc_ntds_dn = ctx.samdb.get_dsServiceName()
130 ctx.dc_dnsHostName = ctx.get_dnsHostName()
131 ctx.behavior_version = ctx.get_behavior_version()
133 if machinepass is not None:
134 ctx.acct_pass = machinepass
136 ctx.acct_pass = samba.generate_random_machine_password(128, 255)
138 ctx.dnsdomain = ctx.samdb.domain_dns_name()
140 # the following are all dependent on the new DC's netbios_name (which
141 # we expect to always be specified, except when cloning a DC)
143 # work out the DNs of all the objects we will be adding
144 ctx.myname = netbios_name
145 ctx.samname = "%s$" % ctx.myname
146 ctx.server_dn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (ctx.myname, ctx.site, ctx.config_dn)
147 ctx.ntds_dn = "CN=NTDS Settings,%s" % ctx.server_dn
148 ctx.acct_dn = "CN=%s,OU=Domain Controllers,%s" % (ctx.myname, ctx.base_dn)
149 ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain)
150 ctx.dnsforest = ctx.samdb.forest_dns_name()
152 topology_base = "CN=Topology,CN=Domain System Volume,CN=DFSR-GlobalSettings,CN=System,%s" % ctx.base_dn
153 if ctx.dn_exists(topology_base):
154 ctx.topology_dn = "CN=%s,%s" % (ctx.myname, topology_base)
156 ctx.topology_dn = None
158 ctx.SPNs = ["HOST/%s" % ctx.myname,
159 "HOST/%s" % ctx.dnshostname,
160 "GC/%s/%s" % (ctx.dnshostname, ctx.dnsforest)]
162 res_rid_manager = ctx.samdb.search(scope=ldb.SCOPE_BASE,
163 attrs=["rIDManagerReference"],
166 ctx.rid_manager_dn = res_rid_manager[0]["rIDManagerReference"][0]
168 ctx.domaindns_zone = 'DC=DomainDnsZones,%s' % ctx.base_dn
169 ctx.forestdns_zone = 'DC=ForestDnsZones,%s' % ctx.root_dn
171 expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.domaindns_zone)
172 res_domaindns = ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
174 base=ctx.samdb.get_partitions_dn(),
176 if dns_backend is None:
177 ctx.dns_backend = "NONE"
179 if len(res_domaindns) == 0:
180 ctx.dns_backend = "NONE"
181 print("NO DNS zone information found in source domain, not replicating DNS")
183 ctx.dns_backend = dns_backend
185 ctx.realm = ctx.dnsdomain
189 ctx.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC |
190 drsuapi.DRSUAPI_DRS_PER_SYNC |
191 drsuapi.DRSUAPI_DRS_GET_ANC |
192 drsuapi.DRSUAPI_DRS_GET_NC_SIZE |
193 drsuapi.DRSUAPI_DRS_NEVER_SYNCED)
195 # these elements are optional
196 ctx.never_reveal_sid = None
197 ctx.reveal_sid = None
198 ctx.connection_dn = None
203 ctx.subdomain = False
205 ctx.partition_dn = None
208 ctx.dns_cname_dn = None
210 # Do not normally register 127. addresses but allow override for selftest
211 ctx.force_all_ips = False
213 def del_noerror(ctx, dn, recursive=False):
216 res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_ONELEVEL, attrs=["dn"])
220 ctx.del_noerror(r.dn, recursive=True)
223 print("Deleted %s" % dn)
227 def cleanup_old_accounts(ctx, force=False):
228 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
229 expression='sAMAccountName=%s' % ldb.binary_encode(ctx.samname),
230 attrs=["msDS-krbTgtLink", "objectSID"])
235 creds = Credentials()
238 creds.set_machine_account(ctx.lp)
239 creds.set_kerberos_state(ctx.creds.get_kerberos_state())
240 machine_samdb = SamDB(url="ldap://%s" % ctx.server,
241 session_info=system_session(),
242 credentials=creds, lp=ctx.lp)
246 token_res = machine_samdb.search(scope=ldb.SCOPE_BASE, base="", attrs=["tokenGroups"])
247 if token_res[0]["tokenGroups"][0] \
248 == res[0]["objectSID"][0]:
249 raise DCJoinException("Not removing account %s which "
250 "looks like a Samba DC account "
251 "matching the password we already have. "
252 "To override, remove secrets.ldb and secrets.tdb"
255 ctx.del_noerror(res[0].dn, recursive=True)
257 if "msDS-Krbtgtlink" in res[0]:
258 new_krbtgt_dn = res[0]["msDS-Krbtgtlink"][0]
259 ctx.del_noerror(ctx.new_krbtgt_dn)
261 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
262 expression='(&(sAMAccountName=%s)(servicePrincipalName=%s))' %
263 (ldb.binary_encode("dns-%s" % ctx.myname),
264 ldb.binary_encode("dns/%s" % ctx.dnshostname)),
267 ctx.del_noerror(res[0].dn, recursive=True)
269 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
270 expression='(sAMAccountName=%s)' % ldb.binary_encode("dns-%s" % ctx.myname),
273 raise DCJoinException("Not removing account %s which looks like "
274 "a Samba DNS service account but does not "
275 "have servicePrincipalName=%s" %
276 (ldb.binary_encode("dns-%s" % ctx.myname),
277 ldb.binary_encode("dns/%s" % ctx.dnshostname)))
279 def cleanup_old_join(ctx, force=False):
280 """Remove any DNs from a previous join."""
281 # find the krbtgt link
282 if not ctx.subdomain:
283 ctx.cleanup_old_accounts(force=force)
285 if ctx.connection_dn is not None:
286 ctx.del_noerror(ctx.connection_dn)
287 if ctx.krbtgt_dn is not None:
288 ctx.del_noerror(ctx.krbtgt_dn)
289 ctx.del_noerror(ctx.ntds_dn)
290 ctx.del_noerror(ctx.server_dn, recursive=True)
292 ctx.del_noerror(ctx.topology_dn)
294 ctx.del_noerror(ctx.partition_dn)
297 binding_options = "sign"
298 lsaconn = lsa.lsarpc("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
301 objectAttr = lsa.ObjectAttribute()
302 objectAttr.sec_qos = lsa.QosInfo()
304 pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
305 objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
308 name.string = ctx.realm
309 info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
311 lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
314 name.string = ctx.forest_domain_name
315 info = lsaconn.QueryTrustedDomainInfoByName(pol_handle, name, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
317 lsaconn.DeleteTrustedDomain(pol_handle, info.info_ex.sid)
320 ctx.del_noerror(ctx.dns_a_dn)
323 ctx.del_noerror(ctx.dns_cname_dn)
325 def promote_possible(ctx):
326 """confirm that the account is just a bare NT4 BDC or a member server, so can be safely promoted"""
328 # This shouldn't happen
329 raise Exception("Can not promote into a subdomain")
331 res = ctx.samdb.search(base=ctx.samdb.get_default_basedn(),
332 expression='sAMAccountName=%s' % ldb.binary_encode(ctx.samname),
333 attrs=["msDS-krbTgtLink", "userAccountControl", "serverReferenceBL", "rIDSetReferences"])
335 raise Exception("Could not find domain member account '%s' to promote to a DC, use 'samba-tool domain join' instead'" % ctx.samname)
336 if "msDS-krbTgtLink" in res[0] or "serverReferenceBL" in res[0] or "rIDSetReferences" in res[0]:
337 raise Exception("Account '%s' appears to be an active DC, use 'samba-tool domain join' if you must re-create this account" % ctx.samname)
338 if (int(res[0]["userAccountControl"][0]) & (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT |
339 samba.dsdb.UF_SERVER_TRUST_ACCOUNT) == 0):
340 raise Exception("Account %s is not a domain member or a bare NT4 BDC, use 'samba-tool domain join' instead'" % ctx.samname)
342 ctx.promote_from_dn = res[0].dn
344 def find_dc(ctx, domain):
345 """find a writeable DC for the given domain"""
347 ctx.cldap_ret = ctx.net.finddc(domain=domain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS | nbt.NBT_SERVER_WRITABLE)
348 except NTSTATUSError as error:
349 raise CommandError("Failed to find a writeable DC for domain '%s': %s" %
350 (domain, error.args[1]))
352 raise CommandError("Failed to find a writeable DC for domain '%s'" % domain)
353 if ctx.cldap_ret.client_site is not None and ctx.cldap_ret.client_site != "":
354 ctx.site = ctx.cldap_ret.client_site
355 return ctx.cldap_ret.pdc_dns_name
357 def find_dc_site(ctx, server):
359 cldap_ret = ctx.net.finddc(address=server,
360 flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
361 if cldap_ret.client_site is not None and cldap_ret.client_site != "":
362 site = cldap_ret.client_site
365 def get_behavior_version(ctx):
366 res = ctx.samdb.search(base=ctx.base_dn, scope=ldb.SCOPE_BASE, attrs=["msDS-Behavior-Version"])
367 if "msDS-Behavior-Version" in res[0]:
368 return int(res[0]["msDS-Behavior-Version"][0])
370 return samba.dsdb.DS_DOMAIN_FUNCTION_2000
372 def get_dnsHostName(ctx):
373 res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dnsHostName"])
374 return str(res[0]["dnsHostName"][0])
376 def get_domain_name(ctx):
377 '''get netbios name of the domain from the partitions record'''
378 partitions_dn = ctx.samdb.get_partitions_dn()
379 res = ctx.samdb.search(base=partitions_dn, scope=ldb.SCOPE_ONELEVEL, attrs=["nETBIOSName"],
380 expression='ncName=%s' % ldb.binary_encode(str(ctx.samdb.get_default_basedn())))
381 return str(res[0]["nETBIOSName"][0])
383 def get_forest_domain_name(ctx):
384 '''get netbios name of the domain from the partitions record'''
385 partitions_dn = ctx.samdb.get_partitions_dn()
386 res = ctx.samdb.search(base=partitions_dn, scope=ldb.SCOPE_ONELEVEL, attrs=["nETBIOSName"],
387 expression='ncName=%s' % ldb.binary_encode(str(ctx.samdb.get_root_basedn())))
388 return str(res[0]["nETBIOSName"][0])
390 def get_parent_partition_dn(ctx):
391 '''get the parent domain partition DN from parent DNS name'''
392 res = ctx.samdb.search(base=ctx.config_dn, attrs=[],
393 expression='(&(objectclass=crossRef)(dnsRoot=%s)(systemFlags:%s:=%u))' %
394 (ldb.binary_encode(ctx.parent_dnsdomain),
395 ldb.OID_COMPARATOR_AND, samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN))
396 return str(res[0].dn)
398 def get_naming_master(ctx):
399 '''get the parent domain partition DN from parent DNS name'''
400 res = ctx.samdb.search(base='CN=Partitions,%s' % ctx.config_dn, attrs=['fSMORoleOwner'],
401 scope=ldb.SCOPE_BASE, controls=["extended_dn:1:1"])
402 if 'fSMORoleOwner' not in res[0]:
403 raise DCJoinException("Can't find naming master on partition DN %s in %s" % (ctx.partition_dn, ctx.samdb.url))
405 master_guid = str(misc.GUID(ldb.Dn(ctx.samdb, res[0]['fSMORoleOwner'][0].decode('utf8')).get_extended_component('GUID')))
407 raise DCJoinException("Can't find GUID in naming master on partition DN %s" % res[0]['fSMORoleOwner'][0])
409 master_host = '%s._msdcs.%s' % (master_guid, ctx.dnsforest)
413 '''get the SID of the connected user. Only works with w2k8 and later,
414 so only used for RODC join'''
415 res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["tokenGroups"])
416 binsid = res[0]["tokenGroups"][0]
417 return get_string(ctx.samdb.schema_format_value("objectSID", binsid))
419 def dn_exists(ctx, dn):
420 '''check if a DN exists'''
422 res = ctx.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=[])
423 except ldb.LdbError as e5:
424 (enum, estr) = e5.args
425 if enum == ldb.ERR_NO_SUCH_OBJECT:
430 def add_krbtgt_account(ctx):
431 '''RODCs need a special krbtgt account'''
432 print("Adding %s" % ctx.krbtgt_dn)
435 "objectclass": "user",
436 "useraccountcontrol": str(samba.dsdb.UF_NORMAL_ACCOUNT |
437 samba.dsdb.UF_ACCOUNTDISABLE),
438 "showinadvancedviewonly": "TRUE",
439 "description": "krbtgt for %s" % ctx.samname}
440 ctx.samdb.add(rec, ["rodc_join:1:1"])
442 # now we need to search for the samAccountName attribute on the krbtgt DN,
443 # as this will have been magically set to the krbtgt number
444 res = ctx.samdb.search(base=ctx.krbtgt_dn, scope=ldb.SCOPE_BASE, attrs=["samAccountName"])
445 ctx.krbtgt_name = res[0]["samAccountName"][0]
447 print("Got krbtgt_name=%s" % ctx.krbtgt_name)
450 m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
451 m["msDS-krbTgtLink"] = ldb.MessageElement(ctx.krbtgt_dn,
452 ldb.FLAG_MOD_REPLACE, "msDS-krbTgtLink")
455 ctx.new_krbtgt_dn = "CN=%s,CN=Users,%s" % (ctx.krbtgt_name, ctx.base_dn)
456 print("Renaming %s to %s" % (ctx.krbtgt_dn, ctx.new_krbtgt_dn))
457 ctx.samdb.rename(ctx.krbtgt_dn, ctx.new_krbtgt_dn)
459 def drsuapi_connect(ctx):
460 '''make a DRSUAPI connection to the naming master'''
461 binding_options = "seal"
462 if ctx.lp.log_level() >= 9:
463 binding_options += ",print"
464 binding_string = "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options)
465 ctx.drsuapi = drsuapi.drsuapi(binding_string, ctx.lp, ctx.creds)
466 (ctx.drsuapi_handle, ctx.bind_supported_extensions) = drs_utils.drs_DsBind(ctx.drsuapi)
468 def create_tmp_samdb(ctx):
469 '''create a temporary samdb object for schema queries'''
470 ctx.tmp_schema = Schema(ctx.domsid,
471 schemadn=ctx.schema_dn)
472 ctx.tmp_samdb = SamDB(session_info=system_session(), url=None, auto_connect=False,
473 credentials=ctx.creds, lp=ctx.lp, global_schema=False,
475 ctx.tmp_samdb.set_schema(ctx.tmp_schema)
477 def build_DsReplicaAttribute(ctx, attrname, attrvalue):
478 '''build a DsReplicaAttributeCtr object'''
479 r = drsuapi.DsReplicaAttribute()
480 r.attid = ctx.tmp_samdb.get_attid_from_lDAPDisplayName(attrname)
483 def DsAddEntry(ctx, recs):
484 '''add a record via the DRSUAPI DsAddEntry call'''
485 if ctx.drsuapi is None:
486 ctx.drsuapi_connect()
487 if ctx.tmp_samdb is None:
488 ctx.create_tmp_samdb()
492 id = drsuapi.DsReplicaObjectIdentifier()
499 if not isinstance(rec[a], list):
503 v = [x.encode('utf8') if isinstance(x, text_type) else x for x in v]
504 rattr = ctx.tmp_samdb.dsdb_DsReplicaAttribute(ctx.tmp_samdb, a, v)
507 attribute_ctr = drsuapi.DsReplicaAttributeCtr()
508 attribute_ctr.num_attributes = len(attrs)
509 attribute_ctr.attributes = attrs
511 object = drsuapi.DsReplicaObject()
512 object.identifier = id
513 object.attribute_ctr = attribute_ctr
515 list_object = drsuapi.DsReplicaObjectListItem()
516 list_object.object = object
517 objects.append(list_object)
519 req2 = drsuapi.DsAddEntryRequest2()
520 req2.first_object = objects[0]
521 prev = req2.first_object
522 for o in objects[1:]:
526 (level, ctr) = ctx.drsuapi.DsAddEntry(ctx.drsuapi_handle, 2, req2)
528 if ctr.dir_err != drsuapi.DRSUAPI_DIRERR_OK:
529 print("DsAddEntry failed with dir_err %u" % ctr.dir_err)
530 raise RuntimeError("DsAddEntry failed")
531 if ctr.extended_err[0] != werror.WERR_SUCCESS:
532 print("DsAddEntry failed with status %s info %s" % (ctr.extended_err))
533 raise RuntimeError("DsAddEntry failed")
536 raise RuntimeError("expected err_ver 1, got %u" % ctr.err_ver)
537 if ctr.err_data.status[0] != werror.WERR_SUCCESS:
538 if ctr.err_data.info is None:
539 print("DsAddEntry failed with status %s, info omitted" % (ctr.err_data.status[1]))
541 print("DsAddEntry failed with status %s info %s" % (ctr.err_data.status[1],
542 ctr.err_data.info.extended_err))
543 raise RuntimeError("DsAddEntry failed")
544 if ctr.err_data.dir_err != drsuapi.DRSUAPI_DIRERR_OK:
545 print("DsAddEntry failed with dir_err %u" % ctr.err_data.dir_err)
546 raise RuntimeError("DsAddEntry failed")
550 def join_ntdsdsa_obj(ctx):
551 '''return the ntdsdsa object to add'''
553 print("Adding %s" % ctx.ntds_dn)
556 "objectclass": "nTDSDSA",
557 "systemFlags": str(samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
558 "dMDLocation": ctx.schema_dn}
560 nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn]
562 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
563 rec["msDS-Behavior-Version"] = str(samba.dsdb.DS_DOMAIN_FUNCTION_2008_R2)
565 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
566 rec["msDS-HasDomainNCs"] = ctx.base_dn
569 rec["objectCategory"] = "CN=NTDS-DSA-RO,%s" % ctx.schema_dn
570 rec["msDS-HasFullReplicaNCs"] = ctx.full_nc_list
571 rec["options"] = "37"
573 rec["objectCategory"] = "CN=NTDS-DSA,%s" % ctx.schema_dn
574 rec["HasMasterNCs"] = []
576 if nc in ctx.full_nc_list:
577 rec["HasMasterNCs"].append(nc)
578 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
579 rec["msDS-HasMasterNCs"] = ctx.full_nc_list
581 rec["invocationId"] = ndr_pack(ctx.invocation_id)
585 def join_add_ntdsdsa(ctx):
586 '''add the ntdsdsa object'''
588 rec = ctx.join_ntdsdsa_obj()
589 if ctx.forced_local_samdb:
590 ctx.samdb.add(rec, controls=["relax:0"])
592 ctx.samdb.add(rec, ["rodc_join:1:1"])
594 ctx.DsAddEntry([rec])
596 # find the GUID of our NTDS DN
597 res = ctx.samdb.search(base=ctx.ntds_dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"])
598 ctx.ntds_guid = misc.GUID(ctx.samdb.schema_format_value("objectGUID", res[0]["objectGUID"][0]))
600 def join_add_objects(ctx, specified_sid=None):
601 '''add the various objects needed for the join'''
603 print("Adding %s" % ctx.acct_dn)
606 "objectClass": "computer",
607 "displayname": ctx.samname,
608 "samaccountname": ctx.samname,
609 "userAccountControl": str(ctx.userAccountControl | samba.dsdb.UF_ACCOUNTDISABLE),
610 "dnshostname": ctx.dnshostname}
611 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2008:
612 rec['msDS-SupportedEncryptionTypes'] = str(samba.dsdb.ENC_ALL_TYPES)
613 elif ctx.promote_existing:
614 rec['msDS-SupportedEncryptionTypes'] = []
616 rec["managedby"] = ctx.managedby
617 elif ctx.promote_existing:
618 rec["managedby"] = []
620 if ctx.never_reveal_sid:
621 rec["msDS-NeverRevealGroup"] = ctx.never_reveal_sid
622 elif ctx.promote_existing:
623 rec["msDS-NeverRevealGroup"] = []
626 rec["msDS-RevealOnDemandGroup"] = ctx.reveal_sid
627 elif ctx.promote_existing:
628 rec["msDS-RevealOnDemandGroup"] = []
631 rec["objectSid"] = ndr_pack(specified_sid)
633 if ctx.promote_existing:
634 if ctx.promote_from_dn != ctx.acct_dn:
635 ctx.samdb.rename(ctx.promote_from_dn, ctx.acct_dn)
636 ctx.samdb.modify(ldb.Message.from_dict(ctx.samdb, rec, ldb.FLAG_MOD_REPLACE))
639 if specified_sid is not None:
640 controls = ["relax:0"]
641 ctx.samdb.add(rec, controls=controls)
644 ctx.add_krbtgt_account()
647 print("Adding %s" % ctx.server_dn)
650 "objectclass": "server",
651 # windows uses 50000000 decimal for systemFlags. A windows hex/decimal mixup bug?
652 "systemFlags": str(samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_RENAME |
653 samba.dsdb.SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE |
654 samba.dsdb.SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE),
655 # windows seems to add the dnsHostName later
656 "dnsHostName": ctx.dnshostname}
659 rec["serverReference"] = ctx.acct_dn
664 # the rest is done after replication
669 ctx.join_add_ntdsdsa()
671 # Add the Replica-Locations or RO-Replica-Locations attributes
672 # TODO Is this supposed to be for the schema partition too?
673 expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.domaindns_zone)
674 domain = (ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
676 base=ctx.samdb.get_partitions_dn(),
677 expression=expr), ctx.domaindns_zone)
679 expr = "(&(objectClass=crossRef)(ncName=%s))" % ldb.binary_encode(ctx.forestdns_zone)
680 forest = (ctx.samdb.search(scope=ldb.SCOPE_ONELEVEL,
682 base=ctx.samdb.get_partitions_dn(),
683 expression=expr), ctx.forestdns_zone)
685 for part, zone in (domain, forest):
686 if zone not in ctx.nc_list:
692 attr = "msDS-NC-Replica-Locations"
694 attr = "msDS-NC-RO-Replica-Locations"
696 m[attr] = ldb.MessageElement(ctx.ntds_dn,
697 ldb.FLAG_MOD_ADD, attr)
700 if ctx.connection_dn is not None:
701 print("Adding %s" % ctx.connection_dn)
703 "dn": ctx.connection_dn,
704 "objectclass": "nTDSConnection",
705 "enabledconnection": "TRUE",
707 "fromServer": ctx.dc_ntds_dn}
711 print("Adding SPNs to %s" % ctx.acct_dn)
713 m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
714 for i in range(len(ctx.SPNs)):
715 ctx.SPNs[i] = ctx.SPNs[i].replace("$NTDSGUID", str(ctx.ntds_guid))
716 m["servicePrincipalName"] = ldb.MessageElement(ctx.SPNs,
717 ldb.FLAG_MOD_REPLACE,
718 "servicePrincipalName")
721 # The account password set operation should normally be done over
722 # LDAP. Windows 2000 DCs however allow this only with SSL
723 # connections which are hard to set up and otherwise refuse with
724 # ERR_UNWILLING_TO_PERFORM. In this case we fall back to libnet
726 print("Setting account password for %s" % ctx.samname)
728 ctx.samdb.setpassword("(&(objectClass=user)(sAMAccountName=%s))"
729 % ldb.binary_encode(ctx.samname),
731 force_change_at_next_login=False,
732 username=ctx.samname)
733 except ldb.LdbError as e2:
735 if num != ldb.ERR_UNWILLING_TO_PERFORM:
737 ctx.net.set_password(account_name=ctx.samname,
738 domain_name=ctx.domain_name,
739 newpassword=ctx.acct_pass)
741 res = ctx.samdb.search(base=ctx.acct_dn, scope=ldb.SCOPE_BASE,
742 attrs=["msDS-KeyVersionNumber",
744 if "msDS-KeyVersionNumber" in res[0]:
745 ctx.key_version_number = int(res[0]["msDS-KeyVersionNumber"][0])
747 ctx.key_version_number = None
749 ctx.new_dc_account_sid = ndr_unpack(security.dom_sid,
750 res[0]["objectSid"][0])
752 print("Enabling account")
754 m.dn = ldb.Dn(ctx.samdb, ctx.acct_dn)
755 m["userAccountControl"] = ldb.MessageElement(str(ctx.userAccountControl),
756 ldb.FLAG_MOD_REPLACE,
757 "userAccountControl")
760 if ctx.dns_backend.startswith("BIND9_"):
761 ctx.dnspass = samba.generate_random_password(128, 255)
763 recs = ctx.samdb.parse_ldif(read_and_sub_file(setup_path("provision_dns_add_samba.ldif"),
764 {"DNSDOMAIN": ctx.dnsdomain,
765 "DOMAINDN": ctx.base_dn,
766 "HOSTNAME": ctx.myname,
767 "DNSPASS_B64": b64encode(ctx.dnspass.encode('utf-16-le')).decode('utf8'),
768 "DNSNAME": ctx.dnshostname}))
769 for changetype, msg in recs:
770 assert changetype == ldb.CHANGETYPE_NONE
771 dns_acct_dn = msg["dn"]
772 print("Adding DNS account %s with dns/ SPN" % msg["dn"])
774 # Remove dns password (we will set it as a modify, as we can't do clearTextPassword over LDAP)
775 del msg["clearTextPassword"]
776 # Remove isCriticalSystemObject for similar reasons, it cannot be set over LDAP
777 del msg["isCriticalSystemObject"]
778 # Disable account until password is set
779 msg["userAccountControl"] = str(samba.dsdb.UF_NORMAL_ACCOUNT |
780 samba.dsdb.UF_ACCOUNTDISABLE)
783 except ldb.LdbError as e:
785 if num != ldb.ERR_ENTRY_ALREADY_EXISTS:
788 # The account password set operation should normally be done over
789 # LDAP. Windows 2000 DCs however allow this only with SSL
790 # connections which are hard to set up and otherwise refuse with
791 # ERR_UNWILLING_TO_PERFORM. In this case we fall back to libnet
793 print("Setting account password for dns-%s" % ctx.myname)
795 ctx.samdb.setpassword("(&(objectClass=user)(samAccountName=dns-%s))"
796 % ldb.binary_encode(ctx.myname),
798 force_change_at_next_login=False,
799 username=ctx.samname)
800 except ldb.LdbError as e3:
802 if num != ldb.ERR_UNWILLING_TO_PERFORM:
804 ctx.net.set_password(account_name="dns-%s" % ctx.myname,
805 domain_name=ctx.domain_name,
806 newpassword=ctx.dnspass)
808 res = ctx.samdb.search(base=dns_acct_dn, scope=ldb.SCOPE_BASE,
809 attrs=["msDS-KeyVersionNumber"])
810 if "msDS-KeyVersionNumber" in res[0]:
811 ctx.dns_key_version_number = int(res[0]["msDS-KeyVersionNumber"][0])
813 ctx.dns_key_version_number = None
815 def join_add_objects2(ctx):
816 """add the various objects needed for the join, for subdomains post replication"""
818 print("Adding %s" % ctx.partition_dn)
819 name_map = {'SubdomainAdmins': "%s-%s" % (str(ctx.domsid), security.DOMAIN_RID_ADMINS)}
820 sd_binary = descriptor.get_paritions_crossref_subdomain_descriptor(ctx.forestsid, name_map=name_map)
822 "dn": ctx.partition_dn,
823 "objectclass": "crossRef",
824 "objectCategory": "CN=Cross-Ref,%s" % ctx.schema_dn,
825 "nCName": ctx.base_dn,
826 "nETBIOSName": ctx.domain_name,
827 "dnsRoot": ctx.dnsdomain,
828 "trustParent": ctx.parent_partition_dn,
829 "systemFlags": str(samba.dsdb.SYSTEM_FLAG_CR_NTDS_NC |samba.dsdb.SYSTEM_FLAG_CR_NTDS_DOMAIN),
830 "ntSecurityDescriptor": sd_binary,
833 if ctx.behavior_version >= samba.dsdb.DS_DOMAIN_FUNCTION_2003:
834 rec["msDS-Behavior-Version"] = str(ctx.behavior_version)
836 rec2 = ctx.join_ntdsdsa_obj()
838 objects = ctx.DsAddEntry([rec, rec2])
839 if len(objects) != 2:
840 raise DCJoinException("Expected 2 objects from DsAddEntry")
842 ctx.ntds_guid = objects[1].guid
844 print("Replicating partition DN")
845 ctx.repl.replicate(ctx.partition_dn,
846 misc.GUID("00000000-0000-0000-0000-000000000000"),
848 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
849 replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP)
851 print("Replicating NTDS DN")
852 ctx.repl.replicate(ctx.ntds_dn,
853 misc.GUID("00000000-0000-0000-0000-000000000000"),
855 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
856 replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP)
858 def join_provision(ctx):
859 """Provision the local SAM."""
861 print("Calling bare provision")
863 smbconf = ctx.lp.configfile
865 presult = provision(ctx.logger, system_session(), smbconf=smbconf,
866 targetdir=ctx.targetdir, samdb_fill=FILL_DRS, realm=ctx.realm,
867 rootdn=ctx.root_dn, domaindn=ctx.base_dn,
868 schemadn=ctx.schema_dn, configdn=ctx.config_dn,
869 serverdn=ctx.server_dn, domain=ctx.domain_name,
870 hostname=ctx.myname, domainsid=ctx.domsid,
871 machinepass=ctx.acct_pass, serverrole="active directory domain controller",
872 sitename=ctx.site, lp=ctx.lp, ntdsguid=ctx.ntds_guid,
873 use_ntvfs=ctx.use_ntvfs, dns_backend=ctx.dns_backend,
874 plaintext_secrets=ctx.plaintext_secrets,
875 backend_store=ctx.backend_store
877 print("Provision OK for domain DN %s" % presult.domaindn)
878 ctx.local_samdb = presult.samdb
880 ctx.paths = presult.paths
881 ctx.names = presult.names
883 # Fix up the forestsid, it may be different if we are joining as a subdomain
884 ctx.names.forestsid = ctx.forestsid
886 def join_provision_own_domain(ctx):
887 """Provision the local SAM."""
889 # we now operate exclusively on the local database, which
890 # we need to reopen in order to get the newly created schema
891 # we set the transaction_index_cache_size to 200,000 to ensure it is
892 # not too small, if it's too small the performance of the join will
893 # be negatively impacted.
894 print("Reconnecting to local samdb")
895 ctx.samdb = SamDB(url=ctx.local_samdb.url,
897 "transaction_index_cache_size:200000"],
898 session_info=system_session(),
899 lp=ctx.local_samdb.lp,
901 ctx.samdb.set_invocation_id(str(ctx.invocation_id))
902 ctx.local_samdb = ctx.samdb
904 ctx.logger.info("Finding domain GUID from ncName")
905 res = ctx.local_samdb.search(base=ctx.partition_dn, scope=ldb.SCOPE_BASE, attrs=['ncName'],
906 controls=["extended_dn:1:1", "reveal_internals:0"])
908 if 'nCName' not in res[0]:
909 raise DCJoinException("Can't find naming context on partition DN %s in %s" % (ctx.partition_dn, ctx.samdb.url))
912 ctx.names.domainguid = str(misc.GUID(ldb.Dn(ctx.samdb, res[0]['ncName'][0].decode('utf8')).get_extended_component('GUID')))
914 raise DCJoinException("Can't find GUID in naming master on partition DN %s" % res[0]['ncName'][0])
916 ctx.logger.info("Got domain GUID %s" % ctx.names.domainguid)
918 ctx.logger.info("Calling own domain provision")
920 secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
922 presult = provision_fill(ctx.local_samdb, secrets_ldb,
923 ctx.logger, ctx.names, ctx.paths,
924 dom_for_fun_level=DS_DOMAIN_FUNCTION_2003,
925 targetdir=ctx.targetdir, samdb_fill=FILL_SUBDOMAIN,
926 machinepass=ctx.acct_pass, serverrole="active directory domain controller",
927 lp=ctx.lp, hostip=ctx.names.hostip, hostip6=ctx.names.hostip6,
928 dns_backend=ctx.dns_backend, adminpass=ctx.adminpass)
929 print("Provision OK for domain %s" % ctx.names.dnsdomain)
931 def create_replicator(ctx, repl_creds, binding_options):
932 '''Creates a new DRS object for managing replications'''
933 return drs_utils.drs_Replicate(
934 "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
935 ctx.lp, repl_creds, ctx.local_samdb, ctx.invocation_id)
937 def join_replicate(ctx):
938 """Replicate the SAM."""
940 print("Starting replication")
941 ctx.local_samdb.transaction_start()
943 source_dsa_invocation_id = misc.GUID(ctx.samdb.get_invocation_id())
944 if ctx.ntds_guid is None:
945 print("Using DS_BIND_GUID_W2K3")
946 destination_dsa_guid = misc.GUID(drsuapi.DRSUAPI_DS_BIND_GUID_W2K3)
948 destination_dsa_guid = ctx.ntds_guid
951 repl_creds = Credentials()
952 repl_creds.guess(ctx.lp)
953 repl_creds.set_kerberos_state(DONT_USE_KERBEROS)
954 repl_creds.set_username(ctx.samname)
955 repl_creds.set_password(ctx.acct_pass)
957 repl_creds = ctx.creds
959 binding_options = "seal"
960 if ctx.lp.log_level() >= 9:
961 binding_options += ",print"
963 repl = ctx.create_replicator(repl_creds, binding_options)
965 repl.replicate(ctx.schema_dn, source_dsa_invocation_id,
966 destination_dsa_guid, schema=True, rodc=ctx.RODC,
967 replica_flags=ctx.replica_flags)
968 repl.replicate(ctx.config_dn, source_dsa_invocation_id,
969 destination_dsa_guid, rodc=ctx.RODC,
970 replica_flags=ctx.replica_flags)
971 if not ctx.subdomain:
972 # Replicate first the critical object for the basedn
973 if not ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
974 print("Replicating critical objects from the base DN of the domain")
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 ctx.domain_replica_flags ^= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
980 repl.replicate(ctx.base_dn, source_dsa_invocation_id,
981 destination_dsa_guid, rodc=ctx.RODC,
982 replica_flags=ctx.domain_replica_flags)
983 print("Done with always replicated NC (base, config, schema)")
985 # At this point we should already have an entry in the ForestDNS
986 # and DomainDNS NC (those under CN=Partions,DC=...) in order to
987 # indicate that we hold a replica for this NC.
988 for nc in (ctx.domaindns_zone, ctx.forestdns_zone):
989 if nc in ctx.nc_list:
990 print("Replicating %s" % (str(nc)))
991 repl.replicate(nc, source_dsa_invocation_id,
992 destination_dsa_guid, rodc=ctx.RODC,
993 replica_flags=ctx.replica_flags)
996 repl.replicate(ctx.acct_dn, source_dsa_invocation_id,
997 destination_dsa_guid,
998 exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
999 repl.replicate(ctx.new_krbtgt_dn, source_dsa_invocation_id,
1000 destination_dsa_guid,
1001 exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True)
1002 elif ctx.rid_manager_dn is not None:
1003 # Try and get a RID Set if we can. This is only possible against the RID Master. Warn otherwise.
1005 repl.replicate(ctx.rid_manager_dn, source_dsa_invocation_id,
1006 destination_dsa_guid,
1007 exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC)
1008 except samba.DsExtendedError as e1:
1009 (enum, estr) = e1.args
1010 if enum == drsuapi.DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER:
1011 print("WARNING: Unable to replicate own RID Set, as server %s (the server we joined) is not the RID Master." % ctx.server)
1012 print("NOTE: This is normal and expected, Samba will be able to create users after it contacts the RID Master at first startup.")
1017 ctx.source_dsa_invocation_id = source_dsa_invocation_id
1018 ctx.destination_dsa_guid = destination_dsa_guid
1020 print("Committing SAM database")
1022 ctx.local_samdb.transaction_cancel()
1025 ctx.local_samdb.transaction_commit()
1027 # A large replication may have caused our LDB connection to the
1028 # remote DC to timeout, so check the connection is still alive
1029 ctx.refresh_ldb_connection()
1031 def refresh_ldb_connection(ctx):
1033 # query the rootDSE to check the connection
1034 ctx.samdb.search(scope=ldb.SCOPE_BASE, attrs=[])
1035 except ldb.LdbError as e:
1036 (enum, estr) = e.args
1038 # if the connection was disconnected, then reconnect
1039 if (enum == ldb.ERR_OPERATIONS_ERROR and
1040 ('NT_STATUS_CONNECTION_DISCONNECTED' in estr or
1041 'NT_STATUS_CONNECTION_RESET' in estr)):
1042 ctx.logger.warning("LDB connection disconnected. Reconnecting")
1043 ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
1044 session_info=system_session(),
1045 credentials=ctx.creds, lp=ctx.lp)
1047 raise DCJoinException(estr)
1049 def send_DsReplicaUpdateRefs(ctx, dn):
1050 r = drsuapi.DsReplicaUpdateRefsRequest1()
1051 r.naming_context = drsuapi.DsReplicaObjectIdentifier()
1052 r.naming_context.dn = str(dn)
1053 r.naming_context.guid = misc.GUID("00000000-0000-0000-0000-000000000000")
1054 r.naming_context.sid = security.dom_sid("S-0-0")
1055 r.dest_dsa_guid = ctx.ntds_guid
1056 r.dest_dsa_dns_name = "%s._msdcs.%s" % (str(ctx.ntds_guid), ctx.dnsforest)
1057 r.options = drsuapi.DRSUAPI_DRS_ADD_REF | drsuapi.DRSUAPI_DRS_DEL_REF
1059 r.options |= drsuapi.DRSUAPI_DRS_WRIT_REP
1061 if ctx.drsuapi is None:
1062 ctx.drsuapi_connect()
1064 ctx.drsuapi.DsReplicaUpdateRefs(ctx.drsuapi_handle, 1, r)
1066 def join_add_dns_records(ctx):
1067 """Remotely Add a DNS record to the target DC. We assume that if we
1068 replicate DNS that the server holds the DNS roles and can accept
1071 This avoids issues getting replication going after the DC
1072 first starts as the rest of the domain does not have to
1073 wait for samba_dnsupdate to run successfully.
1075 Specifically, we add the records implied by the DsReplicaUpdateRefs
1078 We do not just run samba_dnsupdate as we want to strictly
1079 operate against the DC we just joined:
1080 - We do not want to query another DNS server
1081 - We do not want to obtain a Kerberos ticket
1082 (as the KDC we select may not be the DC we just joined,
1083 and so may not be in sync with the password we just set)
1084 - We do not wish to set the _ldap records until we have started
1085 - We do not wish to use NTLM (the --use-samba-tool mode forces
1090 client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
1091 select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA |\
1092 dnsserver.DNS_RPC_VIEW_NO_CHILDREN
1094 zone = ctx.dnsdomain
1095 msdcs_zone = "_msdcs.%s" % ctx.dnsforest
1097 msdcs_cname = str(ctx.ntds_guid)
1098 cname_target = "%s.%s" % (name, zone)
1099 IPs = samba.interface_ips(ctx.lp, ctx.force_all_ips)
1101 ctx.logger.info("Adding %d remote DNS records for %s.%s" %
1102 (len(IPs), name, zone))
1104 binding_options = "sign"
1105 dns_conn = dnsserver.dnsserver("ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options),
1110 sd_helper = sd_utils.SDUtils(ctx.samdb)
1112 change_owner_sd = security.descriptor()
1113 change_owner_sd.owner_sid = ctx.new_dc_account_sid
1114 change_owner_sd.group_sid = security.dom_sid("%s-%d" %
1116 security.DOMAIN_RID_DCS))
1118 # TODO: Remove any old records from the primary DNS name
1121 = dns_conn.DnssrvEnumRecords2(client_version,
1131 except WERRORError as e:
1132 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1138 for record in rec.records:
1139 if record.wType == dnsp.DNS_TYPE_A or \
1140 record.wType == dnsp.DNS_TYPE_AAAA:
1142 del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1143 del_rec_buf.rec = record
1145 dns_conn.DnssrvUpdateRecord2(client_version,
1152 except WERRORError as e:
1153 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1159 if IP.find(':') != -1:
1160 ctx.logger.info("Adding DNS AAAA record %s.%s for IPv6 IP: %s"
1162 rec = AAAARecord(IP)
1164 ctx.logger.info("Adding DNS A record %s.%s for IPv4 IP: %s"
1169 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1170 add_rec_buf.rec = rec
1171 dns_conn.DnssrvUpdateRecord2(client_version,
1180 domaindns_zone_dn = ldb.Dn(ctx.samdb, ctx.domaindns_zone)
1181 (ctx.dns_a_dn, ldap_record) \
1182 = ctx.samdb.dns_lookup("%s.%s" % (name, zone),
1183 dns_partition=domaindns_zone_dn)
1185 # Make the DC own the DNS record, not the administrator
1186 sd_helper.modify_sd_on_dn(ctx.dns_a_dn, change_owner_sd,
1187 controls=["sd_flags:1:%d"
1188 % (security.SECINFO_OWNER
1189 | security.SECINFO_GROUP)])
1192 ctx.logger.info("Adding DNS CNAME record %s.%s for %s"
1193 % (msdcs_cname, msdcs_zone, cname_target))
1195 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1196 rec = CNameRecord(cname_target)
1197 add_rec_buf.rec = rec
1198 dns_conn.DnssrvUpdateRecord2(client_version,
1206 forestdns_zone_dn = ldb.Dn(ctx.samdb, ctx.forestdns_zone)
1207 (ctx.dns_cname_dn, ldap_record) \
1208 = ctx.samdb.dns_lookup("%s.%s" % (msdcs_cname, msdcs_zone),
1209 dns_partition=forestdns_zone_dn)
1211 # Make the DC own the DNS record, not the administrator
1212 sd_helper.modify_sd_on_dn(ctx.dns_cname_dn, change_owner_sd,
1213 controls=["sd_flags:1:%d"
1214 % (security.SECINFO_OWNER
1215 | security.SECINFO_GROUP)])
1217 ctx.logger.info("All other DNS records (like _ldap SRV records) " +
1218 "will be created samba_dnsupdate on first startup")
1220 def join_replicate_new_dns_records(ctx):
1221 for nc in (ctx.domaindns_zone, ctx.forestdns_zone):
1222 if nc in ctx.nc_list:
1223 ctx.logger.info("Replicating new DNS records in %s" % (str(nc)))
1224 ctx.repl.replicate(nc, ctx.source_dsa_invocation_id,
1225 ctx.ntds_guid, rodc=ctx.RODC,
1226 replica_flags=ctx.replica_flags,
1229 def join_finalise(ctx):
1230 """Finalise the join, mark us synchronised and setup secrets db."""
1232 # FIXME we shouldn't do this in all cases
1234 # If for some reasons we joined in another site than the one of
1235 # DC we just replicated from then we don't need to send the updatereplicateref
1236 # as replication between sites is time based and on the initiative of the
1238 ctx.logger.info("Sending DsReplicaUpdateRefs for all the replicated partitions")
1239 for nc in ctx.nc_list:
1240 ctx.send_DsReplicaUpdateRefs(nc)
1243 print("Setting RODC invocationId")
1244 ctx.local_samdb.set_invocation_id(str(ctx.invocation_id))
1245 ctx.local_samdb.set_opaque_integer("domainFunctionality",
1246 ctx.behavior_version)
1248 m.dn = ldb.Dn(ctx.local_samdb, "%s" % ctx.ntds_dn)
1249 m["invocationId"] = ldb.MessageElement(ndr_pack(ctx.invocation_id),
1250 ldb.FLAG_MOD_REPLACE,
1252 ctx.local_samdb.modify(m)
1254 # Note: as RODC the invocationId is only stored
1255 # on the RODC itself, the other DCs never see it.
1257 # Thats is why we fix up the replPropertyMetaData stamp
1258 # for the 'invocationId' attribute, we need to change
1259 # the 'version' to '0', this is what windows 2008r2 does as RODC
1261 # This means if the object on a RWDC ever gets a invocationId
1262 # attribute, it will have version '1' (or higher), which will
1263 # will overwrite the RODC local value.
1264 ctx.local_samdb.set_attribute_replmetadata_version(m.dn,
1268 ctx.logger.info("Setting isSynchronized and dsServiceName")
1270 m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
1271 m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isSynchronized")
1273 guid = ctx.ntds_guid
1274 m["dsServiceName"] = ldb.MessageElement("<GUID=%s>" % str(guid),
1275 ldb.FLAG_MOD_REPLACE, "dsServiceName")
1276 ctx.local_samdb.modify(m)
1281 secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
1283 ctx.logger.info("Setting up secrets database")
1284 secretsdb_self_join(secrets_ldb, domain=ctx.domain_name,
1286 dnsdomain=ctx.dnsdomain,
1287 netbiosname=ctx.myname,
1288 domainsid=ctx.domsid,
1289 machinepass=ctx.acct_pass,
1290 secure_channel_type=ctx.secure_channel_type,
1291 key_version_number=ctx.key_version_number)
1293 if ctx.dns_backend.startswith("BIND9_"):
1294 setup_bind9_dns(ctx.local_samdb, secrets_ldb,
1295 ctx.names, ctx.paths, ctx.lp, ctx.logger,
1296 dns_backend=ctx.dns_backend,
1297 dnspass=ctx.dnspass, os_level=ctx.behavior_version,
1298 targetdir=ctx.targetdir,
1299 key_version_number=ctx.dns_key_version_number)
1301 def join_setup_trusts(ctx):
1302 """provision the local SAM."""
1304 print("Setup domain trusts with server %s" % ctx.server)
1305 binding_options = "" # why doesn't signing work here? w2k8r2 claims no session key
1306 lsaconn = lsa.lsarpc("ncacn_np:%s[%s]" % (ctx.server, binding_options),
1309 objectAttr = lsa.ObjectAttribute()
1310 objectAttr.sec_qos = lsa.QosInfo()
1312 pol_handle = lsaconn.OpenPolicy2(''.decode('utf-8'),
1313 objectAttr, security.SEC_FLAG_MAXIMUM_ALLOWED)
1315 info = lsa.TrustDomainInfoInfoEx()
1316 info.domain_name.string = ctx.dnsdomain
1317 info.netbios_name.string = ctx.domain_name
1318 info.sid = ctx.domsid
1319 info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND
1320 info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
1321 info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_WITHIN_FOREST
1324 oldname = lsa.String()
1325 oldname.string = ctx.dnsdomain
1326 oldinfo = lsaconn.QueryTrustedDomainInfoByName(pol_handle, oldname,
1327 lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
1328 print("Removing old trust record for %s (SID %s)" % (ctx.dnsdomain, oldinfo.info_ex.sid))
1329 lsaconn.DeleteTrustedDomain(pol_handle, oldinfo.info_ex.sid)
1330 except RuntimeError:
1333 password_blob = string_to_byte_array(ctx.trustdom_pass.encode('utf-16-le'))
1335 clear_value = drsblobs.AuthInfoClear()
1336 clear_value.size = len(password_blob)
1337 clear_value.password = password_blob
1339 clear_authentication_information = drsblobs.AuthenticationInformation()
1340 clear_authentication_information.LastUpdateTime = samba.unix2nttime(int(time.time()))
1341 clear_authentication_information.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
1342 clear_authentication_information.AuthInfo = clear_value
1344 authentication_information_array = drsblobs.AuthenticationInformationArray()
1345 authentication_information_array.count = 1
1346 authentication_information_array.array = [clear_authentication_information]
1348 outgoing = drsblobs.trustAuthInOutBlob()
1350 outgoing.current = authentication_information_array
1352 trustpass = drsblobs.trustDomainPasswords()
1353 confounder = [3] * 512
1355 for i in range(512):
1356 confounder[i] = random.randint(0, 255)
1358 trustpass.confounder = confounder
1360 trustpass.outgoing = outgoing
1361 trustpass.incoming = outgoing
1363 trustpass_blob = ndr_pack(trustpass)
1365 encrypted_trustpass = arcfour_encrypt(lsaconn.session_key, trustpass_blob)
1367 auth_blob = lsa.DATA_BUF2()
1368 auth_blob.size = len(encrypted_trustpass)
1369 auth_blob.data = string_to_byte_array(encrypted_trustpass)
1371 auth_info = lsa.TrustDomainInfoAuthInfoInternal()
1372 auth_info.auth_blob = auth_blob
1374 trustdom_handle = lsaconn.CreateTrustedDomainEx2(pol_handle,
1377 security.SEC_STD_DELETE)
1380 "dn": "cn=%s,cn=system,%s" % (ctx.dnsforest, ctx.base_dn),
1381 "objectclass": "trustedDomain",
1382 "trustType": str(info.trust_type),
1383 "trustAttributes": str(info.trust_attributes),
1384 "trustDirection": str(info.trust_direction),
1385 "flatname": ctx.forest_domain_name,
1386 "trustPartner": ctx.dnsforest,
1387 "trustAuthIncoming": ndr_pack(outgoing),
1388 "trustAuthOutgoing": ndr_pack(outgoing),
1389 "securityIdentifier": ndr_pack(ctx.forestsid)
1391 ctx.local_samdb.add(rec)
1394 "dn": "cn=%s$,cn=users,%s" % (ctx.forest_domain_name, ctx.base_dn),
1395 "objectclass": "user",
1396 "userAccountControl": str(samba.dsdb.UF_INTERDOMAIN_TRUST_ACCOUNT),
1397 "clearTextPassword": ctx.trustdom_pass.encode('utf-16-le'),
1398 "samAccountName": "%s$" % ctx.forest_domain_name
1400 ctx.local_samdb.add(rec)
1402 def build_nc_lists(ctx):
1403 # nc_list is the list of naming context (NC) for which we will
1404 # replicate in and send a updateRef command to the partner DC
1406 # full_nc_list is the list of naming context (NC) we hold
1407 # read/write copies of. These are not subsets of each other.
1408 ctx.nc_list = [ctx.config_dn, ctx.schema_dn]
1409 ctx.full_nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn]
1411 if ctx.subdomain and ctx.dns_backend != "NONE":
1412 ctx.full_nc_list += [ctx.domaindns_zone]
1414 elif not ctx.subdomain:
1415 ctx.nc_list += [ctx.base_dn]
1417 if ctx.dns_backend != "NONE":
1418 ctx.nc_list += [ctx.domaindns_zone]
1419 ctx.nc_list += [ctx.forestdns_zone]
1420 ctx.full_nc_list += [ctx.domaindns_zone]
1421 ctx.full_nc_list += [ctx.forestdns_zone]
1424 ctx.build_nc_lists()
1426 if ctx.promote_existing:
1427 ctx.promote_possible()
1429 ctx.cleanup_old_join()
1432 ctx.join_add_objects()
1433 ctx.join_provision()
1434 ctx.join_replicate()
1436 ctx.join_add_objects2()
1437 ctx.join_provision_own_domain()
1438 ctx.join_setup_trusts()
1440 if ctx.dns_backend != "NONE":
1441 ctx.join_add_dns_records()
1442 ctx.join_replicate_new_dns_records()
1447 print("Join failed - cleaning up")
1451 # cleanup the failed join (checking we still have a live LDB
1452 # connection to the remote DC first)
1453 ctx.refresh_ldb_connection()
1454 ctx.cleanup_old_join()
1458 def join_RODC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
1459 targetdir=None, domain=None, domain_critical_only=False,
1460 machinepass=None, use_ntvfs=False, dns_backend=None,
1461 promote_existing=False, plaintext_secrets=False,
1462 backend_store=None):
1463 """Join as a RODC."""
1465 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1466 targetdir, domain, machinepass, use_ntvfs, dns_backend,
1467 promote_existing, plaintext_secrets,
1468 backend_store=backend_store)
1470 lp.set("workgroup", ctx.domain_name)
1471 logger.info("workgroup is %s" % ctx.domain_name)
1473 lp.set("realm", ctx.realm)
1474 logger.info("realm is %s" % ctx.realm)
1476 ctx.krbtgt_dn = "CN=krbtgt_%s,CN=Users,%s" % (ctx.myname, ctx.base_dn)
1478 # setup some defaults for accounts that should be replicated to this RODC
1479 ctx.never_reveal_sid = [
1480 "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_DENY),
1481 "<SID=%s>" % security.SID_BUILTIN_ADMINISTRATORS,
1482 "<SID=%s>" % security.SID_BUILTIN_SERVER_OPERATORS,
1483 "<SID=%s>" % security.SID_BUILTIN_BACKUP_OPERATORS,
1484 "<SID=%s>" % security.SID_BUILTIN_ACCOUNT_OPERATORS]
1485 ctx.reveal_sid = "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_ALLOW)
1487 mysid = ctx.get_mysid()
1488 admin_dn = "<SID=%s>" % mysid
1489 ctx.managedby = admin_dn
1491 ctx.userAccountControl = (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT |
1492 samba.dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION |
1493 samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT)
1495 ctx.SPNs.extend(["RestrictedKrbHost/%s" % ctx.myname,
1496 "RestrictedKrbHost/%s" % ctx.dnshostname])
1498 ctx.connection_dn = "CN=RODC Connection (FRS),%s" % ctx.ntds_dn
1499 ctx.secure_channel_type = misc.SEC_CHAN_RODC
1501 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING |
1502 drsuapi.DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP)
1503 ctx.domain_replica_flags = ctx.replica_flags
1504 if domain_critical_only:
1505 ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
1509 logger.info("Joined domain %s (SID %s) as an RODC" % (ctx.domain_name, ctx.domsid))
1512 def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_name=None,
1513 targetdir=None, domain=None, domain_critical_only=False,
1514 machinepass=None, use_ntvfs=False, dns_backend=None,
1515 promote_existing=False, plaintext_secrets=False,
1516 backend_store=None):
1518 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1519 targetdir, domain, machinepass, use_ntvfs, dns_backend,
1520 promote_existing, plaintext_secrets,
1521 backend_store=backend_store)
1523 lp.set("workgroup", ctx.domain_name)
1524 logger.info("workgroup is %s" % ctx.domain_name)
1526 lp.set("realm", ctx.realm)
1527 logger.info("realm is %s" % ctx.realm)
1529 ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
1531 ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
1532 ctx.secure_channel_type = misc.SEC_CHAN_BDC
1534 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1535 drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1536 ctx.domain_replica_flags = ctx.replica_flags
1537 if domain_critical_only:
1538 ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
1541 logger.info("Joined domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))
1544 def join_clone(logger=None, server=None, creds=None, lp=None,
1545 targetdir=None, domain=None, include_secrets=False,
1546 dns_backend="NONE", backend_store=None):
1547 """Creates a local clone of a remote DC."""
1548 ctx = DCCloneContext(logger, server, creds, lp, targetdir=targetdir,
1549 domain=domain, dns_backend=dns_backend,
1550 include_secrets=include_secrets,
1551 backend_store=backend_store)
1553 lp.set("workgroup", ctx.domain_name)
1554 logger.info("workgroup is %s" % ctx.domain_name)
1556 lp.set("realm", ctx.realm)
1557 logger.info("realm is %s" % ctx.realm)
1560 logger.info("Cloned domain %s (SID %s)" % (ctx.domain_name, ctx.domsid))
1564 def join_subdomain(logger=None, server=None, creds=None, lp=None, site=None,
1565 netbios_name=None, targetdir=None, parent_domain=None, dnsdomain=None,
1566 netbios_domain=None, machinepass=None, adminpass=None, use_ntvfs=False,
1567 dns_backend=None, plaintext_secrets=False,
1568 backend_store=None):
1570 ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
1571 targetdir, parent_domain, machinepass, use_ntvfs,
1572 dns_backend, plaintext_secrets,
1573 backend_store=backend_store)
1574 ctx.subdomain = True
1575 if adminpass is None:
1576 ctx.adminpass = samba.generate_random_password(12, 32)
1578 ctx.adminpass = adminpass
1579 ctx.parent_domain_name = ctx.domain_name
1580 ctx.domain_name = netbios_domain
1581 ctx.realm = dnsdomain
1582 ctx.parent_dnsdomain = ctx.dnsdomain
1583 ctx.parent_partition_dn = ctx.get_parent_partition_dn()
1584 ctx.dnsdomain = dnsdomain
1585 ctx.partition_dn = "CN=%s,CN=Partitions,%s" % (ctx.domain_name, ctx.config_dn)
1586 ctx.naming_master = ctx.get_naming_master()
1587 if ctx.naming_master != ctx.server:
1588 logger.info("Reconnecting to naming master %s" % ctx.naming_master)
1589 ctx.server = ctx.naming_master
1590 ctx.samdb = SamDB(url="ldap://%s" % ctx.server,
1591 session_info=system_session(),
1592 credentials=ctx.creds, lp=ctx.lp)
1593 res = ctx.samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=['dnsHostName'],
1595 ctx.server = res[0]["dnsHostName"]
1596 logger.info("DNS name of new naming master is %s" % ctx.server)
1598 ctx.base_dn = samba.dn_from_dns_name(dnsdomain)
1599 ctx.forestsid = ctx.domsid
1600 ctx.domsid = security.random_sid()
1602 ctx.dnshostname = "%s.%s" % (ctx.myname.lower(), ctx.dnsdomain)
1603 # Windows uses 240 bytes as UTF16 so we do
1604 ctx.trustdom_pass = samba.generate_random_machine_password(120, 120)
1606 ctx.userAccountControl = samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION
1608 ctx.SPNs.append('E3514235-4B06-11D1-AB04-00C04FC2DCD2/$NTDSGUID/%s' % ctx.dnsdomain)
1609 ctx.secure_channel_type = misc.SEC_CHAN_BDC
1611 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1612 drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1613 ctx.domain_replica_flags = ctx.replica_flags
1616 ctx.logger.info("Created domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))
1619 class DCCloneContext(DCJoinContext):
1620 """Clones a remote DC."""
1622 def __init__(ctx, logger=None, server=None, creds=None, lp=None,
1623 targetdir=None, domain=None, dns_backend=None,
1624 include_secrets=False, backend_store=None):
1625 super(DCCloneContext, ctx).__init__(logger, server, creds, lp,
1626 targetdir=targetdir, domain=domain,
1627 dns_backend=dns_backend,
1628 backend_store=backend_store)
1630 # As we don't want to create or delete these DNs, we set them to None
1631 ctx.server_dn = None
1634 ctx.myname = ctx.server.split('.')[0]
1635 ctx.ntds_guid = None
1636 ctx.rid_manager_dn = None
1639 ctx.remote_dc_ntds_guid = ctx.samdb.get_ntds_GUID()
1641 ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
1642 drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
1643 if not include_secrets:
1644 ctx.replica_flags |= drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
1645 ctx.domain_replica_flags = ctx.replica_flags
1647 def join_finalise(ctx):
1648 ctx.logger.info("Setting isSynchronized and dsServiceName")
1650 m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
1651 m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE,
1654 # We want to appear to be the server we just cloned
1655 guid = ctx.remote_dc_ntds_guid
1656 m["dsServiceName"] = ldb.MessageElement("<GUID=%s>" % str(guid),
1657 ldb.FLAG_MOD_REPLACE,
1659 ctx.local_samdb.modify(m)
1662 ctx.build_nc_lists()
1664 # When cloning a DC, we just want to provision a DC locally, then
1665 # grab the remote DC's entire DB via DRS replication
1666 ctx.join_provision()
1667 ctx.join_replicate()
1671 # Used to create a renamed backup of a DC. Renaming the domain means that the
1672 # cloned/backup DC can be started without interfering with the production DC.
1673 class DCCloneAndRenameContext(DCCloneContext):
1674 """Clones a remote DC, renaming the domain along the way."""
1676 def __init__(ctx, new_base_dn, new_domain_name, new_realm, logger=None,
1677 server=None, creds=None, lp=None, targetdir=None, domain=None,
1678 dns_backend=None, include_secrets=True, backend_store=None):
1679 super(DCCloneAndRenameContext, ctx).__init__(logger, server, creds, lp,
1680 targetdir=targetdir,
1682 dns_backend=dns_backend,
1683 include_secrets=include_secrets,
1684 backend_store=backend_store)
1685 # store the new DN (etc) that we want the cloned DB to use
1686 ctx.new_base_dn = new_base_dn
1687 ctx.new_domain_name = new_domain_name
1688 ctx.new_realm = new_realm
1690 def create_replicator(ctx, repl_creds, binding_options):
1691 """Creates a new DRS object for managing replications"""
1693 # We want to rename all the domain objects, and the simplest way to do
1694 # this is during replication. This is because the base DN of the top-
1695 # level replicated object will flow through to all the objects below it
1696 binding_str = "ncacn_ip_tcp:%s[%s]" % (ctx.server, binding_options)
1697 return drs_utils.drs_ReplicateRenamer(binding_str, ctx.lp, repl_creds,
1700 ctx.base_dn, ctx.new_base_dn)
1702 def create_non_global_lp(ctx, global_lp):
1703 '''Creates a non-global LoadParm based on the global LP's settings'''
1705 # the samba code shares a global LoadParm by default. Here we create a
1706 # new LoadParm that retains the global settings, but any changes we
1707 # make to it won't automatically affect the rest of the samba code.
1708 # The easiest way to do this is to dump the global settings to a
1709 # temporary smb.conf file, and then load the temp file into a new
1710 # non-global LoadParm
1711 fd, tmp_file = tempfile.mkstemp()
1712 global_lp.dump(False, tmp_file)
1713 local_lp = samba.param.LoadParm(filename_for_non_global_lp=tmp_file)
1717 def rename_dn(ctx, dn_str):
1718 '''Uses string substitution to replace the base DN'''
1719 old_base_dn = ctx.base_dn
1720 return re.sub('%s$' % old_base_dn, ctx.new_base_dn, dn_str)
1722 # we want to override the normal DCCloneContext's join_provision() so that
1723 # use the new domain DNs during the provision. We do this because:
1724 # - it sets up smb.conf/secrets.ldb with the new realm/workgroup values
1725 # - it sets up a default SAM DB that uses the new Schema DNs (without which
1726 # we couldn't apply the renamed DRS objects during replication)
1727 def join_provision(ctx):
1728 """Provision the local (renamed) SAM."""
1730 print("Provisioning the new (renamed) domain...")
1732 # the provision() calls make_smbconf() which uses lp.dump()/lp.load()
1733 # to create a new smb.conf. By default, it uses the global LoadParm to
1734 # do this, and so it would overwrite the realm/domain values globally.
1735 # We still need the global LoadParm to retain the old domain's details,
1736 # so we can connect to (and clone) the existing DC.
1737 # So, copy the global settings into a non-global LoadParm, which we can
1738 # then pass into provision(). This generates a new smb.conf correctly,
1739 # without overwriting the global realm/domain values just yet.
1740 non_global_lp = ctx.create_non_global_lp(ctx.lp)
1742 # do the provision with the new/renamed domain DN values
1743 presult = provision(ctx.logger, system_session(),
1744 targetdir=ctx.targetdir, samdb_fill=FILL_DRS,
1745 realm=ctx.new_realm, lp=non_global_lp,
1746 rootdn=ctx.rename_dn(ctx.root_dn), domaindn=ctx.new_base_dn,
1747 schemadn=ctx.rename_dn(ctx.schema_dn),
1748 configdn=ctx.rename_dn(ctx.config_dn),
1749 domain=ctx.new_domain_name, domainsid=ctx.domsid,
1750 serverrole="active directory domain controller",
1751 dns_backend=ctx.dns_backend,
1752 backend_store=ctx.backend_store)
1754 print("Provision OK for renamed domain DN %s" % presult.domaindn)
1755 ctx.local_samdb = presult.samdb
1756 ctx.paths = presult.paths