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
71 class ProvisionResult:
78 def check_install(lp, session_info, credentials):
79 """Check whether the current install seems ok.
81 :param lp: Loadparm context
82 :param session_info: Session information
83 :param credentials: Credentials
85 if lp.get("realm") == "":
86 raise Error("Realm empty")
87 ldb = Ldb(lp.get("sam database"), session_info=session_info,
88 credentials=credentials, lp=lp)
89 if len(ldb.search("(cn=Administrator)")) != 1:
90 raise "No administrator account found"
93 def findnss(nssfn, names):
94 """Find a user or group from a list of possibilities.
96 :param nssfn: NSS Function to try (should raise KeyError if not found)
97 :param names: Names to check.
98 :return: Value return by first names list.
105 raise KeyError("Unable to find user/group %r" % names)
108 def open_ldb(session_info, credentials, lp, dbname):
109 """Open a LDB, thrashing it if it is corrupt.
111 :param session_info: auth session information
112 :param credentials: credentials
113 :param lp: Loadparm context
114 :param dbname: Path of the database to open.
115 :return: a Ldb object
117 assert session_info is not None
119 return Ldb(dbname, session_info=session_info, credentials=credentials,
124 return Ldb(dbname, session_info=session_info, credentials=credentials,
128 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
129 """Setup a ldb in the private dir.
131 :param ldb: LDB file to import data into
132 :param ldif_path: Path of the LDIF file to load
133 :param subst_vars: Optional variables to subsitute in LDIF.
135 assert isinstance(ldif_path, str)
137 data = open(ldif_path, 'r').read()
138 if subst_vars is not None:
139 data = substitute_var(data, subst_vars)
141 check_all_substituted(data)
146 def setup_modify_ldif(ldb, ldif_path, substvars=None):
147 """Modify a ldb in the private dir.
149 :param ldb: LDB object.
150 :param ldif_path: LDIF file path.
151 :param substvars: Optional dictionary with substitution variables.
153 data = open(ldif_path, 'r').read()
154 if substvars is not None:
155 data = substitute_var(data, substvars)
157 check_all_substituted(data)
159 ldb.modify_ldif(data)
162 def setup_ldb(ldb, ldif_path, subst_vars):
163 """Import a LDIF a file into a LDB handle, optionally substituting variables.
165 :note: Either all LDIF data will be added or none (using transactions).
167 :param ldb: LDB file to import into.
168 :param ldif_path: Path to the LDIF file.
169 :param subst_vars: Dictionary with substitution variables.
171 assert ldb is not None
172 ldb.transaction_start()
174 setup_add_ldif(ldb, ldif_path, subst_vars)
176 ldb.transaction_cancel()
178 ldb.transaction_commit()
181 def setup_file(template, fname, substvars):
182 """Setup a file in the private dir.
184 :param template: Path of the template file.
185 :param fname: Path of the file to create.
186 :param substvars: Substitution variables.
190 if os.path.exists(f):
193 data = open(template, 'r').read()
195 data = substitute_var(data, substvars)
196 check_all_substituted(data)
198 open(f, 'w').write(data)
201 def provision_paths_from_lp(lp, dnsdomain):
202 """Set the default paths for provisioning.
204 :param lp: Loadparm context.
205 :param dnsdomain: DNS Domain name
207 paths = ProvisionPaths()
208 paths.private_dir = lp.get("private dir")
209 paths.keytab = "secrets.keytab"
210 paths.dns_keytab = "dns.keytab"
212 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
213 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
214 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
215 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
216 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
217 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
218 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
219 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
220 paths.smbconf = os.path.join(paths.private_dir, "smb.conf")
221 paths.phpldapadminconfig = os.path.join(paths.private_dir,
222 "phpldapadmin-config.php")
223 paths.hklm = "hklm.ldb"
224 paths.hkcr = "hkcr.ldb"
225 paths.hkcu = "hkcu.ldb"
226 paths.hku = "hku.ldb"
227 paths.hkpd = "hkpd.ldb"
228 paths.hkpt = "hkpt.ldb"
230 paths.sysvol = lp.get("sysvol", "path")
231 if paths.sysvol is None:
232 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
234 paths.netlogon = lp.get("netlogon", "path")
235 if paths.netlogon is None:
236 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
241 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
243 """setup reasonable name mappings for sam names to unix names.
245 :param ldb: SamDB object.
246 :param sid: The domain sid.
247 :param domaindn: The domain DN.
248 :param root: Name of the UNIX root user.
249 :param nobody: Name of the UNIX nobody user.
250 :param nogroup: Name of the unix nobody group.
251 :param users: Name of the unix users group.
252 :param wheel: Name of the wheel group (users that can become root).
253 :param backup: Name of the backup group."""
254 # add some foreign sids if they are not present already
255 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
256 ldb.add_foreign(domaindn, "S-1-1-0", "World")
257 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
258 ldb.add_foreign(domaindn, "S-1-5-18", "System")
259 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
261 # some well known sids
262 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
263 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
264 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
265 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
266 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
267 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
268 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
269 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
270 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
272 # and some well known domain rids
273 ldb.setup_name_mapping(domaindn, sid + "-500", root)
274 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
275 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
276 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
277 ldb.setup_name_mapping(domaindn, sid + "-513", users)
278 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
281 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
282 credentials, configdn, schemadn, domaindn,
283 hostname, netbiosname, dnsdomain, realm,
284 rootdn, serverrole, sitename, ldap_backend=None,
285 ldap_backend_type=None, erase=False):
286 """Setup the partitions for the SAM database.
288 Alternatively, provision() may call this, and then populate the database.
290 :note: This will wipe the Sam Database!
292 :note: This function always removes the local SAM LDB file. The erase
293 parameter controls whether to erase the existing data, which
294 may not be stored locally but in LDAP.
296 assert session_info is not None
298 samdb = SamDB(samdb_path, session_info=session_info,
299 credentials=credentials, lp=lp)
305 os.unlink(samdb_path)
307 samdb = SamDB(samdb_path, session_info=session_info,
308 credentials=credentials, lp=lp)
310 #Add modules to the list to activate them by default
311 #beware often order is important
313 # Some Known ordering constraints:
314 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
315 # - objectclass must be before password_hash, because password_hash checks
316 # that the objectclass is of type person (filled in by objectclass
317 # module when expanding the objectclass list)
318 # - partition must be last
319 # - each partition has its own module list then
320 modules_list = ["rootdse",
336 modules_list2 = ["show_deleted",
339 domaindn_ldb = "users.ldb"
340 if ldap_backend is not None:
341 domaindn_ldb = ldap_backend
342 configdn_ldb = "configuration.ldb"
343 if ldap_backend is not None:
344 configdn_ldb = ldap_backend
345 schemadn_ldb = "schema.ldb"
346 if ldap_backend is not None:
347 schema_ldb = ldap_backend
348 schemadn_ldb = ldap_backend
350 if ldap_backend_type == "fedora-ds":
351 backend_modules = ["nsuniqueid", "paged_searches"]
352 # We can handle linked attributes here, as we don't have directory-side subtree operations
353 tdb_modules_list = ["linked_attributes"]
354 elif ldap_backend_type == "openldap":
355 backend_modules = ["normalise", "entryuuid", "paged_searches"]
356 # OpenLDAP handles subtree renames, so we don't want to do any of these things
357 tdb_modules_list = None
358 elif serverrole == "domain controller":
359 backend_modules = ["repl_meta_data"]
361 backend_modules = ["objectguid"]
363 if tdb_modules_list is None:
364 tdb_modules_list_as_string = ""
366 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
368 samdb.transaction_start()
370 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
371 "SCHEMADN": schemadn,
372 "SCHEMADN_LDB": schemadn_ldb,
373 "SCHEMADN_MOD2": ",objectguid",
374 "CONFIGDN": configdn,
375 "CONFIGDN_LDB": configdn_ldb,
376 "DOMAINDN": domaindn,
377 "DOMAINDN_LDB": domaindn_ldb,
378 "SCHEMADN_MOD": "schema_fsmo,instancetype",
379 "CONFIGDN_MOD": "naming_fsmo,instancetype",
380 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
381 "MODULES_LIST": ",".join(modules_list),
382 "TDB_MODULES_LIST": tdb_modules_list_as_string,
383 "MODULES_LIST2": ",".join(modules_list2),
384 "BACKEND_MOD": ",".join(backend_modules),
388 samdb.transaction_cancel()
391 samdb.transaction_commit()
393 samdb = SamDB(samdb_path, session_info=session_info,
394 credentials=credentials, lp=lp)
396 samdb.transaction_start()
398 message("Setting up sam.ldb attributes")
399 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
401 message("Setting up sam.ldb rootDSE")
402 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
403 dnsdomain, realm, rootdn, configdn, netbiosname,
407 message("Erasing data from partitions")
408 samdb.erase_partitions()
411 samdb.transaction_cancel()
414 samdb.transaction_commit()
419 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
420 netbiosname, domainsid, keytab_path, samdb_url,
421 dns_keytab_path, dnspass, machinepass):
422 """Add DC-specific bits to a secrets database.
424 :param secretsdb: Ldb Handle to the secrets database
425 :param setup_path: Setup path function
426 :param machinepass: Machine password
428 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
429 "MACHINEPASS_B64": b64encode(machinepass),
432 "DNSDOMAIN": dnsdomain,
433 "DOMAINSID": str(domainsid),
434 "SECRETS_KEYTAB": keytab_path,
435 "NETBIOSNAME": netbiosname,
436 "SAM_LDB": samdb_url,
437 "DNS_KEYTAB": dns_keytab_path,
438 "DNSPASS_B64": b64encode(dnspass),
442 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
443 """Setup the secrets database.
445 :param path: Path to the secrets database.
446 :param setup_path: Get the path to a setup file.
447 :param session_info: Session info.
448 :param credentials: Credentials
449 :param lp: Loadparm context
450 :return: LDB handle for the created secrets database
452 if os.path.exists(path):
454 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
457 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
458 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
460 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
464 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
465 """Setup the templates database.
467 :param path: Path to the database.
468 :param setup_path: Function for obtaining the path to setup files.
469 :param session_info: Session info
470 :param credentials: Credentials
471 :param lp: Loadparm context
473 templates_ldb = SamDB(path, session_info=session_info,
474 credentials=credentials, lp=lp)
475 templates_ldb.erase()
476 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
479 def setup_registry(path, setup_path, session_info, credentials, lp):
480 """Setup the registry.
482 :param path: Path to the registry database
483 :param setup_path: Function that returns the path to a setup.
484 :param session_info: Session information
485 :param credentials: Credentials
486 :param lp: Loadparm context
488 reg = registry.Registry()
489 hive = registry.open_ldb(path, session_info=session_info,
490 credentials=credentials, lp_ctx=lp)
491 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
492 provision_reg = setup_path("provision.reg")
493 assert os.path.exists(provision_reg)
494 reg.diff_apply(provision_reg)
496 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
497 """Setup the idmap database.
499 :param path: path to the idmap database
500 :param setup_path: Function that returns a path to a setup file
501 :param session_info: Session information
502 :param credentials: Credentials
503 :param lp: Loadparm context
505 if os.path.exists(path):
508 idmap_ldb = Ldb(path, session_info=session_info, credentials=credentials,
512 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
515 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
516 dnsdomain, realm, rootdn, configdn, netbiosname,
518 """Setup the SamDB rootdse.
520 :param samdb: Sam Database handle
521 :param setup_path: Obtain setup path
523 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
524 "SCHEMADN": schemadn,
525 "NETBIOSNAME": netbiosname,
526 "DNSDOMAIN": dnsdomain,
527 "DEFAULTSITE": sitename,
529 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
530 "DOMAINDN": domaindn,
532 "CONFIGDN": configdn,
533 "VERSION": samba.version(),
537 def setup_self_join(samdb, configdn, schemadn, domaindn,
538 netbiosname, hostname, dnsdomain, machinepass, dnspass,
539 realm, domainname, domainsid, invocationid, setup_path,
540 policyguid, sitename, hostguid=None):
541 """Join a host to its own domain."""
542 if hostguid is not None:
543 hostguid_add = "objectGUID: %s" % hostguid
547 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
548 "CONFIGDN": configdn,
549 "SCHEMADN": schemadn,
550 "DOMAINDN": domaindn,
551 "INVOCATIONID": invocationid,
552 "NETBIOSNAME": netbiosname,
553 "DEFAULTSITE": sitename,
554 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
555 "MACHINEPASS_B64": b64encode(machinepass),
556 "DNSPASS_B64": b64encode(dnspass),
558 "DOMAIN": domainname,
559 "HOSTGUID_ADD": hostguid_add,
560 "DNSDOMAIN": dnsdomain})
561 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
562 "POLICYGUID": policyguid,
563 "DNSDOMAIN": dnsdomain,
564 "DOMAINSID": str(domainsid),
565 "DOMAINDN": domaindn})
568 def setup_samdb(path, setup_path, session_info, credentials, lp,
569 schemadn, configdn, domaindn, dnsdomain, realm,
570 netbiosname, message, hostname, rootdn,
571 domainsid, aci, domainguid, policyguid,
572 domainname, fill, adminpass, krbtgtpass,
573 machinepass, hostguid, invocationid, dnspass,
574 serverrole, sitename, ldap_backend=None,
575 ldap_backend_type=None):
576 """Setup a complete SAM Database.
578 :note: This will wipe the main SAM database file!
581 erase = (fill != FILL_DRS)
583 # Also wipes the database
584 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
585 domaindn=domaindn, message=message, lp=lp,
586 credentials=credentials, session_info=session_info,
587 hostname=hostname, netbiosname=netbiosname,
588 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
589 ldap_backend=ldap_backend, serverrole=serverrole,
590 ldap_backend_type=ldap_backend_type, erase=erase,
593 samdb = SamDB(path, session_info=session_info,
594 credentials=credentials, lp=lp)
597 # We want to finish here, but setup the index before we do so
598 message("Setting up sam.ldb index")
599 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
602 message("Pre-loading the Samba 4 and AD schema")
603 samdb = SamDB(path, session_info=session_info,
604 credentials=credentials, lp=lp)
605 samdb.set_domain_sid(domainsid)
606 if serverrole == "domain controller":
607 samdb.set_invocation_id(invocationid)
609 load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename)
611 samdb.transaction_start()
614 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
615 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
616 "DOMAINDN": domaindn,
620 message("Modifying DomainDN: " + domaindn + "")
621 if domainguid is not None:
622 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
626 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
627 "LDAPTIME": timestring(int(time.time())),
628 "DOMAINSID": str(domainsid),
629 "SCHEMADN": schemadn,
630 "NETBIOSNAME": netbiosname,
631 "DEFAULTSITE": sitename,
632 "CONFIGDN": configdn,
633 "POLICYGUID": policyguid,
634 "DOMAINDN": domaindn,
635 "DOMAINGUID_MOD": domainguid_mod,
638 message("Adding configuration container (permitted to fail)")
639 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
640 "CONFIGDN": configdn,
642 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
644 message("Modifying configuration container")
645 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
646 "CONFIGDN": configdn,
647 "SCHEMADN": schemadn,
650 message("Adding schema container (permitted to fail)")
651 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
652 "SCHEMADN": schemadn,
654 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
656 message("Modifying schema container")
657 setup_modify_ldif(samdb,
658 setup_path("provision_schema_basedn_modify.ldif"), {
659 "SCHEMADN": schemadn,
660 "NETBIOSNAME": netbiosname,
661 "DEFAULTSITE": sitename,
662 "CONFIGDN": configdn,
665 message("Setting up sam.ldb Samba4 schema")
666 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
667 {"SCHEMADN": schemadn })
668 message("Setting up sam.ldb AD schema")
669 setup_add_ldif(samdb, setup_path("schema.ldif"),
670 {"SCHEMADN": schemadn})
672 message("Setting up sam.ldb configuration data")
673 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
674 "CONFIGDN": configdn,
675 "NETBIOSNAME": netbiosname,
676 "DEFAULTSITE": sitename,
677 "DNSDOMAIN": dnsdomain,
678 "DOMAIN": domainname,
679 "SCHEMADN": schemadn,
680 "DOMAINDN": domaindn,
683 message("Setting up display specifiers")
684 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
685 {"CONFIGDN": configdn})
687 message("Adding users container (permitted to fail)")
688 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
689 "DOMAINDN": domaindn})
690 message("Modifying users container")
691 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
692 "DOMAINDN": domaindn})
693 message("Adding computers container (permitted to fail)")
694 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
695 "DOMAINDN": domaindn})
696 message("Modifying computers container")
697 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
698 "DOMAINDN": domaindn})
699 message("Setting up sam.ldb data")
700 setup_add_ldif(samdb, setup_path("provision.ldif"), {
701 "DOMAINDN": domaindn,
702 "NETBIOSNAME": netbiosname,
703 "DEFAULTSITE": sitename,
704 "CONFIGDN": configdn,
707 if fill == FILL_FULL:
708 message("Setting up sam.ldb users and groups")
709 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
710 "DOMAINDN": domaindn,
711 "DOMAINSID": str(domainsid),
712 "CONFIGDN": configdn,
713 "ADMINPASS_B64": b64encode(adminpass),
714 "KRBTGTPASS_B64": b64encode(krbtgtpass),
717 if serverrole == "domain controller":
718 message("Setting up self join")
719 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
720 domaindn=domaindn, invocationid=invocationid,
721 dnspass=dnspass, netbiosname=netbiosname,
722 dnsdomain=dnsdomain, realm=realm,
723 machinepass=machinepass, domainname=domainname,
724 domainsid=domainsid, policyguid=policyguid,
725 hostname=hostname, hostguid=hostguid,
726 setup_path=setup_path, sitename=sitename)
728 #We want to setup the index last, as adds are faster unindexed
729 message("Setting up sam.ldb index")
730 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
732 samdb.transaction_cancel()
735 samdb.transaction_commit()
740 FILL_NT4SYNC = "NT4SYNC"
743 def provision(setup_dir, message, session_info,
744 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
745 rootdn=None, domaindn=None, schemadn=None, configdn=None,
746 domain=None, hostname=None, hostip=None, domainsid=None,
747 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
748 policyguid=None, invocationid=None, machinepass=None,
749 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
750 wheel=None, backup=None, aci=None, serverrole=None,
751 ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
754 :note: caution, this wipes all existing data!
757 def setup_path(file):
758 return os.path.join(setup_dir, file)
760 if domainsid is None:
761 domainsid = security.random_sid()
762 if policyguid is None:
763 policyguid = uuid.random()
764 if adminpass is None:
765 adminpass = misc.random_password(12)
766 if krbtgtpass is None:
767 krbtgtpass = misc.random_password(12)
768 if machinepass is None:
769 machinepass = misc.random_password(12)
771 dnspass = misc.random_password(12)
773 root = findnss(pwd.getpwnam, ["root"])[0]
775 nobody = findnss(pwd.getpwnam, ["nobody"])[0]
777 nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
779 users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown",
782 wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
784 backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
786 aci = "# no aci for local ldb"
788 hostname = gethostname().split(".")[0].lower()
791 hostip = gethostbyname(hostname)
793 netbiosname = hostname.upper()
794 if not valid_netbios_name(netbiosname):
795 raise InvalidNetbiosName(netbiosname)
797 if targetdir is not None:
798 if not os.path.exists(targetdir):
800 if not os.path.exists(os.path.join(targetdir, "etc")):
801 os.mkdir(os.path.join(targetdir, "etc"))
803 smbconf = os.path.join(targetdir, os.path.join("etc", "smb.conf"))
805 # only install a new smb.conf if there isn't one there already
807 if not os.path.exists(smbconf):
808 message("Setting up smb.conf")
809 if serverrole is None:
810 serverrole = "standalone"
812 assert serverrole in ("domain controller", "member server", "standalone")
813 if serverrole == "domain controller":
815 elif serverrole == "member server":
816 smbconfsuffix = "member"
817 elif serverrole == "standalone":
818 smbconfsuffix = "standalone"
820 assert domain is not None
821 assert realm is not None
823 default_lp = param.LoadParm()
824 #Load non-existant file
825 default_lp.load(smbconf)
827 if targetdir is not None:
828 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
829 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
831 default_lp.set("lock dir", os.path.abspath(targetdir))
833 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
834 netlogon = os.path.join(os.path.join(sysvol, "scripts"))
836 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
838 "HOSTNAME": hostname,
841 "SERVERROLE": serverrole,
842 "NETLOGONPATH": netlogon,
843 "SYSVOLPATH": sysvol,
844 "PRIVATEDIR_LINE": privatedir_line,
845 "LOCKDIR_LINE": lockdir_line
848 lp = param.LoadParm()
851 if serverrole is None:
852 serverrole = lp.get("server role")
853 assert serverrole in ("domain controller", "member server", "standalone")
854 if invocationid is None and serverrole == "domain controller":
855 invocationid = uuid.random()
858 realm = lp.get("realm")
860 assert realm is not None
861 realm = realm.upper()
863 if lp.get("realm").upper() != realm.upper():
864 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
865 (lp.get("realm"), smbconf, realm))
867 dnsdomain = realm.lower()
869 paths = provision_paths_from_lp(lp, dnsdomain)
871 if targetdir is not None:
872 if not os.path.exists(paths.private_dir):
873 os.mkdir(paths.private_dir)
875 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
877 if ldap_backend == "ldapi":
878 # provision-backend will set this path suggested slapd command line / fedorads.inf
879 ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
881 if serverrole == "domain controller":
883 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
885 domain = lp.get("workgroup")
887 if lp.get("workgroup").upper() != domain.upper():
888 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
889 lp.get("workgroup"), domain)
891 assert domain is not None
892 domain = domain.upper()
893 if not valid_netbios_name(domain):
894 raise InvalidNetbiosName(domain)
897 domaindn = "CN=" + netbiosname
904 configdn = "CN=Configuration," + rootdn
906 schemadn = "CN=Schema," + configdn
908 message("set DOMAIN SID: %s" % str(domainsid))
909 message("Provisioning for %s in realm %s" % (domain, realm))
910 message("Using administrator password: %s" % adminpass)
912 # only install a new shares config db if there is none
913 if not os.path.exists(paths.shareconf):
914 message("Setting up share.ldb")
915 share_ldb = Ldb(paths.shareconf, session_info=session_info,
916 credentials=credentials, lp=lp)
917 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
920 message("Setting up secrets.ldb")
921 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
922 session_info=session_info,
923 credentials=credentials, lp=lp)
925 message("Setting up the registry")
926 setup_registry(paths.hklm, setup_path, session_info,
927 credentials=credentials, lp=lp)
929 message("Setting up templates db")
930 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
931 credentials=credentials, lp=lp)
933 message("Setting up idmap db")
934 setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
935 credentials=credentials, lp=lp)
937 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
938 credentials=credentials, lp=lp, schemadn=schemadn,
939 configdn=configdn, domaindn=domaindn,
940 dnsdomain=dnsdomain, netbiosname=netbiosname,
941 realm=realm, message=message, hostname=hostname,
942 rootdn=rootdn, domainsid=domainsid,
943 aci=aci, domainguid=domainguid, policyguid=policyguid,
944 domainname=domain, fill=samdb_fill,
945 adminpass=adminpass, krbtgtpass=krbtgtpass,
946 hostguid=hostguid, invocationid=invocationid,
947 machinepass=machinepass, dnspass=dnspass,
948 serverrole=serverrole, ldap_backend=ldap_backend,
949 ldap_backend_type=ldap_backend_type, sitename=sitename)
951 if lp.get("server role") == "domain controller":
952 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
953 "{" + policyguid + "}")
954 os.makedirs(policy_path, 0755)
955 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
956 os.makedirs(os.path.join(policy_path, "User"), 0755)
957 if not os.path.isdir(paths.netlogon):
958 os.makedirs(paths.netlogon, 0755)
959 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
960 credentials=credentials, lp=lp)
961 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
962 netbiosname=netbiosname, domainsid=domainsid,
963 keytab_path=paths.keytab, samdb_url=paths.samdb,
964 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
965 machinepass=machinepass, dnsdomain=dnsdomain)
967 if samdb_fill == FILL_FULL:
968 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
969 nobody=nobody, nogroup=nogroup, wheel=wheel,
970 users=users, backup=backup)
972 message("Setting up sam.ldb rootDSE marking as synchronized")
973 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
975 # Only make a zone file on the first DC, it should be replicated with DNS replication
976 if serverrole == "domain controller":
977 samdb = SamDB(paths.samdb, session_info=session_info,
978 credentials=credentials, lp=lp)
980 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
981 assert isinstance(domainguid, str)
982 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
983 expression="(&(objectClass=computer)(cn=%s))" % hostname,
985 assert isinstance(hostguid, str)
987 message("Setting up DNS zone: %s" % dnsdomain)
988 create_zone_file(paths.dns, setup_path, samdb,
989 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
990 domaindn=domaindn, dnspass=dnspass, realm=realm,
991 domainguid=domainguid, hostguid=hostguid)
992 message("Please install the zone located in %s into your DNS server" % paths.dns)
994 message("Setting up phpLDAPadmin configuration")
995 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
998 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1000 result = ProvisionResult()
1001 result.domaindn = domaindn
1002 result.paths = paths
1004 result.samdb = samdb
1007 def provision_become_dc(setup_dir=None,
1008 smbconf=None, targetdir=None, realm=None,
1009 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1010 domain=None, hostname=None, domainsid=None,
1011 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
1012 policyguid=None, invocationid=None, machinepass=None,
1013 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1014 wheel=None, backup=None, aci=None, serverrole=None,
1015 ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
1018 """print a message if quiet is not set."""
1021 provision(setup_dir, message, system_session(), None,
1022 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1023 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn,
1024 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1027 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1028 """Create a PHP LDAP admin configuration file.
1030 :param path: Path to write the configuration to.
1031 :param setup_path: Function to generate setup paths.
1033 setup_file(setup_path("phpldapadmin-config.php"), path,
1034 {"S4_LDAPI_URI": ldapi_uri})
1037 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
1038 hostip, hostname, dnspass, realm, domainguid, hostguid):
1039 """Write out a DNS zone file, from the info in the current database.
1041 :param path: Path of the new file.
1042 :param setup_path": Setup path function.
1043 :param samdb: SamDB object
1044 :param dnsdomain: DNS Domain name
1045 :param domaindn: DN of the Domain
1046 :param hostip: Local IP
1047 :param hostname: Local hostname
1048 :param dnspass: Password for DNS
1049 :param realm: Realm name
1050 :param domainguid: GUID of the domain.
1051 :param hostguid: GUID of the host.
1053 assert isinstance(domainguid, str)
1055 setup_file(setup_path("provision.zone"), path, {
1056 "DNSPASS_B64": b64encode(dnspass),
1057 "HOSTNAME": hostname,
1058 "DNSDOMAIN": dnsdomain,
1061 "DOMAINGUID": domainguid,
1062 "DATESTRING": time.strftime("%Y%m%d%H"),
1063 "DEFAULTSITE": DEFAULTSITE,
1064 "HOSTGUID": hostguid,
1067 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1068 """Load schema for the SamDB.
1070 :param samdb: Load a schema into a SamDB.
1071 :param setup_path: Setup path function.
1072 :param schemadn: DN of the schema
1073 :param netbiosname: NetBIOS name of the host.
1074 :param configdn: DN of the configuration
1076 schema_data = open(setup_path("schema.ldif"), 'r').read()
1077 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1078 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1079 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1080 head_data = substitute_var(head_data, {
1081 "SCHEMADN": schemadn,
1082 "NETBIOSNAME": netbiosname,
1083 "CONFIGDN": configdn,
1084 "DEFAULTSITE":sitename
1086 samdb.attach_schema_from_ldif(head_data, schema_data)