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 samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
36 from samba.samdb import SamDB
39 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
40 LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
42 """Functions for setting up a Samba configuration."""
44 DEFAULTSITE = "Default-First-Site-Name"
46 class InvalidNetbiosName(Exception):
47 def __init__(self, name):
48 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
64 self.dns_keytab = None
69 def check_install(lp, session_info, credentials):
70 """Check whether the current install seems ok.
72 :param lp: Loadparm context
73 :param session_info: Session information
74 :param credentials: Credentials
76 if lp.get("realm") == "":
77 raise Error("Realm empty")
78 ldb = Ldb(lp.get("sam database"), session_info=session_info,
79 credentials=credentials, lp=lp)
80 if len(ldb.search("(cn=Administrator)")) != 1:
81 raise "No administrator account found"
84 def findnss(nssfn, names):
85 """Find a user or group from a list of possibilities.
87 :param nssfn: NSS Function to try (should raise KeyError if not found)
88 :param names: Names to check.
89 :return: Value return by first names list.
96 raise KeyError("Unable to find user/group %r" % names)
99 def open_ldb(session_info, credentials, lp, dbname):
100 """Open a LDB, thrashing it if it is corrupt.
102 :param session_info: auth session information
103 :param credentials: credentials
104 :param lp: Loadparm context
105 :param dbname: Path of the database to open.
106 :return: a Ldb object
108 assert session_info is not None
110 return Ldb(dbname, session_info=session_info, credentials=credentials,
115 return Ldb(dbname, session_info=session_info, credentials=credentials,
119 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
120 """Setup a ldb in the private dir.
122 :param ldb: LDB file to import data into
123 :param ldif_path: Path of the LDIF file to load
124 :param subst_vars: Optional variables to subsitute in LDIF.
126 assert isinstance(ldif_path, str)
128 data = open(ldif_path, 'r').read()
129 if subst_vars is not None:
130 data = substitute_var(data, subst_vars)
132 check_all_substituted(data)
137 def setup_modify_ldif(ldb, ldif_path, substvars=None):
138 """Modify a ldb in the private dir.
140 :param ldb: LDB object.
141 :param ldif_path: LDIF file path.
142 :param substvars: Optional dictionary with substitution variables.
144 data = open(ldif_path, 'r').read()
145 if substvars is not None:
146 data = substitute_var(data, substvars)
148 check_all_substituted(data)
150 ldb.modify_ldif(data)
153 def setup_ldb(ldb, ldif_path, subst_vars):
154 """Import a LDIF a file into a LDB handle, optionally substituting variables.
156 :note: Either all LDIF data will be added or none (using transactions).
158 :param ldb: LDB file to import into.
159 :param ldif_path: Path to the LDIF file.
160 :param subst_vars: Dictionary with substitution variables.
162 assert ldb is not None
163 ldb.transaction_start()
165 setup_add_ldif(ldb, ldif_path, subst_vars)
167 ldb.transaction_cancel()
169 ldb.transaction_commit()
172 def setup_file(template, fname, substvars):
173 """Setup a file in the private dir.
175 :param template: Path of the template file.
176 :param fname: Path of the file to create.
177 :param substvars: Substitution variables.
181 if os.path.exists(f):
184 data = open(template, 'r').read()
186 data = substitute_var(data, substvars)
187 check_all_substituted(data)
189 open(f, 'w').write(data)
192 def provision_paths_from_lp(lp, dnsdomain):
193 """Set the default paths for provisioning.
195 :param lp: Loadparm context.
196 :param dnsdomain: DNS Domain name
198 paths = ProvisionPaths()
199 private_dir = lp.get("private dir")
200 paths.keytab = "secrets.keytab"
201 paths.dns_keytab = "dns.keytab"
203 paths.shareconf = os.path.join(private_dir, "share.ldb")
204 paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb")
205 paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb")
206 paths.templates = os.path.join(private_dir, "templates.ldb")
207 paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
208 paths.winsdb = os.path.join(private_dir, "wins.ldb")
209 paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
210 paths.smbconf = os.path.join(private_dir, "smb.conf")
211 paths.phpldapadminconfig = os.path.join(private_dir,
212 "phpldapadmin-config.php")
213 paths.hklm = "hklm.ldb"
214 paths.hkcr = "hkcr.ldb"
215 paths.hkcu = "hkcu.ldb"
216 paths.hku = "hku.ldb"
217 paths.hkpd = "hkpd.ldb"
218 paths.hkpt = "hkpt.ldb"
220 paths.sysvol = lp.get("sysvol", "path")
221 if paths.sysvol is None:
222 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
224 paths.netlogon = lp.get("netlogon", "path")
225 if paths.netlogon is None:
226 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
231 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
233 """setup reasonable name mappings for sam names to unix names.
235 :param ldb: SamDB object.
236 :param sid: The domain sid.
237 :param domaindn: The domain DN.
238 :param root: Name of the UNIX root user.
239 :param nobody: Name of the UNIX nobody user.
240 :param nogroup: Name of the unix nobody group.
241 :param users: Name of the unix users group.
242 :param wheel: Name of the wheel group (users that can become root).
243 :param backup: Name of the backup group."""
244 # add some foreign sids if they are not present already
245 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
246 ldb.add_foreign(domaindn, "S-1-1-0", "World")
247 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
248 ldb.add_foreign(domaindn, "S-1-5-18", "System")
249 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
251 # some well known sids
252 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
253 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
254 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
255 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
256 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
257 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
258 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
259 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
260 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
262 # and some well known domain rids
263 ldb.setup_name_mapping(domaindn, sid + "-500", root)
264 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
265 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
266 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
267 ldb.setup_name_mapping(domaindn, sid + "-513", users)
268 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
271 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
272 credentials, configdn, schemadn, domaindn,
273 hostname, netbiosname, dnsdomain, realm,
274 rootdn, serverrole, ldap_backend=None,
275 ldap_backend_type=None, erase=False):
276 """Setup the partitions for the SAM database.
278 Alternatively, provision() may call this, and then populate the database.
280 :param erase: Remove the existing data present in the database.
282 :note: This will wipe the Sam Database!
284 :note: This function always removes the local SAM LDB file. The erase
285 parameter controls whether to erase the existing data, which
286 may not be stored locally but in LDAP.
288 assert session_info is not None
290 if os.path.exists(samdb_path):
291 os.unlink(samdb_path)
293 # Also wipes the database
294 samdb = SamDB(samdb_path, session_info=session_info,
295 credentials=credentials, lp=lp)
297 #Add modules to the list to activate them by default
298 #beware often order is important
300 # Some Known ordering constraints:
301 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
302 # - objectclass must be before password_hash, because password_hash checks
303 # that the objectclass is of type person (filled in by objectclass
304 # module when expanding the objectclass list)
305 # - partition must be last
306 # - each partition has its own module list then
307 modules_list = ["rootdse",
323 modules_list2 = ["show_deleted",
326 domaindn_ldb = "users.ldb"
327 if ldap_backend is not None:
328 domaindn_ldb = ldap_backend
329 configdn_ldb = "configuration.ldb"
330 if ldap_backend is not None:
331 configdn_ldb = ldap_backend
332 schemadn_ldb = "schema.ldb"
333 if ldap_backend is not None:
334 schema_ldb = ldap_backend
336 schemadn_ldb = ldap_backend
338 if ldap_backend_type == "fedora-ds":
339 backend_modules = ["nsuniqueid","paged_searches"]
340 elif ldap_backend_type == "openldap":
341 backend_modules = ["normalise","entryuuid","paged_searches"]
342 elif serverrole == "domain controller":
343 backend_modules = ["repl_meta_data"]
345 backend_modules = ["objectguid"]
347 samdb.transaction_start()
349 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
350 "SCHEMADN": schemadn,
351 "SCHEMADN_LDB": schemadn_ldb,
352 "SCHEMADN_MOD2": ",objectguid",
353 "CONFIGDN": configdn,
354 "CONFIGDN_LDB": configdn_ldb,
355 "DOMAINDN": domaindn,
356 "DOMAINDN_LDB": domaindn_ldb,
357 "SCHEMADN_MOD": "schema_fsmo,instancetype",
358 "CONFIGDN_MOD": "naming_fsmo,instancetype",
359 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
360 "MODULES_LIST": ",".join(modules_list),
361 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
362 "MODULES_LIST2": ",".join(modules_list2),
363 "BACKEND_MOD": ",".join(backend_modules),
367 samdb.transaction_cancel()
370 samdb.transaction_commit()
372 samdb = SamDB(samdb_path, session_info=session_info,
373 credentials=credentials, lp=lp)
375 samdb.transaction_start()
377 message("Setting up sam.ldb attributes")
378 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
380 message("Setting up sam.ldb rootDSE")
381 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
382 dnsdomain, realm, rootdn, configdn, netbiosname)
385 message("Erasing data from partitions")
386 samdb.erase_partitions()
389 samdb.transaction_cancel()
392 samdb.transaction_commit()
397 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
398 netbiosname, domainsid, keytab_path, samdb_url,
399 dns_keytab_path, dnspass, machinepass):
400 """Add DC-specific bits to a secrets database.
402 :param secretsdb: Ldb Handle to the secrets database
403 :param setup_path: Setup path function
404 :param machinepass: Machine password
406 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
407 "MACHINEPASS_B64": b64encode(machinepass),
410 "DNSDOMAIN": dnsdomain,
411 "DOMAINSID": str(domainsid),
412 "SECRETS_KEYTAB": keytab_path,
413 "NETBIOSNAME": netbiosname,
414 "SAM_LDB": samdb_url,
415 "DNS_KEYTAB": dns_keytab_path,
416 "DNSPASS_B64": b64encode(dnspass),
420 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
421 """Setup the secrets database.
423 :param path: Path to the secrets database.
424 :param setup_path: Get the path to a setup file.
425 :param session_info: Session info.
426 :param credentials: Credentials
427 :param lp: Loadparm context
428 :return: LDB handle for the created secrets database
430 if os.path.exists(path):
432 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
435 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
436 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
438 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
442 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
443 """Setup the templates database.
445 :param path: Path to the database.
446 :param setup_path: Function for obtaining the path to setup files.
447 :param session_info: Session info
448 :param credentials: Credentials
449 :param lp: Loadparm context
451 templates_ldb = SamDB(path, session_info=session_info,
452 credentials=credentials, lp=lp)
453 templates_ldb.erase()
454 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
457 def setup_registry(path, setup_path, session_info, credentials, lp):
458 """Setup the registry.
460 :param path: Path to the registry database
461 :param setup_path: Function that returns the path to a setup.
462 :param session_info: Session information
463 :param credentials: Credentials
464 :param lp: Loadparm context
466 reg = registry.Registry()
467 hive = registry.open_ldb(path, session_info=session_info,
468 credentials=credentials, lp_ctx=lp)
469 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
470 provision_reg = setup_path("provision.reg")
471 assert os.path.exists(provision_reg)
472 reg.diff_apply(provision_reg)
475 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
476 dnsdomain, realm, rootdn, configdn, netbiosname):
477 """Setup the SamDB rootdse.
479 :param samdb: Sam Database handle
480 :param setup_path: Obtain setup path
482 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
483 "SCHEMADN": schemadn,
484 "NETBIOSNAME": netbiosname,
485 "DNSDOMAIN": dnsdomain,
486 "DEFAULTSITE": DEFAULTSITE,
488 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
489 "DOMAINDN": domaindn,
491 "CONFIGDN": configdn,
492 "VERSION": samba.version(),
496 def setup_self_join(samdb, configdn, schemadn, domaindn,
497 netbiosname, hostname, dnsdomain, machinepass, dnspass,
498 realm, domainname, domainsid, invocationid, setup_path,
499 policyguid, hostguid=None):
500 """Join a host to its own domain."""
501 if hostguid is not None:
502 hostguid_add = "objectGUID: %s" % hostguid
506 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
507 "CONFIGDN": configdn,
508 "SCHEMADN": schemadn,
509 "DOMAINDN": domaindn,
510 "INVOCATIONID": invocationid,
511 "NETBIOSNAME": netbiosname,
512 "DEFAULTSITE": DEFAULTSITE,
513 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
514 "MACHINEPASS_B64": b64encode(machinepass),
515 "DNSPASS_B64": b64encode(dnspass),
517 "DOMAIN": domainname,
518 "HOSTGUID_ADD": hostguid_add,
519 "DNSDOMAIN": dnsdomain})
520 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
521 "POLICYGUID": policyguid,
522 "DNSDOMAIN": dnsdomain,
523 "DOMAINSID": str(domainsid),
524 "DOMAINDN": domaindn})
527 def setup_samdb(path, setup_path, session_info, credentials, lp,
528 schemadn, configdn, domaindn, dnsdomain, realm,
529 netbiosname, message, hostname, rootdn, erase,
530 domainsid, aci, domainguid, policyguid,
531 domainname, fill, adminpass, krbtgtpass,
532 machinepass, hostguid, invocationid, dnspass,
533 serverrole, ldap_backend=None, ldap_backend_type=None):
534 """Setup a complete SAM Database.
536 :note: This will wipe the main SAM database file!
539 # Also wipes the database
540 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
541 domaindn=domaindn, message=message, lp=lp,
542 credentials=credentials, session_info=session_info,
543 hostname=hostname, netbiosname=netbiosname,
544 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
545 ldap_backend=ldap_backend, serverrole=serverrole,
546 ldap_backend_type=ldap_backend_type, erase=erase)
548 samdb = SamDB(path, session_info=session_info,
549 credentials=credentials, lp=lp)
552 # We want to finish here, but setup the index before we do so
553 message("Setting up sam.ldb index")
554 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
557 message("Pre-loading the Samba 4 and AD schema")
558 samdb = SamDB(path, session_info=session_info,
559 credentials=credentials, lp=lp)
560 samdb.set_domain_sid(domainsid)
561 if lp.get("server role") == "domain controller":
562 samdb.set_invocation_id(invocationid)
564 load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
566 samdb.transaction_start()
569 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
570 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
571 "DOMAINDN": domaindn,
575 message("Modifying DomainDN: " + domaindn + "")
576 if domainguid is not None:
577 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
581 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
582 "LDAPTIME": timestring(int(time.time())),
583 "DOMAINSID": str(domainsid),
584 "SCHEMADN": schemadn,
585 "NETBIOSNAME": netbiosname,
586 "DEFAULTSITE": DEFAULTSITE,
587 "CONFIGDN": configdn,
588 "POLICYGUID": policyguid,
589 "DOMAINDN": domaindn,
590 "DOMAINGUID_MOD": domainguid_mod,
593 message("Adding configuration container (permitted to fail)")
594 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
595 "CONFIGDN": configdn,
597 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
599 message("Modifying configuration container")
600 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
601 "CONFIGDN": configdn,
602 "SCHEMADN": schemadn,
605 message("Adding schema container (permitted to fail)")
606 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
607 "SCHEMADN": schemadn,
609 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
611 message("Modifying schema container")
612 setup_modify_ldif(samdb,
613 setup_path("provision_schema_basedn_modify.ldif"), {
614 "SCHEMADN": schemadn,
615 "NETBIOSNAME": netbiosname,
616 "DEFAULTSITE": DEFAULTSITE,
617 "CONFIGDN": configdn,
620 message("Setting up sam.ldb Samba4 schema")
621 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
622 {"SCHEMADN": schemadn })
623 message("Setting up sam.ldb AD schema")
624 setup_add_ldif(samdb, setup_path("schema.ldif"),
625 {"SCHEMADN": schemadn})
627 message("Setting up sam.ldb configuration data")
628 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
629 "CONFIGDN": configdn,
630 "NETBIOSNAME": netbiosname,
631 "DEFAULTSITE": DEFAULTSITE,
632 "DNSDOMAIN": dnsdomain,
633 "DOMAIN": domainname,
634 "SCHEMADN": schemadn,
635 "DOMAINDN": domaindn,
638 message("Setting up display specifiers")
639 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
640 {"CONFIGDN": configdn})
642 message("Adding users container (permitted to fail)")
643 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
644 "DOMAINDN": domaindn})
645 message("Modifying users container")
646 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
647 "DOMAINDN": domaindn})
648 message("Adding computers container (permitted to fail)")
649 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
650 "DOMAINDN": domaindn})
651 message("Modifying computers container")
652 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
653 "DOMAINDN": domaindn})
654 message("Setting up sam.ldb data")
655 setup_add_ldif(samdb, setup_path("provision.ldif"), {
656 "DOMAINDN": domaindn,
657 "NETBIOSNAME": netbiosname,
658 "DEFAULTSITE": DEFAULTSITE,
659 "CONFIGDN": configdn,
662 if fill == FILL_FULL:
663 message("Setting up sam.ldb users and groups")
664 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
665 "DOMAINDN": domaindn,
666 "DOMAINSID": str(domainsid),
667 "CONFIGDN": configdn,
668 "ADMINPASS_B64": b64encode(adminpass),
669 "KRBTGTPASS_B64": b64encode(krbtgtpass),
672 if lp.get("server role") == "domain controller":
673 message("Setting up self join")
674 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
675 domaindn=domaindn, invocationid=invocationid,
676 dnspass=dnspass, netbiosname=netbiosname,
677 dnsdomain=dnsdomain, realm=realm,
678 machinepass=machinepass, domainname=domainname,
679 domainsid=domainsid, policyguid=policyguid,
680 hostname=hostname, hostguid=hostguid,
681 setup_path=setup_path)
683 #We want to setup the index last, as adds are faster unindexed
684 message("Setting up sam.ldb index")
685 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
687 samdb.transaction_cancel()
690 samdb.transaction_commit()
694 FILL_NT4SYNC = "NT4SYNC"
697 def provision(lp, setup_dir, message, paths, session_info,
698 credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None,
699 domain=None, hostname=None, hostip=None, domainsid=None,
700 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
701 policyguid=None, invocationid=None, machinepass=None,
702 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
703 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
704 ldap_backend=None, ldap_backend_type=None):
707 :note: caution, this wipes all existing data!
710 def setup_path(file):
711 return os.path.join(setup_dir, file)
713 if domainsid is None:
714 domainsid = security.random_sid()
715 if policyguid is None:
716 policyguid = uuid.random()
717 if adminpass is None:
718 adminpass = misc.random_password(12)
719 if krbtgtpass is None:
720 krbtgtpass = misc.random_password(12)
721 if machinepass is None:
722 machinepass = misc.random_password(12)
724 dnspass = misc.random_password(12)
726 root = findnss(pwd.getpwnam, ["root"])[0]
728 nobody = findnss(pwd.getpwnam, ["nobody"])[0]
730 nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
732 users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown",
735 wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
737 backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
739 aci = "# no aci for local ldb"
740 if serverrole is None:
741 serverrole = lp.get("server role")
742 assert serverrole in ("domain controller", "member server")
743 if invocationid is None and serverrole == "domain controller":
744 invocationid = uuid.random()
747 realm = lp.get("realm")
749 if lp.get("realm").upper() != realm.upper():
750 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
751 (lp.get("realm"), realm))
753 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
755 if ldap_backend == "ldapi":
756 # provision-backend will set this path suggested slapd command line / fedorads.inf
757 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
759 assert realm is not None
760 realm = realm.upper()
763 hostname = gethostname().split(".")[0].lower()
766 hostip = gethostbyname(hostname)
768 netbiosname = hostname.upper()
769 if not valid_netbios_name(netbiosname):
770 raise InvalidNetbiosName(netbiosname)
772 dnsdomain = realm.lower()
773 if serverrole == "domain controller":
774 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
776 domain = lp.get("workgroup")
778 if lp.get("workgroup").upper() != domain.upper():
779 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
780 lp.get("workgroup"), domain)
782 assert domain is not None
783 domain = domain.upper()
784 if not valid_netbios_name(domain):
785 raise InvalidNetbiosName(domain)
787 domaindn = "CN=" + netbiosname
793 configdn = "CN=Configuration," + rootdn
794 schemadn = "CN=Schema," + configdn
796 message("set DOMAIN SID: %s" % str(domainsid))
797 message("Provisioning for %s in realm %s" % (domain, realm))
798 message("Using administrator password: %s" % adminpass)
800 assert paths.smbconf is not None
802 # only install a new smb.conf if there isn't one there already
803 if not os.path.exists(paths.smbconf):
804 message("Setting up smb.conf")
805 if serverrole == "domain controller":
807 elif serverrole == "member":
808 smbconfsuffix = "member"
809 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
811 "HOSTNAME": hostname,
812 "DOMAIN_CONF": domain,
814 "SERVERROLE": serverrole,
815 "NETLOGONPATH": paths.netlogon,
816 "SYSVOLPATH": paths.sysvol,
818 lp.load(paths.smbconf)
820 # only install a new shares config db if there is none
821 if not os.path.exists(paths.shareconf):
822 message("Setting up share.ldb")
823 share_ldb = Ldb(paths.shareconf, session_info=session_info,
824 credentials=credentials, lp=lp)
825 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
828 message("Setting up secrets.ldb")
829 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
830 session_info=session_info,
831 credentials=credentials, lp=lp)
833 message("Setting up the registry")
834 setup_registry(paths.hklm, setup_path, session_info,
835 credentials=credentials, lp=lp)
837 message("Setting up templates db")
838 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
839 credentials=credentials, lp=lp)
841 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
842 credentials=credentials, lp=lp, schemadn=schemadn,
843 configdn=configdn, domaindn=domaindn,
844 dnsdomain=dnsdomain, netbiosname=netbiosname,
845 realm=realm, message=message, hostname=hostname,
846 rootdn=rootdn, erase=erase, domainsid=domainsid,
847 aci=aci, domainguid=domainguid, policyguid=policyguid,
848 domainname=domain, fill=samdb_fill,
849 adminpass=adminpass, krbtgtpass=krbtgtpass,
850 hostguid=hostguid, invocationid=invocationid,
851 machinepass=machinepass, dnspass=dnspass,
852 serverrole=serverrole, ldap_backend=ldap_backend,
853 ldap_backend_type=ldap_backend_type)
855 if lp.get("server role") == "domain controller":
856 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
857 "{" + policyguid + "}")
858 os.makedirs(policy_path, 0755)
859 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
860 os.makedirs(os.path.join(policy_path, "User"), 0755)
861 if not os.path.isdir(paths.netlogon):
862 os.makedirs(paths.netlogon, 0755)
863 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
864 credentials=credentials, lp=lp)
865 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
866 netbiosname=netbiosname, domainsid=domainsid,
867 keytab_path=paths.keytab, samdb_url=paths.samdb,
868 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
869 machinepass=machinepass, dnsdomain=dnsdomain)
871 if samdb_fill == FILL_FULL:
872 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
873 nobody=nobody, nogroup=nogroup, wheel=wheel,
874 users=users, backup=backup)
876 message("Setting up sam.ldb rootDSE marking as synchronized")
877 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
879 message("Setting up phpLDAPadmin configuration")
880 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
883 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
885 if lp.get("server role") == "domain controller":
886 samdb = SamDB(paths.samdb, session_info=session_info,
887 credentials=credentials, lp=lp)
889 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
890 assert isinstance(domainguid, str)
891 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
892 expression="(&(objectClass=computer)(cn=%s))" % hostname,
894 assert isinstance(hostguid, str)
896 message("Setting up DNS zone: %s" % dnsdomain)
897 create_zone_file(paths.dns, setup_path, samdb,
898 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
899 domaindn=domaindn, dnspass=dnspass, realm=realm,
900 domainguid=domainguid, hostguid=hostguid)
901 message("Please install the zone located in %s into your DNS server" % paths.dns)
906 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
907 """Create a PHP LDAP admin configuration file.
909 :param path: Path to write the configuration to.
910 :param setup_path: Function to generate setup paths.
912 setup_file(setup_path("phpldapadmin-config.php"), path,
913 {"S4_LDAPI_URI": ldapi_uri})
916 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
917 hostip, hostname, dnspass, realm, domainguid, hostguid):
918 """Write out a DNS zone file, from the info in the current database.
920 :param path: Path of the new file.
921 :param setup_path": Setup path function.
922 :param samdb: SamDB object
923 :param dnsdomain: DNS Domain name
924 :param domaindn: DN of the Domain
925 :param hostip: Local IP
926 :param hostname: Local hostname
927 :param dnspass: Password for DNS
928 :param realm: Realm name
929 :param domainguid: GUID of the domain.
930 :param hostguid: GUID of the host.
932 assert isinstance(domainguid, str)
934 setup_file(setup_path("provision.zone"), path, {
935 "DNSPASS_B64": b64encode(dnspass),
936 "HOSTNAME": hostname,
937 "DNSDOMAIN": dnsdomain,
940 "DOMAINGUID": domainguid,
941 "DATESTRING": time.strftime("%Y%m%d%H"),
942 "DEFAULTSITE": DEFAULTSITE,
943 "HOSTGUID": hostguid,
947 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
948 """Load schema for the SamDB.
950 :param samdb: Load a schema into a SamDB.
951 :param setup_path: Setup path function.
952 :param schemadn: DN of the schema
953 :param netbiosname: NetBIOS name of the host.
954 :param configdn: DN of the configuration
956 schema_data = open(setup_path("schema.ldif"), 'r').read()
957 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
958 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
959 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
960 head_data = substitute_var(head_data, {
961 "SCHEMADN": schemadn,
962 "NETBIOSNAME": netbiosname,
963 "CONFIGDN": configdn,
964 "DEFAULTSITE": DEFAULTSITE
966 samdb.attach_schema_from_ldif(head_data, schema_data)