2 # backend code for provisioning a Samba4 server
3 # Released under the GNU GPL v2 or later
4 # Copyright Jelmer Vernooij 2007
6 # Based on the original in EJS:
7 # Copyright Andrew Tridgell 2005
10 from base64 import b64encode
16 from socket import gethostname, gethostbyname
20 from samba import Ldb, substitute_var, valid_netbios_name
21 from samba.samdb import SamDB
23 from ldb import Dn, SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
24 LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
27 DEFAULTSITE = "Default-First-Site-Name"
29 class InvalidNetbiosName(Exception):
30 def __init__(self, name):
31 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
47 self.dns_keytab = None
50 self.ldap_basedn_ldif = None
51 self.ldap_config_basedn_ldif = None
52 self.ldap_schema_basedn_ldif = None
55 def install_ok(lp, session_info, credentials):
56 """Check whether the current install seems ok."""
57 if lp.get("realm") == "":
59 ldb = Ldb(lp.get("sam database"), session_info=session_info,
60 credentials=credentials, lp=lp)
61 if len(ldb.search(ldb.Dn("(cn=Administrator)"))) != 1:
66 def findnss(nssfn, *names):
67 """Find a user or group from a list of possibilities."""
73 raise Exception("Unable to find user/group for %s" % arguments[1])
76 def open_ldb(session_info, credentials, lp, dbname):
77 assert session_info is not None
79 return Ldb(dbname, session_info=session_info, credentials=credentials,
84 return Ldb(dbname, session_info=session_info, credentials=credentials,
88 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
89 """Setup a ldb in the private dir."""
90 assert isinstance(ldif_path, str)
92 data = open(ldif_path, 'r').read()
93 if subst_vars is not None:
94 data = substitute_var(data, subst_vars)
96 assert "${" not in data
101 def setup_modify_ldif(ldb, ldif_path, substvars=None):
102 """Modify a ldb in the private dir.
104 :param ldb: LDB object.
105 :param ldif_path: LDIF file path.
106 :param substvars: Optional dictionary with substitution variables.
108 data = open(ldif_path, 'r').read()
109 if substvars is not None:
110 data = substitute_var(data, substvars)
112 assert "${" not in data
114 ldb.modify_ldif(data)
117 def setup_ldb(ldb, ldif_path, subst_vars):
118 assert ldb is not None
119 ldb.transaction_start()
121 setup_add_ldif(ldb, ldif_path, subst_vars)
123 ldb.transaction_cancel()
125 ldb.transaction_commit()
128 def setup_file(template, fname, substvars):
129 """Setup a file in the private dir."""
132 if os.path.exists(f):
135 data = open(template, 'r').read()
137 data = substitute_var(data, substvars)
138 assert not "${" in data
140 open(f, 'w').write(data)
143 def provision_paths_from_lp(lp, dnsdomain):
144 """Set the default paths for provisioning.
146 :param lp: Loadparm context.
147 :param dnsdomain: DNS Domain name
149 paths = ProvisionPaths()
150 private_dir = lp.get("private dir")
151 paths.shareconf = os.path.join(private_dir, "share.ldb")
152 paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb")
153 paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb")
154 paths.templates = os.path.join(private_dir, "templates.ldb")
155 paths.keytab = os.path.join(private_dir, "secrets.keytab")
156 paths.dns_keytab = os.path.join(private_dir, "dns.keytab")
157 paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
158 paths.winsdb = os.path.join(private_dir, "wins.ldb")
159 paths.ldap_basedn_ldif = os.path.join(private_dir,
161 paths.ldap_config_basedn_ldif = os.path.join(private_dir,
162 dnsdomain + "-config.ldif")
163 paths.ldap_schema_basedn_ldif = os.path.join(private_dir,
164 dnsdomain + "-schema.ldif")
165 paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
166 paths.phpldapadminconfig = os.path.join(private_dir,
167 "phpldapadmin-config.php")
168 paths.hklm = os.path.join(private_dir, "hklm.ldb")
169 paths.sysvol = lp.get("sysvol", "path")
170 if paths.sysvol is None:
171 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
173 paths.netlogon = lp.get("netlogon", "path")
174 if paths.netlogon is None:
175 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
180 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
182 """setup reasonable name mappings for sam names to unix names."""
183 # add some foreign sids if they are not present already
184 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
185 ldb.add_foreign(domaindn, "S-1-1-0", "World")
186 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
187 ldb.add_foreign(domaindn, "S-1-5-18", "System")
188 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
190 # some well known sids
191 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
192 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
193 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
194 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
195 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
196 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
197 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
198 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
199 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
201 # and some well known domain rids
202 ldb.setup_name_mapping(domaindn, sid + "-500", root)
203 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
204 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
205 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
206 ldb.setup_name_mapping(domaindn, sid + "-513", users)
207 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
210 def provision_become_dc(setup_dir, message, paths, lp, session_info,
212 assert session_info is not None
215 def setup_path(file):
216 return os.path.join(setup_dir, file)
217 os.path.unlink(paths.samdb)
219 message("Setting up templates db")
220 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
221 credentials=credentials, lp=lp)
223 # Also wipes the database
224 message("Setting up sam.ldb")
225 samdb = SamDB(paths.samdb, session_info=session_info,
226 credentials=credentials, lp=lp)
228 message("Setting up sam.ldb partitions")
229 setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn)
231 samdb = SamDB(paths.samdb, session_info=session_info,
232 credentials=credentials, lp=lp)
234 ldb.transaction_start()
236 message("Setting up sam.ldb attributes")
237 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
239 message("Setting up sam.ldb rootDSE")
240 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn,
241 hostname, dnsdomain, realm, rootdn, configdn,
245 message("Erasing data from partitions")
246 samdb.erase_partitions()
248 message("Setting up sam.ldb indexes")
249 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
251 samdb.transaction_cancel()
254 samdb.transaction_commit()
256 message("Setting up %s" % paths.secrets)
257 secrets_ldb = setup_secretsdb(paths.secrets, setup_path, session_info,
259 setup_ldb(secrets_ldb, setup_path("secrets_dc.ldif"),
260 { "MACHINEPASS_B64": b64encode(machinepass) })
263 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
264 if os.path.exists(path):
266 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials, lp=lp)
268 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
269 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
273 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
274 templates_ldb = SamDB(path, session_info=session_info,
275 credentials=credentials, lp=lp)
276 templates_ldb.erase()
277 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
280 def setup_registry(path, setup_path, session_info, credentials, lp):
281 reg = registry.Registry()
282 hive = registry.open_ldb(path, session_info=session_info,
283 credentials=credentials, lp_ctx=lp)
284 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
285 provision_reg = setup_path("provision.reg")
286 assert os.path.exists(provision_reg)
287 reg.diff_apply(provision_reg)
290 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
291 dnsdomain, realm, rootdn, configdn, netbiosname):
292 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
293 "SCHEMADN": schemadn,
294 "NETBIOSNAME": netbiosname,
295 "DNSDOMAIN": dnsdomain,
296 "DEFAULTSITE": DEFAULTSITE,
298 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
299 "DOMAINDN": domaindn,
301 "CONFIGDN": configdn,
302 "VERSION": samba.version(),
306 def setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn):
307 #Add modules to the list to activate them by default
308 #beware often order is important
310 # Some Known ordering constraints:
311 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
312 # - objectclass must be before password_hash, because password_hash checks
313 # that the objectclass is of type person (filled in by objectclass
314 # module when expanding the objectclass list)
315 # - partition must be last
316 # - each partition has its own module list then
317 modules_list = ["rootdse",
333 modules_list2 = ["show_deleted",
336 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
337 "SCHEMADN": schemadn,
338 "SCHEMADN_LDB": "schema.ldb",
339 "SCHEMADN_MOD2": ",objectguid",
340 "CONFIGDN": configdn,
341 "CONFIGDN_LDB": "configuration.ldb",
342 "DOMAINDN": domaindn,
343 "DOMAINDN_LDB": "users.ldb",
344 "SCHEMADN_MOD": "schema_fsmo",
345 "CONFIGDN_MOD": "naming_fsmo",
346 "CONFIGDN_MOD2": ",objectguid",
347 "DOMAINDN_MOD": "pdc_fsmo,password_hash",
348 "DOMAINDN_MOD2": ",objectguid",
349 "MODULES_LIST": ",".join(modules_list),
350 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
351 "MODULES_LIST2": ",".join(modules_list2),
355 def setup_self_join(samdb, configdn, schemadn, domaindn,
356 netbiosname, hostname, dnsdomain, machinepass, dnspass,
357 realm, domainname, domainsid, invocationid, setup_path,
358 policyguid, hostguid=None):
359 if hostguid is not None:
360 hostguid_add = "objectGUID: %s" % hostguid
364 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
365 "CONFIGDN": configdn,
366 "SCHEMADN": schemadn,
367 "DOMAINDN": domaindn,
368 "INVOCATIONID": invocationid,
369 "NETBIOSNAME": netbiosname,
370 "DEFAULTSITE": DEFAULTSITE,
371 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
372 "MACHINEPASS_B64": b64encode(machinepass),
373 "DNSPASS_B64": b64encode(dnspass),
375 "DOMAIN": domainname,
376 "HOSTGUID_ADD": hostguid_add,
377 "DNSDOMAIN": dnsdomain})
378 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
379 "POLICYGUID": policyguid,
380 "DNSDOMAIN": dnsdomain,
381 "DOMAINSID": str(domainsid),
382 "DOMAINDN": domaindn})
385 def setup_samdb(path, setup_path, session_info, credentials, lp,
386 schemadn, configdn, domaindn, dnsdomain, realm,
387 netbiosname, message, hostname, rootdn, erase,
388 domainsid, aci, rdn_dc, domainguid, policyguid,
389 domainname, blank, adminpass, krbtgtpass,
390 machinepass, hostguid, invocationid, dnspass):
391 # Also wipes the database
392 message("Setting up sam.ldb")
393 samdb = SamDB(path, session_info=session_info,
394 credentials=credentials, lp=lp)
396 message("Setting up sam.ldb partitions")
397 setup_samdb_partitions(samdb, setup_path, schemadn, configdn, domaindn)
399 samdb = SamDB(path, session_info=session_info,
400 credentials=credentials, lp=lp)
402 samdb.transaction_start()
404 message("Setting up sam.ldb attributes")
405 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
407 message("Setting up sam.ldb rootDSE")
408 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn,
409 hostname, dnsdomain, realm, rootdn, configdn,
413 message("Erasing data from partitions")
414 samdb.erase_partitions()
416 samdb.transaction_cancel()
419 samdb.transaction_commit()
421 message("Pre-loading the Samba 4 and AD schema")
422 samdb = SamDB(path, session_info=session_info,
423 credentials=credentials, lp=lp)
424 samdb.set_domain_sid(domainsid)
425 load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
427 samdb.transaction_start()
430 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
431 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
432 "DOMAINDN": domaindn,
434 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
438 message("Modifying DomainDN: " + domaindn + "")
439 if domainguid is not None:
440 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
444 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
446 "LDAPTIME": timestring(int(time.time())),
447 "DOMAINSID": str(domainsid),
448 "SCHEMADN": schemadn,
449 "NETBIOSNAME": netbiosname,
450 "DEFAULTSITE": DEFAULTSITE,
451 "CONFIGDN": configdn,
452 "POLICYGUID": policyguid,
453 "DOMAINDN": domaindn,
454 "DOMAINGUID_MOD": domainguid_mod,
457 message("Adding configuration container (permitted to fail)")
458 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
459 "CONFIGDN": configdn,
461 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
463 message("Modifying configuration container")
464 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
465 "CONFIGDN": configdn,
466 "SCHEMADN": schemadn,
469 message("Adding schema container (permitted to fail)")
470 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
471 "SCHEMADN": schemadn,
473 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
475 message("Modifying schema container")
476 setup_modify_ldif(samdb, setup_path("provision_schema_basedn_modify.ldif"), {
477 "SCHEMADN": schemadn,
478 "NETBIOSNAME": netbiosname,
479 "DEFAULTSITE": DEFAULTSITE,
480 "CONFIGDN": configdn,
483 message("Setting up sam.ldb Samba4 schema")
484 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
485 {"SCHEMADN": schemadn })
486 message("Setting up sam.ldb AD schema")
487 setup_add_ldif(samdb, setup_path("schema.ldif"),
488 {"SCHEMADN": schemadn})
490 message("Setting up sam.ldb configuration data")
491 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
492 "CONFIGDN": configdn,
493 "NETBIOSNAME": netbiosname,
494 "DEFAULTSITE": DEFAULTSITE,
495 "DNSDOMAIN": dnsdomain,
496 "DOMAIN": domainname,
497 "SCHEMADN": schemadn,
498 "DOMAINDN": domaindn,
501 message("Setting up display specifiers")
502 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
503 {"CONFIGDN": configdn})
505 message("Adding users container (permitted to fail)")
506 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
507 "DOMAINDN": domaindn})
508 message("Modifying users container")
509 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
510 "DOMAINDN": domaindn})
511 message("Adding computers container (permitted to fail)")
512 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
513 "DOMAINDN": domaindn})
514 message("Modifying computers container")
515 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
516 "DOMAINDN": domaindn})
517 message("Setting up sam.ldb data")
518 setup_add_ldif(samdb, setup_path("provision.ldif"), {
519 "DOMAINDN": domaindn,
520 "NETBIOSNAME": netbiosname,
521 "DEFAULTSITE": DEFAULTSITE,
522 "CONFIGDN": configdn,
526 message("Setting up sam.ldb users and groups")
527 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
528 "DOMAINDN": domaindn,
529 "DOMAINSID": str(domainsid),
530 "CONFIGDN": configdn,
531 "ADMINPASS_B64": b64encode(adminpass),
532 "KRBTGTPASS_B64": b64encode(krbtgtpass),
535 if lp.get("server role") == "domain controller":
536 message("Setting up self join")
537 setup_self_join(samdb, configdn=configdn, schemadn=schemadn, domaindn=domaindn,
538 invocationid=invocationid, dnspass=dnspass, netbiosname=netbiosname,
539 dnsdomain=dnsdomain, realm=realm, machinepass=machinepass,
540 domainname=domainname, domainsid=domainsid, policyguid=policyguid,
541 hostname=hostname, hostguid=hostguid, setup_path=setup_path)
543 message("Setting up sam.ldb index")
544 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
546 message("Setting up sam.ldb rootDSE marking as synchronized")
547 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
549 samdb.transaction_cancel()
552 samdb.transaction_commit()
556 def provision(lp, setup_dir, message, blank, paths, session_info,
557 credentials, ldapbackend, realm=None, domain=None, hostname=None,
558 hostip=None, domainsid=None, hostguid=None, adminpass=None,
559 krbtgtpass=None, domainguid=None, policyguid=None,
560 invocationid=None, machinepass=None, dnspass=None, root=None,
561 nobody=None, nogroup=None, users=None, wheel=None, backup=None,
562 aci=None, serverrole=None):
565 :note: caution, this wipes all existing data!
568 def setup_path(file):
569 return os.path.join(setup_dir, file)
573 if domainsid is None:
574 domainsid = security.random_sid()
575 if policyguid is None:
576 policyguid = uuid.random()
577 if invocationid is None:
578 invocationid = uuid.random()
579 if adminpass is None:
580 adminpass = misc.random_password(12)
581 if krbtgtpass is None:
582 krbtgtpass = misc.random_password(12)
583 if machinepass is None:
584 machinepass = misc.random_password(12)
586 dnspass = misc.random_password(12)
588 root = findnss(pwd.getpwnam, "root")[4]
590 nobody = findnss(pwd.getpwnam, "nobody")[4]
592 nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[2]
594 users = findnss(grp.getgrnam, "users", "guest", "other", "unknown", "usr")[2]
596 wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[2]
598 backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[2]
600 aci = "# no aci for local ldb"
601 if serverrole is None:
602 serverrole = lp.get("server role")
605 realm = lp.get("realm")
607 if lp.get("realm").upper() != realm.upper():
608 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'\n" %
609 (lp.get("realm"), realm))
611 assert realm is not None
612 realm = realm.upper()
615 domain = lp.get("workgroup")
617 if lp.get("workgroup").upper() != domain.upper():
618 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'\n",
619 lp.get("workgroup"), domain)
621 assert domain is not None
622 domain = domain.upper()
623 if not valid_netbios_name(domain):
624 raise InvalidNetbiosName(domain)
627 hostname = gethostname().split(".")[0].lower()
630 hostip = gethostbyname(hostname)
632 netbiosname = hostname.upper()
633 if not valid_netbios_name(netbiosname):
634 raise InvalidNetbiosName(netbiosname)
636 dnsdomain = realm.lower()
637 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
639 configdn = "CN=Configuration," + rootdn
640 schemadn = "CN=Schema," + configdn
642 rdn_dc = domaindn.split(",")[0][len("DC="):]
644 message("set DOMAIN SID: %s" % str(domainsid))
645 message("Provisioning for %s in realm %s" % (domain, realm))
646 message("Using administrator password: %s" % adminpass)
648 assert paths.smbconf is not None
650 # only install a new smb.conf if there isn't one there already
651 if not os.path.exists(paths.smbconf):
652 message("Setting up smb.conf")
653 if serverrole == "domain controller":
655 elif serverrole == "member":
656 smbconfsuffix = "member"
658 assert "Invalid server role setting: %s" % serverrole
659 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix), paths.smbconf, {
660 "HOSTNAME": hostname,
661 "DOMAIN_CONF": domain,
663 "SERVERROLE": serverrole,
664 "NETLOGONPATH": paths.netlogon,
665 "SYSVOLPATH": paths.sysvol,
669 # only install a new shares config db if there is none
670 if not os.path.exists(paths.shareconf):
671 message("Setting up share.ldb")
672 share_ldb = Ldb(paths.shareconf, session_info=session_info,
673 credentials=credentials, lp=lp)
674 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
676 message("Setting up secrets.ldb")
677 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
678 session_info=session_info,
679 credentials=credentials, lp=lp)
681 message("Setting up the registry")
682 setup_registry(paths.hklm, setup_path, session_info,
683 credentials=credentials, lp=lp)
685 message("Setting up templates db")
686 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
687 credentials=credentials, lp=lp)
689 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, credentials=credentials,
690 lp=lp, schemadn=schemadn, configdn=configdn, domaindn=domaindn,
691 dnsdomain=dnsdomain, netbiosname=netbiosname, realm=realm, message=message,
692 hostname=hostname, rootdn=rootdn, erase=erase, domainsid=domainsid, aci=aci,
693 rdn_dc=rdn_dc, domainguid=domainguid, policyguid=policyguid,
694 domainname=domain, blank=blank, adminpass=adminpass, krbtgtpass=krbtgtpass,
695 hostguid=hostguid, invocationid=invocationid, machinepass=machinepass,
698 if lp.get("server role") == "domain controller":
699 os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}"), 0755)
700 os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "Machine"), 0755)
701 os.makedirs(os.path.join(paths.sysvol, dnsdomain, "Policies", "{" + policyguid + "}", "User"), 0755)
702 if not os.path.isdir(paths.netlogon):
703 os.makedirs(paths.netlogon, 0755)
704 secrets_ldb = Ldb(paths.secrets, session_info=session_info, credentials=credentials, lp=lp)
705 setup_ldb(secrets_ldb, setup_path("secrets_dc.ldif"), {
706 "MACHINEPASS_B64": b64encode(machinepass),
709 "LDAPTIME": timestring(int(time.time())),
710 "DNSDOMAIN": dnsdomain,
711 "DOMAINSID": str(domainsid),
712 "SECRETS_KEYTAB": paths.keytab,
713 "NETBIOSNAME": netbiosname,
714 "SAM_LDB": paths.samdb,
715 "DNS_KEYTAB": paths.dns_keytab,
716 "DNSPASS_B64": b64encode(dnspass),
720 setup_name_mappings(samdb, str(domainsid),
721 domaindn, root=root, nobody=nobody,
722 nogroup=nogroup, wheel=wheel, users=users,
725 message("Setting up phpLDAPadmin configuration")
726 create_phplpapdadmin_config(paths.phpldapadminconfig, setup_path, paths.s4_ldapi_path)
728 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
730 if lp.get("server role") == "domain controller":
731 samdb = SamDB(paths.samdb, session_info=session_info,
732 credentials=credentials, lp=lp)
734 domainguid = samdb.searchone(Dn(samdb, domaindn), "objectGUID")
735 assert isinstance(domainguid, str)
736 hostguid = samdb.searchone(Dn(samdb, domaindn), "objectGUID",
737 expression="(&(objectClass=computer)(cn=%s))" % hostname,
739 assert isinstance(hostguid, str)
741 message("Setting up DNS zone: %s" % dnsdomain)
742 create_zone_file(paths.dns, setup_path, samdb,
743 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
744 domaindn=domaindn, dnspass=dnspass, realm=realm,
745 domainguid=domainguid, hostguid=hostguid)
746 message("Please install the zone located in %s into your DNS server" % paths.dns)
748 def create_phplpapdadmin_config(path, setup_path, s4_ldapi_path):
749 setup_file(setup_path("phpldapadmin-config.php"),
750 path, {"S4_LDAPI_URI": "ldapi://%s" % s4_ldapi_path.replace("/", "%2F")})
753 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
754 hostip, hostname, dnspass, realm, domainguid, hostguid):
755 """Write out a DNS zone file, from the info in the current database."""
757 setup_file(setup_path("provision.zone"), path, {
758 "DNSPASS_B64": b64encode(dnspass),
759 "HOSTNAME": hostname,
760 "DNSDOMAIN": dnsdomain,
763 "DOMAINGUID": domainguid,
764 "DATESTRING": time.strftime("%Y%m%d%H"),
765 "DEFAULTSITE": DEFAULTSITE,
766 "HOSTGUID": hostguid,
770 def provision_ldapbase(setup_dir, message, paths):
771 """Write out a DNS zone file, from the info in the current database."""
772 message("Setting up LDAP base entry: %s" % domaindn)
773 rdns = domaindn.split(",")
775 rdn_dc = rdns[0][len("DC="):]
777 def setup_path(file):
778 return os.path.join(setup_dir, file)
780 setup_file(setup_path("provision_basedn.ldif"),
781 paths.ldap_basedn_ldif)
783 setup_file(setup_path("provision_configuration_basedn.ldif"),
784 paths.ldap_config_basedn_ldif)
786 setup_file(setup_path("provision_schema_basedn.ldif"),
787 paths.ldap_schema_basedn_ldif, {
788 "SCHEMADN": schemadn,
789 "ACI": "# no aci for local ldb",
790 "EXTENSIBLEOBJECT": "objectClass: extensibleObject"})
792 message("Please install the LDIF located in " + paths.ldap_basedn_ldif + ", " + paths.ldap_config_basedn_ldif + " and " + paths.ldap_schema_basedn_ldif + " into your LDAP server, and re-run with --ldap-backend=ldap://my.ldap.server")
795 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
797 schema_data = open(setup_path("schema.ldif"), 'r').read()
798 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
799 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
800 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
801 head_data = substitute_var(head_data, {
802 "SCHEMADN": schemadn,
803 "NETBIOSNAME": netbiosname,
804 "CONFIGDN": configdn,
805 "DEFAULTSITE": DEFAULTSITE})
806 samdb.attach_schema_from_ldif(head_data, schema_data)
809 def join_domain(domain, netbios_name, join_type, creds):
810 ctx = NetContext(creds)
812 joindom.domain = domain
813 joindom.join_type = join_type
814 joindom.netbios_name = netbios_name
815 if not ctx.JoinDomain(joindom):
816 raise Exception("Domain Join failed: " + joindom.error_string)
819 def vampire(domain, session_info, credentials, message):
820 """Vampire a remote domain.
822 Session info and credentials are required for for
823 access to our local database (might be remote ldap)
825 ctx = NetContext(credentials)
826 machine_creds = Credentials()
827 machine_creds.set_domain(form.domain)
828 if not machine_creds.set_machine_account():
829 raise Exception("Failed to access domain join information!")
830 vampire_ctx.machine_creds = machine_creds
831 vampire_ctx.session_info = session_info
832 if not ctx.SamSyncLdb(vampire_ctx):
833 raise Exception("Migration of remote domain to Samba failed: %s " % vampire_ctx.error_string)