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)
65 self.dns_keytab = None
70 def check_install(lp, session_info, credentials):
71 """Check whether the current install seems ok.
73 :param lp: Loadparm context
74 :param session_info: Session information
75 :param credentials: Credentials
77 if lp.get("realm") == "":
78 raise Error("Realm empty")
79 ldb = Ldb(lp.get("sam database"), session_info=session_info,
80 credentials=credentials, lp=lp)
81 if len(ldb.search("(cn=Administrator)")) != 1:
82 raise "No administrator account found"
85 def findnss(nssfn, names):
86 """Find a user or group from a list of possibilities.
88 :param nssfn: NSS Function to try (should raise KeyError if not found)
89 :param names: Names to check.
90 :return: Value return by first names list.
97 raise KeyError("Unable to find user/group %r" % names)
100 def open_ldb(session_info, credentials, lp, dbname):
101 """Open a LDB, thrashing it if it is corrupt.
103 :param session_info: auth session information
104 :param credentials: credentials
105 :param lp: Loadparm context
106 :param dbname: Path of the database to open.
107 :return: a Ldb object
109 assert session_info is not None
111 return Ldb(dbname, session_info=session_info, credentials=credentials,
116 return Ldb(dbname, session_info=session_info, credentials=credentials,
120 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
121 """Setup a ldb in the private dir.
123 :param ldb: LDB file to import data into
124 :param ldif_path: Path of the LDIF file to load
125 :param subst_vars: Optional variables to subsitute in LDIF.
127 assert isinstance(ldif_path, str)
129 data = open(ldif_path, 'r').read()
130 if subst_vars is not None:
131 data = substitute_var(data, subst_vars)
133 check_all_substituted(data)
138 def setup_modify_ldif(ldb, ldif_path, substvars=None):
139 """Modify a ldb in the private dir.
141 :param ldb: LDB object.
142 :param ldif_path: LDIF file path.
143 :param substvars: Optional dictionary with substitution variables.
145 data = open(ldif_path, 'r').read()
146 if substvars is not None:
147 data = substitute_var(data, substvars)
149 check_all_substituted(data)
151 ldb.modify_ldif(data)
154 def setup_ldb(ldb, ldif_path, subst_vars):
155 """Import a LDIF a file into a LDB handle, optionally substituting variables.
157 :note: Either all LDIF data will be added or none (using transactions).
159 :param ldb: LDB file to import into.
160 :param ldif_path: Path to the LDIF file.
161 :param subst_vars: Dictionary with substitution variables.
163 assert ldb is not None
164 ldb.transaction_start()
166 setup_add_ldif(ldb, ldif_path, subst_vars)
168 ldb.transaction_cancel()
170 ldb.transaction_commit()
173 def setup_file(template, fname, substvars):
174 """Setup a file in the private dir.
176 :param template: Path of the template file.
177 :param fname: Path of the file to create.
178 :param substvars: Substitution variables.
182 if os.path.exists(f):
185 data = open(template, 'r').read()
187 data = substitute_var(data, substvars)
188 check_all_substituted(data)
190 open(f, 'w').write(data)
193 def provision_paths_from_lp(lp, dnsdomain):
194 """Set the default paths for provisioning.
196 :param lp: Loadparm context.
197 :param dnsdomain: DNS Domain name
199 paths = ProvisionPaths()
200 private_dir = lp.get("private dir")
201 paths.keytab = "secrets.keytab"
202 paths.dns_keytab = "dns.keytab"
204 paths.shareconf = os.path.join(private_dir, "share.ldb")
205 paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb")
206 paths.idmapdb = os.path.join(private_dir, lp.get("idmap database") or "idmap.ldb")
207 paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb")
208 paths.templates = os.path.join(private_dir, "templates.ldb")
209 paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
210 paths.winsdb = os.path.join(private_dir, "wins.ldb")
211 paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
212 paths.smbconf = os.path.join(private_dir, "smb.conf")
213 paths.phpldapadminconfig = os.path.join(private_dir,
214 "phpldapadmin-config.php")
215 paths.hklm = "hklm.ldb"
216 paths.hkcr = "hkcr.ldb"
217 paths.hkcu = "hkcu.ldb"
218 paths.hku = "hku.ldb"
219 paths.hkpd = "hkpd.ldb"
220 paths.hkpt = "hkpt.ldb"
222 paths.sysvol = lp.get("sysvol", "path")
223 if paths.sysvol is None:
224 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
226 paths.netlogon = lp.get("netlogon", "path")
227 if paths.netlogon is None:
228 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
233 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
235 """setup reasonable name mappings for sam names to unix names.
237 :param ldb: SamDB object.
238 :param sid: The domain sid.
239 :param domaindn: The domain DN.
240 :param root: Name of the UNIX root user.
241 :param nobody: Name of the UNIX nobody user.
242 :param nogroup: Name of the unix nobody group.
243 :param users: Name of the unix users group.
244 :param wheel: Name of the wheel group (users that can become root).
245 :param backup: Name of the backup group."""
246 # add some foreign sids if they are not present already
247 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
248 ldb.add_foreign(domaindn, "S-1-1-0", "World")
249 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
250 ldb.add_foreign(domaindn, "S-1-5-18", "System")
251 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
253 # some well known sids
254 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
255 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
256 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
257 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
258 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
259 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
260 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
261 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
262 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
264 # and some well known domain rids
265 ldb.setup_name_mapping(domaindn, sid + "-500", root)
266 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
267 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
268 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
269 ldb.setup_name_mapping(domaindn, sid + "-513", users)
270 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
273 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
274 credentials, configdn, schemadn, domaindn,
275 hostname, netbiosname, dnsdomain, realm,
276 rootdn, serverrole, sitename, ldap_backend=None,
277 ldap_backend_type=None, erase=False):
278 """Setup the partitions for the SAM database.
280 Alternatively, provision() may call this, and then populate the database.
282 :param erase: Remove the existing data present in 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 if os.path.exists(samdb_path):
293 os.unlink(samdb_path)
295 # Also wipes the database
296 samdb = SamDB(samdb_path, session_info=session_info,
297 credentials=credentials, lp=lp)
299 #Add modules to the list to activate them by default
300 #beware often order is important
302 # Some Known ordering constraints:
303 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
304 # - objectclass must be before password_hash, because password_hash checks
305 # that the objectclass is of type person (filled in by objectclass
306 # module when expanding the objectclass list)
307 # - partition must be last
308 # - each partition has its own module list then
309 modules_list = ["rootdse",
325 modules_list2 = ["show_deleted",
328 domaindn_ldb = "users.ldb"
329 if ldap_backend is not None:
330 domaindn_ldb = ldap_backend
331 configdn_ldb = "configuration.ldb"
332 if ldap_backend is not None:
333 configdn_ldb = ldap_backend
334 schemadn_ldb = "schema.ldb"
335 if ldap_backend is not None:
336 schema_ldb = ldap_backend
337 schemadn_ldb = ldap_backend
339 if ldap_backend_type == "fedora-ds":
340 backend_modules = ["nsuniqueid", "paged_searches"]
341 elif ldap_backend_type == "openldap":
342 backend_modules = ["normalise", "entryuuid", "paged_searches"]
343 elif serverrole == "domain controller":
344 backend_modules = ["repl_meta_data"]
346 backend_modules = ["objectguid"]
348 samdb.transaction_start()
350 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
351 "SCHEMADN": schemadn,
352 "SCHEMADN_LDB": schemadn_ldb,
353 "SCHEMADN_MOD2": ",objectguid",
354 "CONFIGDN": configdn,
355 "CONFIGDN_LDB": configdn_ldb,
356 "DOMAINDN": domaindn,
357 "DOMAINDN_LDB": domaindn_ldb,
358 "SCHEMADN_MOD": "schema_fsmo,instancetype",
359 "CONFIGDN_MOD": "naming_fsmo,instancetype",
360 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
361 "MODULES_LIST": ",".join(modules_list),
362 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
363 "MODULES_LIST2": ",".join(modules_list2),
364 "BACKEND_MOD": ",".join(backend_modules),
368 samdb.transaction_cancel()
371 samdb.transaction_commit()
373 samdb = SamDB(samdb_path, session_info=session_info,
374 credentials=credentials, lp=lp)
376 samdb.transaction_start()
378 message("Setting up sam.ldb attributes")
379 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
381 message("Setting up sam.ldb rootDSE")
382 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
383 dnsdomain, realm, rootdn, configdn, netbiosname,
387 message("Erasing data from partitions")
388 samdb.erase_partitions()
391 samdb.transaction_cancel()
394 samdb.transaction_commit()
399 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
400 netbiosname, domainsid, keytab_path, samdb_url,
401 dns_keytab_path, dnspass, machinepass):
402 """Add DC-specific bits to a secrets database.
404 :param secretsdb: Ldb Handle to the secrets database
405 :param setup_path: Setup path function
406 :param machinepass: Machine password
408 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
409 "MACHINEPASS_B64": b64encode(machinepass),
412 "DNSDOMAIN": dnsdomain,
413 "DOMAINSID": str(domainsid),
414 "SECRETS_KEYTAB": keytab_path,
415 "NETBIOSNAME": netbiosname,
416 "SAM_LDB": samdb_url,
417 "DNS_KEYTAB": dns_keytab_path,
418 "DNSPASS_B64": b64encode(dnspass),
422 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
423 """Setup the secrets database.
425 :param path: Path to the secrets database.
426 :param setup_path: Get the path to a setup file.
427 :param session_info: Session info.
428 :param credentials: Credentials
429 :param lp: Loadparm context
430 :return: LDB handle for the created secrets database
432 if os.path.exists(path):
434 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
437 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
438 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
440 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
444 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
445 """Setup the templates database.
447 :param path: Path to the database.
448 :param setup_path: Function for obtaining the path to setup files.
449 :param session_info: Session info
450 :param credentials: Credentials
451 :param lp: Loadparm context
453 templates_ldb = SamDB(path, session_info=session_info,
454 credentials=credentials, lp=lp)
455 templates_ldb.erase()
456 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
459 def setup_registry(path, setup_path, session_info, credentials, lp):
460 """Setup the registry.
462 :param path: Path to the registry database
463 :param setup_path: Function that returns the path to a setup.
464 :param session_info: Session information
465 :param credentials: Credentials
466 :param lp: Loadparm context
468 reg = registry.Registry()
469 hive = registry.open_ldb(path, session_info=session_info,
470 credentials=credentials, lp_ctx=lp)
471 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
472 provision_reg = setup_path("provision.reg")
473 assert os.path.exists(provision_reg)
474 reg.diff_apply(provision_reg)
476 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
477 """Setup the idmap database.
479 :param path: path to the idmap database
480 :param setup_path: Function that returns a path to a setup file
481 :param session_info: Session information
482 :param credentials: Credentials
483 :param lp: Loadparm context
485 if os.path.exists(path):
488 idmap_ldb = Ldb(path, session_info=session_info, credentials=credentials,
492 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
495 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
496 dnsdomain, realm, rootdn, configdn, netbiosname,
498 """Setup the SamDB rootdse.
500 :param samdb: Sam Database handle
501 :param setup_path: Obtain setup path
503 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
504 "SCHEMADN": schemadn,
505 "NETBIOSNAME": netbiosname,
506 "DNSDOMAIN": dnsdomain,
507 "DEFAULTSITE": sitename,
509 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
510 "DOMAINDN": domaindn,
512 "CONFIGDN": configdn,
513 "VERSION": samba.version(),
517 def setup_self_join(samdb, configdn, schemadn, domaindn,
518 netbiosname, hostname, dnsdomain, machinepass, dnspass,
519 realm, domainname, domainsid, invocationid, setup_path,
520 policyguid, sitename, hostguid=None):
521 """Join a host to its own domain."""
522 if hostguid is not None:
523 hostguid_add = "objectGUID: %s" % hostguid
527 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
528 "CONFIGDN": configdn,
529 "SCHEMADN": schemadn,
530 "DOMAINDN": domaindn,
531 "INVOCATIONID": invocationid,
532 "NETBIOSNAME": netbiosname,
533 "DEFAULTSITE": sitename,
534 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
535 "MACHINEPASS_B64": b64encode(machinepass),
536 "DNSPASS_B64": b64encode(dnspass),
538 "DOMAIN": domainname,
539 "HOSTGUID_ADD": hostguid_add,
540 "DNSDOMAIN": dnsdomain})
541 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
542 "POLICYGUID": policyguid,
543 "DNSDOMAIN": dnsdomain,
544 "DOMAINSID": str(domainsid),
545 "DOMAINDN": domaindn})
548 def setup_samdb(path, setup_path, session_info, credentials, lp,
549 schemadn, configdn, domaindn, dnsdomain, realm,
550 netbiosname, message, hostname, rootdn, erase,
551 domainsid, aci, domainguid, policyguid,
552 domainname, fill, adminpass, krbtgtpass,
553 machinepass, hostguid, invocationid, dnspass,
554 serverrole, sitename, ldap_backend=None,
555 ldap_backend_type=None):
556 """Setup a complete SAM Database.
558 :note: This will wipe the main SAM database file!
561 assert serverrole in ("domain controller", "member server")
563 # Also wipes the database
564 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
565 domaindn=domaindn, message=message, lp=lp,
566 credentials=credentials, session_info=session_info,
567 hostname=hostname, netbiosname=netbiosname,
568 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
569 ldap_backend=ldap_backend, serverrole=serverrole,
570 ldap_backend_type=ldap_backend_type, erase=erase,
573 samdb = SamDB(path, session_info=session_info,
574 credentials=credentials, lp=lp)
577 # We want to finish here, but setup the index before we do so
578 message("Setting up sam.ldb index")
579 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
582 message("Pre-loading the Samba 4 and AD schema")
583 samdb = SamDB(path, session_info=session_info,
584 credentials=credentials, lp=lp)
585 samdb.set_domain_sid(domainsid)
586 if lp.get("server role") == "domain controller":
587 samdb.set_invocation_id(invocationid)
589 load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename)
591 samdb.transaction_start()
594 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
595 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
596 "DOMAINDN": domaindn,
600 message("Modifying DomainDN: " + domaindn + "")
601 if domainguid is not None:
602 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
606 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
607 "LDAPTIME": timestring(int(time.time())),
608 "DOMAINSID": str(domainsid),
609 "SCHEMADN": schemadn,
610 "NETBIOSNAME": netbiosname,
611 "DEFAULTSITE": sitename,
612 "CONFIGDN": configdn,
613 "POLICYGUID": policyguid,
614 "DOMAINDN": domaindn,
615 "DOMAINGUID_MOD": domainguid_mod,
618 message("Adding configuration container (permitted to fail)")
619 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
620 "CONFIGDN": configdn,
622 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
624 message("Modifying configuration container")
625 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
626 "CONFIGDN": configdn,
627 "SCHEMADN": schemadn,
630 message("Adding schema container (permitted to fail)")
631 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
632 "SCHEMADN": schemadn,
634 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
636 message("Modifying schema container")
637 setup_modify_ldif(samdb,
638 setup_path("provision_schema_basedn_modify.ldif"), {
639 "SCHEMADN": schemadn,
640 "NETBIOSNAME": netbiosname,
641 "DEFAULTSITE": sitename,
642 "CONFIGDN": configdn,
645 message("Setting up sam.ldb Samba4 schema")
646 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
647 {"SCHEMADN": schemadn })
648 message("Setting up sam.ldb AD schema")
649 setup_add_ldif(samdb, setup_path("schema.ldif"),
650 {"SCHEMADN": schemadn})
652 message("Setting up sam.ldb configuration data")
653 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
654 "CONFIGDN": configdn,
655 "NETBIOSNAME": netbiosname,
656 "DEFAULTSITE": sitename,
657 "DNSDOMAIN": dnsdomain,
658 "DOMAIN": domainname,
659 "SCHEMADN": schemadn,
660 "DOMAINDN": domaindn,
663 message("Setting up display specifiers")
664 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
665 {"CONFIGDN": configdn})
667 message("Adding users container (permitted to fail)")
668 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
669 "DOMAINDN": domaindn})
670 message("Modifying users container")
671 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
672 "DOMAINDN": domaindn})
673 message("Adding computers container (permitted to fail)")
674 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
675 "DOMAINDN": domaindn})
676 message("Modifying computers container")
677 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
678 "DOMAINDN": domaindn})
679 message("Setting up sam.ldb data")
680 setup_add_ldif(samdb, setup_path("provision.ldif"), {
681 "DOMAINDN": domaindn,
682 "NETBIOSNAME": netbiosname,
683 "DEFAULTSITE": sitename,
684 "CONFIGDN": configdn,
687 if fill == FILL_FULL:
688 message("Setting up sam.ldb users and groups")
689 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
690 "DOMAINDN": domaindn,
691 "DOMAINSID": str(domainsid),
692 "CONFIGDN": configdn,
693 "ADMINPASS_B64": b64encode(adminpass),
694 "KRBTGTPASS_B64": b64encode(krbtgtpass),
697 if lp.get("server role") == "domain controller":
698 message("Setting up self join")
699 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
700 domaindn=domaindn, invocationid=invocationid,
701 dnspass=dnspass, netbiosname=netbiosname,
702 dnsdomain=dnsdomain, realm=realm,
703 machinepass=machinepass, domainname=domainname,
704 domainsid=domainsid, policyguid=policyguid,
705 hostname=hostname, hostguid=hostguid,
706 setup_path=setup_path, sitename=sitename)
708 #We want to setup the index last, as adds are faster unindexed
709 message("Setting up sam.ldb index")
710 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
712 samdb.transaction_cancel()
715 samdb.transaction_commit()
720 FILL_NT4SYNC = "NT4SYNC"
723 def provision(lp, setup_dir, message, paths, session_info,
724 credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None,
725 domain=None, hostname=None, hostip=None, domainsid=None,
726 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
727 policyguid=None, invocationid=None, machinepass=None,
728 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
729 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
730 ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
733 :note: caution, this wipes all existing data!
736 def setup_path(file):
737 return os.path.join(setup_dir, file)
739 if domainsid is None:
740 domainsid = security.random_sid()
741 if policyguid is None:
742 policyguid = uuid.random()
743 if adminpass is None:
744 adminpass = misc.random_password(12)
745 if krbtgtpass is None:
746 krbtgtpass = misc.random_password(12)
747 if machinepass is None:
748 machinepass = misc.random_password(12)
750 dnspass = misc.random_password(12)
752 root = findnss(pwd.getpwnam, ["root"])[0]
754 nobody = findnss(pwd.getpwnam, ["nobody"])[0]
756 nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
758 users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown",
761 wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
763 backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
765 aci = "# no aci for local ldb"
766 if serverrole is None:
767 serverrole = lp.get("server role")
768 assert serverrole in ("domain controller", "member server")
769 if invocationid is None and serverrole == "domain controller":
770 invocationid = uuid.random()
773 realm = lp.get("realm")
775 if lp.get("realm").upper() != realm.upper():
776 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
777 (lp.get("realm"), realm))
779 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
781 if ldap_backend == "ldapi":
782 # provision-backend will set this path suggested slapd command line / fedorads.inf
783 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
785 assert realm is not None
786 realm = realm.upper()
789 hostname = gethostname().split(".")[0].lower()
792 hostip = gethostbyname(hostname)
794 netbiosname = hostname.upper()
795 if not valid_netbios_name(netbiosname):
796 raise InvalidNetbiosName(netbiosname)
798 dnsdomain = realm.lower()
799 if serverrole == "domain controller":
800 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
802 domain = lp.get("workgroup")
804 if lp.get("workgroup").upper() != domain.upper():
805 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
806 lp.get("workgroup"), domain)
808 assert domain is not None
809 domain = domain.upper()
810 if not valid_netbios_name(domain):
811 raise InvalidNetbiosName(domain)
813 domaindn = "CN=" + netbiosname
819 configdn = "CN=Configuration," + rootdn
820 schemadn = "CN=Schema," + configdn
822 message("set DOMAIN SID: %s" % str(domainsid))
823 message("Provisioning for %s in realm %s" % (domain, realm))
824 message("Using administrator password: %s" % adminpass)
826 assert paths.smbconf is not None
828 # only install a new smb.conf if there isn't one there already
829 if not os.path.exists(paths.smbconf):
830 message("Setting up smb.conf")
831 if serverrole == "domain controller":
833 elif serverrole == "member":
834 smbconfsuffix = "member"
835 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
837 "HOSTNAME": hostname,
838 "DOMAIN_CONF": domain,
840 "SERVERROLE": serverrole,
841 "NETLOGONPATH": paths.netlogon,
842 "SYSVOLPATH": paths.sysvol,
844 lp.load(paths.smbconf)
846 # only install a new shares config db if there is none
847 if not os.path.exists(paths.shareconf):
848 message("Setting up share.ldb")
849 share_ldb = Ldb(paths.shareconf, session_info=session_info,
850 credentials=credentials, lp=lp)
851 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
854 message("Setting up secrets.ldb")
855 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
856 session_info=session_info,
857 credentials=credentials, lp=lp)
859 message("Setting up the registry")
860 setup_registry(paths.hklm, setup_path, session_info,
861 credentials=credentials, lp=lp)
863 message("Setting up templates db")
864 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
865 credentials=credentials, lp=lp)
867 message("Setting up idmap db")
868 setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
869 credentials=credentials, lp=lp)
871 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
872 credentials=credentials, lp=lp, schemadn=schemadn,
873 configdn=configdn, domaindn=domaindn,
874 dnsdomain=dnsdomain, netbiosname=netbiosname,
875 realm=realm, message=message, hostname=hostname,
876 rootdn=rootdn, erase=erase, domainsid=domainsid,
877 aci=aci, domainguid=domainguid, policyguid=policyguid,
878 domainname=domain, fill=samdb_fill,
879 adminpass=adminpass, krbtgtpass=krbtgtpass,
880 hostguid=hostguid, invocationid=invocationid,
881 machinepass=machinepass, dnspass=dnspass,
882 serverrole=serverrole, ldap_backend=ldap_backend,
883 ldap_backend_type=ldap_backend_type, sitename=sitename)
885 if lp.get("server role") == "domain controller":
886 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
887 "{" + policyguid + "}")
888 os.makedirs(policy_path, 0755)
889 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
890 os.makedirs(os.path.join(policy_path, "User"), 0755)
891 if not os.path.isdir(paths.netlogon):
892 os.makedirs(paths.netlogon, 0755)
893 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
894 credentials=credentials, lp=lp)
895 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
896 netbiosname=netbiosname, domainsid=domainsid,
897 keytab_path=paths.keytab, samdb_url=paths.samdb,
898 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
899 machinepass=machinepass, dnsdomain=dnsdomain)
901 if samdb_fill == FILL_FULL:
902 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
903 nobody=nobody, nogroup=nogroup, wheel=wheel,
904 users=users, backup=backup)
906 message("Setting up sam.ldb rootDSE marking as synchronized")
907 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
909 message("Setting up phpLDAPadmin configuration")
910 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
913 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
915 if lp.get("server role") == "domain controller":
916 samdb = SamDB(paths.samdb, session_info=session_info,
917 credentials=credentials, lp=lp)
919 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
920 assert isinstance(domainguid, str)
921 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
922 expression="(&(objectClass=computer)(cn=%s))" % hostname,
924 assert isinstance(hostguid, str)
926 message("Setting up DNS zone: %s" % dnsdomain)
927 create_zone_file(paths.dns, setup_path, samdb,
928 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
929 domaindn=domaindn, dnspass=dnspass, realm=realm,
930 domainguid=domainguid, hostguid=hostguid)
931 message("Please install the zone located in %s into your DNS server" % paths.dns)
936 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
937 """Create a PHP LDAP admin configuration file.
939 :param path: Path to write the configuration to.
940 :param setup_path: Function to generate setup paths.
942 setup_file(setup_path("phpldapadmin-config.php"), path,
943 {"S4_LDAPI_URI": ldapi_uri})
946 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
947 hostip, hostname, dnspass, realm, domainguid, hostguid):
948 """Write out a DNS zone file, from the info in the current database.
950 :param path: Path of the new file.
951 :param setup_path": Setup path function.
952 :param samdb: SamDB object
953 :param dnsdomain: DNS Domain name
954 :param domaindn: DN of the Domain
955 :param hostip: Local IP
956 :param hostname: Local hostname
957 :param dnspass: Password for DNS
958 :param realm: Realm name
959 :param domainguid: GUID of the domain.
960 :param hostguid: GUID of the host.
962 assert isinstance(domainguid, str)
964 setup_file(setup_path("provision.zone"), path, {
965 "DNSPASS_B64": b64encode(dnspass),
966 "HOSTNAME": hostname,
967 "DNSDOMAIN": dnsdomain,
970 "DOMAINGUID": domainguid,
971 "DATESTRING": time.strftime("%Y%m%d%H"),
972 "DEFAULTSITE": DEFAULTSITE,
973 "HOSTGUID": hostguid,
977 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
978 """Load schema for the SamDB.
980 :param samdb: Load a schema into a SamDB.
981 :param setup_path: Setup path function.
982 :param schemadn: DN of the schema
983 :param netbiosname: NetBIOS name of the host.
984 :param configdn: DN of the configuration
986 schema_data = open(setup_path("schema.ldif"), 'r').read()
987 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
988 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
989 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
990 head_data = substitute_var(head_data, {
991 "SCHEMADN": schemadn,
992 "NETBIOSNAME": netbiosname,
993 "CONFIGDN": configdn,
994 "DEFAULTSITE":sitename
996 samdb.attach_schema_from_ldif(head_data, schema_data)