2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from base64 import b64encode
31 from socket import gethostname, gethostbyname
35 from auth import system_session
36 from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
37 from samba.samdb import SamDB
40 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
41 LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
43 """Functions for setting up a Samba configuration."""
45 DEFAULTSITE = "Default-First-Site-Name"
47 class InvalidNetbiosName(Exception):
48 def __init__(self, name):
49 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
66 self.dns_keytab = None
69 self.private_dir = None
72 def check_install(lp, session_info, credentials):
73 """Check whether the current install seems ok.
75 :param lp: Loadparm context
76 :param session_info: Session information
77 :param credentials: Credentials
79 if lp.get("realm") == "":
80 raise Error("Realm empty")
81 ldb = Ldb(lp.get("sam database"), session_info=session_info,
82 credentials=credentials, lp=lp)
83 if len(ldb.search("(cn=Administrator)")) != 1:
84 raise "No administrator account found"
87 def findnss(nssfn, names):
88 """Find a user or group from a list of possibilities.
90 :param nssfn: NSS Function to try (should raise KeyError if not found)
91 :param names: Names to check.
92 :return: Value return by first names list.
99 raise KeyError("Unable to find user/group %r" % names)
102 def open_ldb(session_info, credentials, lp, dbname):
103 """Open a LDB, thrashing it if it is corrupt.
105 :param session_info: auth session information
106 :param credentials: credentials
107 :param lp: Loadparm context
108 :param dbname: Path of the database to open.
109 :return: a Ldb object
111 assert session_info is not None
113 return Ldb(dbname, session_info=session_info, credentials=credentials,
118 return Ldb(dbname, session_info=session_info, credentials=credentials,
122 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
123 """Setup a ldb in the private dir.
125 :param ldb: LDB file to import data into
126 :param ldif_path: Path of the LDIF file to load
127 :param subst_vars: Optional variables to subsitute in LDIF.
129 assert isinstance(ldif_path, str)
131 data = open(ldif_path, 'r').read()
132 if subst_vars is not None:
133 data = substitute_var(data, subst_vars)
135 check_all_substituted(data)
140 def setup_modify_ldif(ldb, ldif_path, substvars=None):
141 """Modify a ldb in the private dir.
143 :param ldb: LDB object.
144 :param ldif_path: LDIF file path.
145 :param substvars: Optional dictionary with substitution variables.
147 data = open(ldif_path, 'r').read()
148 if substvars is not None:
149 data = substitute_var(data, substvars)
151 check_all_substituted(data)
153 ldb.modify_ldif(data)
156 def setup_ldb(ldb, ldif_path, subst_vars):
157 """Import a LDIF a file into a LDB handle, optionally substituting variables.
159 :note: Either all LDIF data will be added or none (using transactions).
161 :param ldb: LDB file to import into.
162 :param ldif_path: Path to the LDIF file.
163 :param subst_vars: Dictionary with substitution variables.
165 assert ldb is not None
166 ldb.transaction_start()
168 setup_add_ldif(ldb, ldif_path, subst_vars)
170 ldb.transaction_cancel()
172 ldb.transaction_commit()
175 def setup_file(template, fname, substvars):
176 """Setup a file in the private dir.
178 :param template: Path of the template file.
179 :param fname: Path of the file to create.
180 :param substvars: Substitution variables.
184 if os.path.exists(f):
187 data = open(template, 'r').read()
189 data = substitute_var(data, substvars)
190 check_all_substituted(data)
192 open(f, 'w').write(data)
195 def provision_paths_from_lp(lp, dnsdomain):
196 """Set the default paths for provisioning.
198 :param lp: Loadparm context.
199 :param dnsdomain: DNS Domain name
201 paths = ProvisionPaths()
202 paths.private_dir = lp.get("private dir")
203 paths.keytab = "secrets.keytab"
204 paths.dns_keytab = "dns.keytab"
206 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
207 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
208 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
209 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
210 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
211 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
212 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
213 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
214 paths.smbconf = os.path.join(paths.private_dir, "smb.conf")
215 paths.phpldapadminconfig = os.path.join(paths.private_dir,
216 "phpldapadmin-config.php")
217 paths.hklm = "hklm.ldb"
218 paths.hkcr = "hkcr.ldb"
219 paths.hkcu = "hkcu.ldb"
220 paths.hku = "hku.ldb"
221 paths.hkpd = "hkpd.ldb"
222 paths.hkpt = "hkpt.ldb"
224 paths.sysvol = lp.get("sysvol", "path")
225 if paths.sysvol is None:
226 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
228 paths.netlogon = lp.get("netlogon", "path")
229 if paths.netlogon is None:
230 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
235 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
237 """setup reasonable name mappings for sam names to unix names.
239 :param ldb: SamDB object.
240 :param sid: The domain sid.
241 :param domaindn: The domain DN.
242 :param root: Name of the UNIX root user.
243 :param nobody: Name of the UNIX nobody user.
244 :param nogroup: Name of the unix nobody group.
245 :param users: Name of the unix users group.
246 :param wheel: Name of the wheel group (users that can become root).
247 :param backup: Name of the backup group."""
248 # add some foreign sids if they are not present already
249 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
250 ldb.add_foreign(domaindn, "S-1-1-0", "World")
251 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
252 ldb.add_foreign(domaindn, "S-1-5-18", "System")
253 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
255 # some well known sids
256 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
257 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
258 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
259 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
260 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
261 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
262 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
263 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
264 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
266 # and some well known domain rids
267 ldb.setup_name_mapping(domaindn, sid + "-500", root)
268 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
269 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
270 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
271 ldb.setup_name_mapping(domaindn, sid + "-513", users)
272 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
275 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
276 credentials, configdn, schemadn, domaindn,
277 hostname, netbiosname, dnsdomain, realm,
278 rootdn, serverrole, sitename, ldap_backend=None,
279 ldap_backend_type=None, erase=False):
280 """Setup the partitions for the SAM database.
282 Alternatively, provision() may call this, and then populate the database.
284 :note: This will wipe the Sam Database!
286 :note: This function always removes the local SAM LDB file. The erase
287 parameter controls whether to erase the existing data, which
288 may not be stored locally but in LDAP.
290 assert session_info is not None
292 samdb = SamDB(samdb_path, session_info=session_info,
293 credentials=credentials, lp=lp)
299 os.unlink(samdb_path)
301 samdb = SamDB(samdb_path, session_info=session_info,
302 credentials=credentials, lp=lp)
304 #Add modules to the list to activate them by default
305 #beware often order is important
307 # Some Known ordering constraints:
308 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
309 # - objectclass must be before password_hash, because password_hash checks
310 # that the objectclass is of type person (filled in by objectclass
311 # module when expanding the objectclass list)
312 # - partition must be last
313 # - each partition has its own module list then
314 modules_list = ["rootdse",
330 modules_list2 = ["show_deleted",
333 domaindn_ldb = "users.ldb"
334 if ldap_backend is not None:
335 domaindn_ldb = ldap_backend
336 configdn_ldb = "configuration.ldb"
337 if ldap_backend is not None:
338 configdn_ldb = ldap_backend
339 schemadn_ldb = "schema.ldb"
340 if ldap_backend is not None:
341 schema_ldb = ldap_backend
342 schemadn_ldb = ldap_backend
344 if ldap_backend_type == "fedora-ds":
345 backend_modules = ["nsuniqueid", "paged_searches"]
346 # We can handle linked attributes here, as we don't have directory-side subtree operations
347 tdb_modules_list = ["linked_attributes"]
348 elif ldap_backend_type == "openldap":
349 backend_modules = ["normalise", "entryuuid", "paged_searches"]
350 # OpenLDAP handles subtree renames, so we don't want to do any of these things
351 tdb_modules_list = None
352 elif serverrole == "domain controller":
353 backend_modules = ["repl_meta_data"]
355 backend_modules = ["objectguid"]
357 if tdb_modules_list is None:
358 tdb_modules_list_as_string = ""
360 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
362 samdb.transaction_start()
364 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
365 "SCHEMADN": schemadn,
366 "SCHEMADN_LDB": schemadn_ldb,
367 "SCHEMADN_MOD2": ",objectguid",
368 "CONFIGDN": configdn,
369 "CONFIGDN_LDB": configdn_ldb,
370 "DOMAINDN": domaindn,
371 "DOMAINDN_LDB": domaindn_ldb,
372 "SCHEMADN_MOD": "schema_fsmo,instancetype",
373 "CONFIGDN_MOD": "naming_fsmo,instancetype",
374 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
375 "MODULES_LIST": ",".join(modules_list),
376 "TDB_MODULES_LIST": tdb_modules_list_as_string,
377 "MODULES_LIST2": ",".join(modules_list2),
378 "BACKEND_MOD": ",".join(backend_modules),
382 samdb.transaction_cancel()
385 samdb.transaction_commit()
387 samdb = SamDB(samdb_path, session_info=session_info,
388 credentials=credentials, lp=lp)
390 samdb.transaction_start()
392 message("Setting up sam.ldb attributes")
393 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
395 message("Setting up sam.ldb rootDSE")
396 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
397 dnsdomain, realm, rootdn, configdn, netbiosname,
401 message("Erasing data from partitions")
402 samdb.erase_partitions()
405 samdb.transaction_cancel()
408 samdb.transaction_commit()
413 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
414 netbiosname, domainsid, keytab_path, samdb_url,
415 dns_keytab_path, dnspass, machinepass):
416 """Add DC-specific bits to a secrets database.
418 :param secretsdb: Ldb Handle to the secrets database
419 :param setup_path: Setup path function
420 :param machinepass: Machine password
422 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
423 "MACHINEPASS_B64": b64encode(machinepass),
426 "DNSDOMAIN": dnsdomain,
427 "DOMAINSID": str(domainsid),
428 "SECRETS_KEYTAB": keytab_path,
429 "NETBIOSNAME": netbiosname,
430 "SAM_LDB": samdb_url,
431 "DNS_KEYTAB": dns_keytab_path,
432 "DNSPASS_B64": b64encode(dnspass),
436 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
437 """Setup the secrets database.
439 :param path: Path to the secrets database.
440 :param setup_path: Get the path to a setup file.
441 :param session_info: Session info.
442 :param credentials: Credentials
443 :param lp: Loadparm context
444 :return: LDB handle for the created secrets database
446 if os.path.exists(path):
448 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
451 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
452 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
454 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
458 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
459 """Setup the templates database.
461 :param path: Path to the database.
462 :param setup_path: Function for obtaining the path to setup files.
463 :param session_info: Session info
464 :param credentials: Credentials
465 :param lp: Loadparm context
467 templates_ldb = SamDB(path, session_info=session_info,
468 credentials=credentials, lp=lp)
469 templates_ldb.erase()
470 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
473 def setup_registry(path, setup_path, session_info, credentials, lp):
474 """Setup the registry.
476 :param path: Path to the registry database
477 :param setup_path: Function that returns the path to a setup.
478 :param session_info: Session information
479 :param credentials: Credentials
480 :param lp: Loadparm context
482 reg = registry.Registry()
483 hive = registry.open_ldb(path, session_info=session_info,
484 credentials=credentials, lp_ctx=lp)
485 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
486 provision_reg = setup_path("provision.reg")
487 assert os.path.exists(provision_reg)
488 reg.diff_apply(provision_reg)
490 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
491 """Setup the idmap database.
493 :param path: path to the idmap database
494 :param setup_path: Function that returns a path to a setup file
495 :param session_info: Session information
496 :param credentials: Credentials
497 :param lp: Loadparm context
499 if os.path.exists(path):
502 idmap_ldb = Ldb(path, session_info=session_info, credentials=credentials,
506 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
509 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
510 dnsdomain, realm, rootdn, configdn, netbiosname,
512 """Setup the SamDB rootdse.
514 :param samdb: Sam Database handle
515 :param setup_path: Obtain setup path
517 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
518 "SCHEMADN": schemadn,
519 "NETBIOSNAME": netbiosname,
520 "DNSDOMAIN": dnsdomain,
521 "DEFAULTSITE": sitename,
523 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
524 "DOMAINDN": domaindn,
526 "CONFIGDN": configdn,
527 "VERSION": samba.version(),
531 def setup_self_join(samdb, configdn, schemadn, domaindn,
532 netbiosname, hostname, dnsdomain, machinepass, dnspass,
533 realm, domainname, domainsid, invocationid, setup_path,
534 policyguid, sitename, hostguid=None):
535 """Join a host to its own domain."""
536 if hostguid is not None:
537 hostguid_add = "objectGUID: %s" % hostguid
541 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
542 "CONFIGDN": configdn,
543 "SCHEMADN": schemadn,
544 "DOMAINDN": domaindn,
545 "INVOCATIONID": invocationid,
546 "NETBIOSNAME": netbiosname,
547 "DEFAULTSITE": sitename,
548 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
549 "MACHINEPASS_B64": b64encode(machinepass),
550 "DNSPASS_B64": b64encode(dnspass),
552 "DOMAIN": domainname,
553 "HOSTGUID_ADD": hostguid_add,
554 "DNSDOMAIN": dnsdomain})
555 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
556 "POLICYGUID": policyguid,
557 "DNSDOMAIN": dnsdomain,
558 "DOMAINSID": str(domainsid),
559 "DOMAINDN": domaindn})
562 def setup_samdb(path, setup_path, session_info, credentials, lp,
563 schemadn, configdn, domaindn, dnsdomain, realm,
564 netbiosname, message, hostname, rootdn,
565 domainsid, aci, domainguid, policyguid,
566 domainname, fill, adminpass, krbtgtpass,
567 machinepass, hostguid, invocationid, dnspass,
568 serverrole, sitename, ldap_backend=None,
569 ldap_backend_type=None):
570 """Setup a complete SAM Database.
572 :note: This will wipe the main SAM database file!
575 assert serverrole in ("domain controller", "member server")
577 erase = (fill != FILL_DRS)
579 # Also wipes the database
580 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
581 domaindn=domaindn, message=message, lp=lp,
582 credentials=credentials, session_info=session_info,
583 hostname=hostname, netbiosname=netbiosname,
584 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
585 ldap_backend=ldap_backend, serverrole=serverrole,
586 ldap_backend_type=ldap_backend_type, erase=erase,
589 samdb = SamDB(path, session_info=session_info,
590 credentials=credentials, lp=lp)
593 # We want to finish here, but setup the index before we do so
594 message("Setting up sam.ldb index")
595 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
598 message("Pre-loading the Samba 4 and AD schema")
599 samdb = SamDB(path, session_info=session_info,
600 credentials=credentials, lp=lp)
601 samdb.set_domain_sid(domainsid)
602 if serverrole == "domain controller":
603 samdb.set_invocation_id(invocationid)
605 load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename)
607 samdb.transaction_start()
610 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
611 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
612 "DOMAINDN": domaindn,
616 message("Modifying DomainDN: " + domaindn + "")
617 if domainguid is not None:
618 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
622 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
623 "LDAPTIME": timestring(int(time.time())),
624 "DOMAINSID": str(domainsid),
625 "SCHEMADN": schemadn,
626 "NETBIOSNAME": netbiosname,
627 "DEFAULTSITE": sitename,
628 "CONFIGDN": configdn,
629 "POLICYGUID": policyguid,
630 "DOMAINDN": domaindn,
631 "DOMAINGUID_MOD": domainguid_mod,
634 message("Adding configuration container (permitted to fail)")
635 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
636 "CONFIGDN": configdn,
638 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
640 message("Modifying configuration container")
641 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
642 "CONFIGDN": configdn,
643 "SCHEMADN": schemadn,
646 message("Adding schema container (permitted to fail)")
647 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
648 "SCHEMADN": schemadn,
650 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
652 message("Modifying schema container")
653 setup_modify_ldif(samdb,
654 setup_path("provision_schema_basedn_modify.ldif"), {
655 "SCHEMADN": schemadn,
656 "NETBIOSNAME": netbiosname,
657 "DEFAULTSITE": sitename,
658 "CONFIGDN": configdn,
661 message("Setting up sam.ldb Samba4 schema")
662 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
663 {"SCHEMADN": schemadn })
664 message("Setting up sam.ldb AD schema")
665 setup_add_ldif(samdb, setup_path("schema.ldif"),
666 {"SCHEMADN": schemadn})
668 message("Setting up sam.ldb configuration data")
669 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
670 "CONFIGDN": configdn,
671 "NETBIOSNAME": netbiosname,
672 "DEFAULTSITE": sitename,
673 "DNSDOMAIN": dnsdomain,
674 "DOMAIN": domainname,
675 "SCHEMADN": schemadn,
676 "DOMAINDN": domaindn,
679 message("Setting up display specifiers")
680 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
681 {"CONFIGDN": configdn})
683 message("Adding users container (permitted to fail)")
684 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
685 "DOMAINDN": domaindn})
686 message("Modifying users container")
687 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
688 "DOMAINDN": domaindn})
689 message("Adding computers container (permitted to fail)")
690 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
691 "DOMAINDN": domaindn})
692 message("Modifying computers container")
693 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
694 "DOMAINDN": domaindn})
695 message("Setting up sam.ldb data")
696 setup_add_ldif(samdb, setup_path("provision.ldif"), {
697 "DOMAINDN": domaindn,
698 "NETBIOSNAME": netbiosname,
699 "DEFAULTSITE": sitename,
700 "CONFIGDN": configdn,
703 if fill == FILL_FULL:
704 message("Setting up sam.ldb users and groups")
705 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
706 "DOMAINDN": domaindn,
707 "DOMAINSID": str(domainsid),
708 "CONFIGDN": configdn,
709 "ADMINPASS_B64": b64encode(adminpass),
710 "KRBTGTPASS_B64": b64encode(krbtgtpass),
713 if serverrole == "domain controller":
714 message("Setting up self join")
715 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
716 domaindn=domaindn, invocationid=invocationid,
717 dnspass=dnspass, netbiosname=netbiosname,
718 dnsdomain=dnsdomain, realm=realm,
719 machinepass=machinepass, domainname=domainname,
720 domainsid=domainsid, policyguid=policyguid,
721 hostname=hostname, hostguid=hostguid,
722 setup_path=setup_path, sitename=sitename)
724 #We want to setup the index last, as adds are faster unindexed
725 message("Setting up sam.ldb index")
726 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
728 samdb.transaction_cancel()
731 samdb.transaction_commit()
736 FILL_NT4SYNC = "NT4SYNC"
739 def provision(setup_dir, message, session_info,
740 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
741 rootdn=None, domaindn=None, schemadn=None, configdn=None,
742 domain=None, hostname=None, hostip=None, domainsid=None,
743 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
744 policyguid=None, invocationid=None, machinepass=None,
745 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
746 wheel=None, backup=None, aci=None, serverrole=None,
747 ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
750 :note: caution, this wipes all existing data!
753 def setup_path(file):
754 return os.path.join(setup_dir, file)
756 if domainsid is None:
757 domainsid = security.random_sid()
758 if policyguid is None:
759 policyguid = uuid.random()
760 if adminpass is None:
761 adminpass = misc.random_password(12)
762 if krbtgtpass is None:
763 krbtgtpass = misc.random_password(12)
764 if machinepass is None:
765 machinepass = misc.random_password(12)
767 dnspass = misc.random_password(12)
769 root = findnss(pwd.getpwnam, ["root"])[0]
771 nobody = findnss(pwd.getpwnam, ["nobody"])[0]
773 nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
775 users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown",
778 wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
780 backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
782 aci = "# no aci for local ldb"
784 hostname = gethostname().split(".")[0].lower()
787 hostip = gethostbyname(hostname)
789 netbiosname = hostname.upper()
790 if not valid_netbios_name(netbiosname):
791 raise InvalidNetbiosName(netbiosname)
793 if targetdir is not None:
794 if not os.path.exists(targetdir):
796 if not os.path.exists(os.path.join(targetdir, "etc")):
797 os.mkdir(os.path.join(targetdir, "etc"))
800 smbconf = os.path.join(targetdir, os.path.join("etc", "smb.conf"))
802 # only install a new smb.conf if there isn't one there already
803 if not os.path.exists(smbconf):
804 message("Setting up smb.conf")
805 assert serverrole is not None
806 if serverrole == "domain controller":
808 elif serverrole == "member server":
809 smbconfsuffix = "member"
811 assert domain is not None
812 assert realm is not None
814 default_lp = param.LoadParm()
815 #Load non-existant file
816 default_lp.load(smbconf)
818 if targetdir is not None:
819 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
820 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
822 default_lp.set("lock dir", os.path.abspath(targetdir))
824 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
825 netlogon = os.path.join(os.path.join(sysvol, "scripts"))
827 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
829 "HOSTNAME": hostname,
830 "DOMAIN_CONF": domain,
832 "SERVERROLE": serverrole,
833 "NETLOGONPATH": netlogon,
834 "SYSVOLPATH": sysvol,
835 "PRIVATEDIR_LINE": privatedir_line,
836 "LOCKDIR_LINE": lockdir_line
839 lp = param.LoadParm()
842 if serverrole is None:
843 serverrole = lp.get("server role")
844 assert serverrole in ("domain controller", "member server")
845 if invocationid is None and serverrole == "domain controller":
846 invocationid = uuid.random()
849 realm = lp.get("realm")
851 assert realm is not None
852 realm = realm.upper()
854 dnsdomain = realm.lower()
856 paths = provision_paths_from_lp(lp, dnsdomain)
858 if targetdir is not None:
859 if not os.path.exists(paths.private_dir):
860 os.mkdir(paths.private_dir)
862 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
864 if ldap_backend == "ldapi":
865 # provision-backend will set this path suggested slapd command line / fedorads.inf
866 ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
868 if serverrole == "domain controller":
870 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
872 domain = lp.get("workgroup")
874 if lp.get("workgroup").upper() != domain.upper():
875 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
876 lp.get("workgroup"), domain)
878 assert domain is not None
879 domain = domain.upper()
880 if not valid_netbios_name(domain):
881 raise InvalidNetbiosName(domain)
884 domaindn = "CN=" + netbiosname
891 configdn = "CN=Configuration," + rootdn
893 schemadn = "CN=Schema," + configdn
895 message("set DOMAIN SID: %s" % str(domainsid))
896 message("Provisioning for %s in realm %s" % (domain, realm))
897 message("Using administrator password: %s" % adminpass)
899 if lp.get("realm").upper() != realm.upper():
900 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
901 (lp.get("realm"), realm))
903 # only install a new shares config db if there is none
904 if not os.path.exists(paths.shareconf):
905 message("Setting up share.ldb")
906 share_ldb = Ldb(paths.shareconf, session_info=session_info,
907 credentials=credentials, lp=lp)
908 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
911 message("Setting up secrets.ldb")
912 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
913 session_info=session_info,
914 credentials=credentials, lp=lp)
916 message("Setting up the registry")
917 setup_registry(paths.hklm, setup_path, session_info,
918 credentials=credentials, lp=lp)
920 message("Setting up templates db")
921 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
922 credentials=credentials, lp=lp)
924 message("Setting up idmap db")
925 setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
926 credentials=credentials, lp=lp)
928 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
929 credentials=credentials, lp=lp, schemadn=schemadn,
930 configdn=configdn, domaindn=domaindn,
931 dnsdomain=dnsdomain, netbiosname=netbiosname,
932 realm=realm, message=message, hostname=hostname,
933 rootdn=rootdn, domainsid=domainsid,
934 aci=aci, domainguid=domainguid, policyguid=policyguid,
935 domainname=domain, fill=samdb_fill,
936 adminpass=adminpass, krbtgtpass=krbtgtpass,
937 hostguid=hostguid, invocationid=invocationid,
938 machinepass=machinepass, dnspass=dnspass,
939 serverrole=serverrole, ldap_backend=ldap_backend,
940 ldap_backend_type=ldap_backend_type, sitename=sitename)
942 if lp.get("server role") == "domain controller":
943 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
944 "{" + policyguid + "}")
945 os.makedirs(policy_path, 0755)
946 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
947 os.makedirs(os.path.join(policy_path, "User"), 0755)
948 if not os.path.isdir(paths.netlogon):
949 os.makedirs(paths.netlogon, 0755)
950 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
951 credentials=credentials, lp=lp)
952 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
953 netbiosname=netbiosname, domainsid=domainsid,
954 keytab_path=paths.keytab, samdb_url=paths.samdb,
955 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
956 machinepass=machinepass, dnsdomain=dnsdomain)
958 if samdb_fill == FILL_FULL:
959 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
960 nobody=nobody, nogroup=nogroup, wheel=wheel,
961 users=users, backup=backup)
963 message("Setting up sam.ldb rootDSE marking as synchronized")
964 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
966 # Only make a zone file on the first DC, it should be replicated with DNS replication
967 if serverrole == "domain controller":
968 samdb = SamDB(paths.samdb, session_info=session_info,
969 credentials=credentials, lp=lp)
971 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
972 assert isinstance(domainguid, str)
973 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
974 expression="(&(objectClass=computer)(cn=%s))" % hostname,
976 assert isinstance(hostguid, str)
978 message("Setting up DNS zone: %s" % dnsdomain)
979 create_zone_file(paths.dns, setup_path, samdb,
980 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
981 domaindn=domaindn, dnspass=dnspass, realm=realm,
982 domainguid=domainguid, hostguid=hostguid)
983 message("Please install the zone located in %s into your DNS server" % paths.dns)
985 message("Setting up phpLDAPadmin configuration")
986 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
989 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
993 def provision_become_dc(setup_dir=None,
994 smbconf=None, targetdir=None, realm=None,
995 rootdn=None, domaindn=None, schemadn=None, configdn=None,
996 domain=None, hostname=None, domainsid=None,
997 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
998 policyguid=None, invocationid=None, machinepass=None,
999 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1000 wheel=None, backup=None, aci=None, serverrole=None,
1001 ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
1004 """print a message if quiet is not set."""
1007 provision(setup_dir, message, system_session(), None,
1008 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1009 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn,
1010 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1013 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1014 """Create a PHP LDAP admin configuration file.
1016 :param path: Path to write the configuration to.
1017 :param setup_path: Function to generate setup paths.
1019 setup_file(setup_path("phpldapadmin-config.php"), path,
1020 {"S4_LDAPI_URI": ldapi_uri})
1023 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
1024 hostip, hostname, dnspass, realm, domainguid, hostguid):
1025 """Write out a DNS zone file, from the info in the current database.
1027 :param path: Path of the new file.
1028 :param setup_path": Setup path function.
1029 :param samdb: SamDB object
1030 :param dnsdomain: DNS Domain name
1031 :param domaindn: DN of the Domain
1032 :param hostip: Local IP
1033 :param hostname: Local hostname
1034 :param dnspass: Password for DNS
1035 :param realm: Realm name
1036 :param domainguid: GUID of the domain.
1037 :param hostguid: GUID of the host.
1039 assert isinstance(domainguid, str)
1041 setup_file(setup_path("provision.zone"), path, {
1042 "DNSPASS_B64": b64encode(dnspass),
1043 "HOSTNAME": hostname,
1044 "DNSDOMAIN": dnsdomain,
1047 "DOMAINGUID": domainguid,
1048 "DATESTRING": time.strftime("%Y%m%d%H"),
1049 "DEFAULTSITE": DEFAULTSITE,
1050 "HOSTGUID": hostguid,
1053 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1054 """Load schema for the SamDB.
1056 :param samdb: Load a schema into a SamDB.
1057 :param setup_path: Setup path function.
1058 :param schemadn: DN of the schema
1059 :param netbiosname: NetBIOS name of the host.
1060 :param configdn: DN of the configuration
1062 schema_data = open(setup_path("schema.ldif"), 'r').read()
1063 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1064 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1065 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1066 head_data = substitute_var(head_data, {
1067 "SCHEMADN": schemadn,
1068 "NETBIOSNAME": netbiosname,
1069 "CONFIGDN": configdn,
1070 "DEFAULTSITE":sitename
1072 samdb.attach_schema_from_ldif(head_data, schema_data)