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 :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 samdb = SamDB(samdb_path, session_info=session_info,
291 credentials=credentials, lp=lp)
297 os.unlink(samdb_path)
299 samdb = SamDB(samdb_path, session_info=session_info,
300 credentials=credentials, lp=lp)
302 #Add modules to the list to activate them by default
303 #beware often order is important
305 # Some Known ordering constraints:
306 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
307 # - objectclass must be before password_hash, because password_hash checks
308 # that the objectclass is of type person (filled in by objectclass
309 # module when expanding the objectclass list)
310 # - partition must be last
311 # - each partition has its own module list then
312 modules_list = ["rootdse",
328 modules_list2 = ["show_deleted",
331 domaindn_ldb = "users.ldb"
332 if ldap_backend is not None:
333 domaindn_ldb = ldap_backend
334 configdn_ldb = "configuration.ldb"
335 if ldap_backend is not None:
336 configdn_ldb = ldap_backend
337 schemadn_ldb = "schema.ldb"
338 if ldap_backend is not None:
339 schema_ldb = ldap_backend
340 schemadn_ldb = ldap_backend
342 if ldap_backend_type == "fedora-ds":
343 backend_modules = ["nsuniqueid", "paged_searches"]
344 elif ldap_backend_type == "openldap":
345 backend_modules = ["normalise", "entryuuid", "paged_searches"]
346 elif serverrole == "domain controller":
347 backend_modules = ["repl_meta_data"]
349 backend_modules = ["objectguid"]
351 samdb.transaction_start()
353 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
354 "SCHEMADN": schemadn,
355 "SCHEMADN_LDB": schemadn_ldb,
356 "SCHEMADN_MOD2": ",objectguid",
357 "CONFIGDN": configdn,
358 "CONFIGDN_LDB": configdn_ldb,
359 "DOMAINDN": domaindn,
360 "DOMAINDN_LDB": domaindn_ldb,
361 "SCHEMADN_MOD": "schema_fsmo,instancetype",
362 "CONFIGDN_MOD": "naming_fsmo,instancetype",
363 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
364 "MODULES_LIST": ",".join(modules_list),
365 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
366 "MODULES_LIST2": ",".join(modules_list2),
367 "BACKEND_MOD": ",".join(backend_modules),
371 samdb.transaction_cancel()
374 samdb.transaction_commit()
376 samdb = SamDB(samdb_path, session_info=session_info,
377 credentials=credentials, lp=lp)
379 samdb.transaction_start()
381 message("Setting up sam.ldb attributes")
382 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
384 message("Setting up sam.ldb rootDSE")
385 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
386 dnsdomain, realm, rootdn, configdn, netbiosname,
390 message("Erasing data from partitions")
391 samdb.erase_partitions()
394 samdb.transaction_cancel()
397 samdb.transaction_commit()
402 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
403 netbiosname, domainsid, keytab_path, samdb_url,
404 dns_keytab_path, dnspass, machinepass):
405 """Add DC-specific bits to a secrets database.
407 :param secretsdb: Ldb Handle to the secrets database
408 :param setup_path: Setup path function
409 :param machinepass: Machine password
411 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
412 "MACHINEPASS_B64": b64encode(machinepass),
415 "DNSDOMAIN": dnsdomain,
416 "DOMAINSID": str(domainsid),
417 "SECRETS_KEYTAB": keytab_path,
418 "NETBIOSNAME": netbiosname,
419 "SAM_LDB": samdb_url,
420 "DNS_KEYTAB": dns_keytab_path,
421 "DNSPASS_B64": b64encode(dnspass),
425 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
426 """Setup the secrets database.
428 :param path: Path to the secrets database.
429 :param setup_path: Get the path to a setup file.
430 :param session_info: Session info.
431 :param credentials: Credentials
432 :param lp: Loadparm context
433 :return: LDB handle for the created secrets database
435 if os.path.exists(path):
437 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
440 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
441 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
443 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
447 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
448 """Setup the templates database.
450 :param path: Path to the database.
451 :param setup_path: Function for obtaining the path to setup files.
452 :param session_info: Session info
453 :param credentials: Credentials
454 :param lp: Loadparm context
456 templates_ldb = SamDB(path, session_info=session_info,
457 credentials=credentials, lp=lp)
458 templates_ldb.erase()
459 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
462 def setup_registry(path, setup_path, session_info, credentials, lp):
463 """Setup the registry.
465 :param path: Path to the registry database
466 :param setup_path: Function that returns the path to a setup.
467 :param session_info: Session information
468 :param credentials: Credentials
469 :param lp: Loadparm context
471 reg = registry.Registry()
472 hive = registry.open_ldb(path, session_info=session_info,
473 credentials=credentials, lp_ctx=lp)
474 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
475 provision_reg = setup_path("provision.reg")
476 assert os.path.exists(provision_reg)
477 reg.diff_apply(provision_reg)
479 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
480 """Setup the idmap database.
482 :param path: path to the idmap database
483 :param setup_path: Function that returns a path to a setup file
484 :param session_info: Session information
485 :param credentials: Credentials
486 :param lp: Loadparm context
488 if os.path.exists(path):
491 idmap_ldb = Ldb(path, session_info=session_info, credentials=credentials,
495 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
498 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
499 dnsdomain, realm, rootdn, configdn, netbiosname,
501 """Setup the SamDB rootdse.
503 :param samdb: Sam Database handle
504 :param setup_path: Obtain setup path
506 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
507 "SCHEMADN": schemadn,
508 "NETBIOSNAME": netbiosname,
509 "DNSDOMAIN": dnsdomain,
510 "DEFAULTSITE": sitename,
512 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
513 "DOMAINDN": domaindn,
515 "CONFIGDN": configdn,
516 "VERSION": samba.version(),
520 def setup_self_join(samdb, configdn, schemadn, domaindn,
521 netbiosname, hostname, dnsdomain, machinepass, dnspass,
522 realm, domainname, domainsid, invocationid, setup_path,
523 policyguid, sitename, hostguid=None):
524 """Join a host to its own domain."""
525 if hostguid is not None:
526 hostguid_add = "objectGUID: %s" % hostguid
530 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
531 "CONFIGDN": configdn,
532 "SCHEMADN": schemadn,
533 "DOMAINDN": domaindn,
534 "INVOCATIONID": invocationid,
535 "NETBIOSNAME": netbiosname,
536 "DEFAULTSITE": sitename,
537 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
538 "MACHINEPASS_B64": b64encode(machinepass),
539 "DNSPASS_B64": b64encode(dnspass),
541 "DOMAIN": domainname,
542 "HOSTGUID_ADD": hostguid_add,
543 "DNSDOMAIN": dnsdomain})
544 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
545 "POLICYGUID": policyguid,
546 "DNSDOMAIN": dnsdomain,
547 "DOMAINSID": str(domainsid),
548 "DOMAINDN": domaindn})
551 def setup_samdb(path, setup_path, session_info, credentials, lp,
552 schemadn, configdn, domaindn, dnsdomain, realm,
553 netbiosname, message, hostname, rootdn,
554 domainsid, aci, domainguid, policyguid,
555 domainname, fill, adminpass, krbtgtpass,
556 machinepass, hostguid, invocationid, dnspass,
557 serverrole, sitename, ldap_backend=None,
558 ldap_backend_type=None):
559 """Setup a complete SAM Database.
561 :note: This will wipe the main SAM database file!
564 assert serverrole in ("domain controller", "member server")
566 erase = (fill != FILL_DRS)
568 # Also wipes the database
569 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
570 domaindn=domaindn, message=message, lp=lp,
571 credentials=credentials, session_info=session_info,
572 hostname=hostname, netbiosname=netbiosname,
573 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
574 ldap_backend=ldap_backend, serverrole=serverrole,
575 ldap_backend_type=ldap_backend_type, erase=erase,
578 samdb = SamDB(path, session_info=session_info,
579 credentials=credentials, lp=lp)
582 # We want to finish here, but setup the index before we do so
583 message("Setting up sam.ldb index")
584 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
587 message("Pre-loading the Samba 4 and AD schema")
588 samdb = SamDB(path, session_info=session_info,
589 credentials=credentials, lp=lp)
590 samdb.set_domain_sid(domainsid)
591 if lp.get("server role") == "domain controller":
592 samdb.set_invocation_id(invocationid)
594 load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename)
596 samdb.transaction_start()
599 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
600 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
601 "DOMAINDN": domaindn,
605 message("Modifying DomainDN: " + domaindn + "")
606 if domainguid is not None:
607 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
611 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
612 "LDAPTIME": timestring(int(time.time())),
613 "DOMAINSID": str(domainsid),
614 "SCHEMADN": schemadn,
615 "NETBIOSNAME": netbiosname,
616 "DEFAULTSITE": sitename,
617 "CONFIGDN": configdn,
618 "POLICYGUID": policyguid,
619 "DOMAINDN": domaindn,
620 "DOMAINGUID_MOD": domainguid_mod,
623 message("Adding configuration container (permitted to fail)")
624 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
625 "CONFIGDN": configdn,
627 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
629 message("Modifying configuration container")
630 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
631 "CONFIGDN": configdn,
632 "SCHEMADN": schemadn,
635 message("Adding schema container (permitted to fail)")
636 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
637 "SCHEMADN": schemadn,
639 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
641 message("Modifying schema container")
642 setup_modify_ldif(samdb,
643 setup_path("provision_schema_basedn_modify.ldif"), {
644 "SCHEMADN": schemadn,
645 "NETBIOSNAME": netbiosname,
646 "DEFAULTSITE": sitename,
647 "CONFIGDN": configdn,
650 message("Setting up sam.ldb Samba4 schema")
651 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
652 {"SCHEMADN": schemadn })
653 message("Setting up sam.ldb AD schema")
654 setup_add_ldif(samdb, setup_path("schema.ldif"),
655 {"SCHEMADN": schemadn})
657 message("Setting up sam.ldb configuration data")
658 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
659 "CONFIGDN": configdn,
660 "NETBIOSNAME": netbiosname,
661 "DEFAULTSITE": sitename,
662 "DNSDOMAIN": dnsdomain,
663 "DOMAIN": domainname,
664 "SCHEMADN": schemadn,
665 "DOMAINDN": domaindn,
668 message("Setting up display specifiers")
669 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
670 {"CONFIGDN": configdn})
672 message("Adding users container (permitted to fail)")
673 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
674 "DOMAINDN": domaindn})
675 message("Modifying users container")
676 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
677 "DOMAINDN": domaindn})
678 message("Adding computers container (permitted to fail)")
679 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
680 "DOMAINDN": domaindn})
681 message("Modifying computers container")
682 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
683 "DOMAINDN": domaindn})
684 message("Setting up sam.ldb data")
685 setup_add_ldif(samdb, setup_path("provision.ldif"), {
686 "DOMAINDN": domaindn,
687 "NETBIOSNAME": netbiosname,
688 "DEFAULTSITE": sitename,
689 "CONFIGDN": configdn,
692 if fill == FILL_FULL:
693 message("Setting up sam.ldb users and groups")
694 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
695 "DOMAINDN": domaindn,
696 "DOMAINSID": str(domainsid),
697 "CONFIGDN": configdn,
698 "ADMINPASS_B64": b64encode(adminpass),
699 "KRBTGTPASS_B64": b64encode(krbtgtpass),
702 if lp.get("server role") == "domain controller":
703 message("Setting up self join")
704 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
705 domaindn=domaindn, invocationid=invocationid,
706 dnspass=dnspass, netbiosname=netbiosname,
707 dnsdomain=dnsdomain, realm=realm,
708 machinepass=machinepass, domainname=domainname,
709 domainsid=domainsid, policyguid=policyguid,
710 hostname=hostname, hostguid=hostguid,
711 setup_path=setup_path, sitename=sitename)
713 #We want to setup the index last, as adds are faster unindexed
714 message("Setting up sam.ldb index")
715 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
717 samdb.transaction_cancel()
720 samdb.transaction_commit()
725 FILL_NT4SYNC = "NT4SYNC"
728 def provision(lp, setup_dir, message, paths, session_info,
729 credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None,
730 domain=None, hostname=None, hostip=None, domainsid=None,
731 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
732 policyguid=None, invocationid=None, machinepass=None,
733 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
734 wheel=None, backup=None, aci=None, serverrole=None,
735 ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
738 :note: caution, this wipes all existing data!
741 def setup_path(file):
742 return os.path.join(setup_dir, file)
744 if domainsid is None:
745 domainsid = security.random_sid()
746 if policyguid is None:
747 policyguid = uuid.random()
748 if adminpass is None:
749 adminpass = misc.random_password(12)
750 if krbtgtpass is None:
751 krbtgtpass = misc.random_password(12)
752 if machinepass is None:
753 machinepass = misc.random_password(12)
755 dnspass = misc.random_password(12)
757 root = findnss(pwd.getpwnam, ["root"])[0]
759 nobody = findnss(pwd.getpwnam, ["nobody"])[0]
761 nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
763 users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown",
766 wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
768 backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
770 aci = "# no aci for local ldb"
771 if serverrole is None:
772 serverrole = lp.get("server role")
773 assert serverrole in ("domain controller", "member server")
774 if invocationid is None and serverrole == "domain controller":
775 invocationid = uuid.random()
778 realm = lp.get("realm")
780 if lp.get("realm").upper() != realm.upper():
781 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
782 (lp.get("realm"), realm))
784 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
786 if ldap_backend == "ldapi":
787 # provision-backend will set this path suggested slapd command line / fedorads.inf
788 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
790 assert realm is not None
791 realm = realm.upper()
794 hostname = gethostname().split(".")[0].lower()
797 hostip = gethostbyname(hostname)
799 netbiosname = hostname.upper()
800 if not valid_netbios_name(netbiosname):
801 raise InvalidNetbiosName(netbiosname)
803 dnsdomain = realm.lower()
804 if serverrole == "domain controller":
805 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
807 domain = lp.get("workgroup")
809 if lp.get("workgroup").upper() != domain.upper():
810 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
811 lp.get("workgroup"), domain)
813 assert domain is not None
814 domain = domain.upper()
815 if not valid_netbios_name(domain):
816 raise InvalidNetbiosName(domain)
818 domaindn = "CN=" + netbiosname
824 configdn = "CN=Configuration," + rootdn
825 schemadn = "CN=Schema," + configdn
827 message("set DOMAIN SID: %s" % str(domainsid))
828 message("Provisioning for %s in realm %s" % (domain, realm))
829 message("Using administrator password: %s" % adminpass)
831 assert paths.smbconf is not None
833 # only install a new smb.conf if there isn't one there already
834 if not os.path.exists(paths.smbconf):
835 message("Setting up smb.conf")
836 if serverrole == "domain controller":
838 elif serverrole == "member server":
839 smbconfsuffix = "member"
840 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
842 "HOSTNAME": hostname,
843 "DOMAIN_CONF": domain,
845 "SERVERROLE": serverrole,
846 "NETLOGONPATH": paths.netlogon,
847 "SYSVOLPATH": paths.sysvol,
849 lp.load(paths.smbconf)
851 # only install a new shares config db if there is none
852 if not os.path.exists(paths.shareconf):
853 message("Setting up share.ldb")
854 share_ldb = Ldb(paths.shareconf, session_info=session_info,
855 credentials=credentials, lp=lp)
856 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
859 message("Setting up secrets.ldb")
860 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
861 session_info=session_info,
862 credentials=credentials, lp=lp)
864 message("Setting up the registry")
865 setup_registry(paths.hklm, setup_path, session_info,
866 credentials=credentials, lp=lp)
868 message("Setting up templates db")
869 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
870 credentials=credentials, lp=lp)
872 message("Setting up idmap db")
873 setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
874 credentials=credentials, lp=lp)
876 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
877 credentials=credentials, lp=lp, schemadn=schemadn,
878 configdn=configdn, domaindn=domaindn,
879 dnsdomain=dnsdomain, netbiosname=netbiosname,
880 realm=realm, message=message, hostname=hostname,
881 rootdn=rootdn, domainsid=domainsid,
882 aci=aci, domainguid=domainguid, policyguid=policyguid,
883 domainname=domain, fill=samdb_fill,
884 adminpass=adminpass, krbtgtpass=krbtgtpass,
885 hostguid=hostguid, invocationid=invocationid,
886 machinepass=machinepass, dnspass=dnspass,
887 serverrole=serverrole, ldap_backend=ldap_backend,
888 ldap_backend_type=ldap_backend_type, sitename=sitename)
890 if lp.get("server role") == "domain controller":
891 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
892 "{" + policyguid + "}")
893 os.makedirs(policy_path, 0755)
894 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
895 os.makedirs(os.path.join(policy_path, "User"), 0755)
896 if not os.path.isdir(paths.netlogon):
897 os.makedirs(paths.netlogon, 0755)
898 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
899 credentials=credentials, lp=lp)
900 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
901 netbiosname=netbiosname, domainsid=domainsid,
902 keytab_path=paths.keytab, samdb_url=paths.samdb,
903 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
904 machinepass=machinepass, dnsdomain=dnsdomain)
906 if samdb_fill == FILL_FULL:
907 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
908 nobody=nobody, nogroup=nogroup, wheel=wheel,
909 users=users, backup=backup)
911 message("Setting up sam.ldb rootDSE marking as synchronized")
912 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
914 message("Setting up phpLDAPadmin configuration")
915 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
918 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
920 if lp.get("server role") == "domain controller":
921 samdb = SamDB(paths.samdb, session_info=session_info,
922 credentials=credentials, lp=lp)
924 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
925 assert isinstance(domainguid, str)
926 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
927 expression="(&(objectClass=computer)(cn=%s))" % hostname,
929 assert isinstance(hostguid, str)
931 message("Setting up DNS zone: %s" % dnsdomain)
932 create_zone_file(paths.dns, setup_path, samdb,
933 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
934 domaindn=domaindn, dnspass=dnspass, realm=realm,
935 domainguid=domainguid, hostguid=hostguid)
936 message("Please install the zone located in %s into your DNS server" % paths.dns)
941 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
942 """Create a PHP LDAP admin configuration file.
944 :param path: Path to write the configuration to.
945 :param setup_path: Function to generate setup paths.
947 setup_file(setup_path("phpldapadmin-config.php"), path,
948 {"S4_LDAPI_URI": ldapi_uri})
951 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
952 hostip, hostname, dnspass, realm, domainguid, hostguid):
953 """Write out a DNS zone file, from the info in the current database.
955 :param path: Path of the new file.
956 :param setup_path": Setup path function.
957 :param samdb: SamDB object
958 :param dnsdomain: DNS Domain name
959 :param domaindn: DN of the Domain
960 :param hostip: Local IP
961 :param hostname: Local hostname
962 :param dnspass: Password for DNS
963 :param realm: Realm name
964 :param domainguid: GUID of the domain.
965 :param hostguid: GUID of the host.
967 assert isinstance(domainguid, str)
969 setup_file(setup_path("provision.zone"), path, {
970 "DNSPASS_B64": b64encode(dnspass),
971 "HOSTNAME": hostname,
972 "DNSDOMAIN": dnsdomain,
975 "DOMAINGUID": domainguid,
976 "DATESTRING": time.strftime("%Y%m%d%H"),
977 "DEFAULTSITE": DEFAULTSITE,
978 "HOSTGUID": hostguid,
982 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
983 """Load schema for the SamDB.
985 :param samdb: Load a schema into a SamDB.
986 :param setup_path: Setup path function.
987 :param schemadn: DN of the schema
988 :param netbiosname: NetBIOS name of the host.
989 :param configdn: DN of the configuration
991 schema_data = open(setup_path("schema.ldif"), 'r').read()
992 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
993 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
994 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
995 head_data = substitute_var(head_data, {
996 "SCHEMADN": schemadn,
997 "NETBIOSNAME": netbiosname,
998 "CONFIGDN": configdn,
999 "DEFAULTSITE":sitename
1001 samdb.attach_schema_from_ldif(head_data, schema_data)