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 # We can handle linked attributes here, as we don't have directory-side subtree operations
345 tdb_modules_list = ["linked_attributes"]
346 elif ldap_backend_type == "openldap":
347 backend_modules = ["normalise", "entryuuid", "paged_searches"]
348 # OpenLDAP handles subtree renames, so we don't want to do any of these things
349 tdb_modules_list = None
350 elif serverrole == "domain controller":
351 backend_modules = ["repl_meta_data"]
353 backend_modules = ["objectguid"]
355 if tdb_modules_list is None:
356 tdb_modules_list_as_string = ""
358 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
360 samdb.transaction_start()
362 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
363 "SCHEMADN": schemadn,
364 "SCHEMADN_LDB": schemadn_ldb,
365 "SCHEMADN_MOD2": ",objectguid",
366 "CONFIGDN": configdn,
367 "CONFIGDN_LDB": configdn_ldb,
368 "DOMAINDN": domaindn,
369 "DOMAINDN_LDB": domaindn_ldb,
370 "SCHEMADN_MOD": "schema_fsmo,instancetype",
371 "CONFIGDN_MOD": "naming_fsmo,instancetype",
372 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
373 "MODULES_LIST": ",".join(modules_list),
374 "TDB_MODULES_LIST": tdb_modules_list_as_string,
375 "MODULES_LIST2": ",".join(modules_list2),
376 "BACKEND_MOD": ",".join(backend_modules),
380 samdb.transaction_cancel()
383 samdb.transaction_commit()
385 samdb = SamDB(samdb_path, session_info=session_info,
386 credentials=credentials, lp=lp)
388 samdb.transaction_start()
390 message("Setting up sam.ldb attributes")
391 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
393 message("Setting up sam.ldb rootDSE")
394 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
395 dnsdomain, realm, rootdn, configdn, netbiosname,
399 message("Erasing data from partitions")
400 samdb.erase_partitions()
403 samdb.transaction_cancel()
406 samdb.transaction_commit()
411 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
412 netbiosname, domainsid, keytab_path, samdb_url,
413 dns_keytab_path, dnspass, machinepass):
414 """Add DC-specific bits to a secrets database.
416 :param secretsdb: Ldb Handle to the secrets database
417 :param setup_path: Setup path function
418 :param machinepass: Machine password
420 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
421 "MACHINEPASS_B64": b64encode(machinepass),
424 "DNSDOMAIN": dnsdomain,
425 "DOMAINSID": str(domainsid),
426 "SECRETS_KEYTAB": keytab_path,
427 "NETBIOSNAME": netbiosname,
428 "SAM_LDB": samdb_url,
429 "DNS_KEYTAB": dns_keytab_path,
430 "DNSPASS_B64": b64encode(dnspass),
434 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
435 """Setup the secrets database.
437 :param path: Path to the secrets database.
438 :param setup_path: Get the path to a setup file.
439 :param session_info: Session info.
440 :param credentials: Credentials
441 :param lp: Loadparm context
442 :return: LDB handle for the created secrets database
444 if os.path.exists(path):
446 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
449 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
450 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
452 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
456 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
457 """Setup the templates database.
459 :param path: Path to the database.
460 :param setup_path: Function for obtaining the path to setup files.
461 :param session_info: Session info
462 :param credentials: Credentials
463 :param lp: Loadparm context
465 templates_ldb = SamDB(path, session_info=session_info,
466 credentials=credentials, lp=lp)
467 templates_ldb.erase()
468 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
471 def setup_registry(path, setup_path, session_info, credentials, lp):
472 """Setup the registry.
474 :param path: Path to the registry database
475 :param setup_path: Function that returns the path to a setup.
476 :param session_info: Session information
477 :param credentials: Credentials
478 :param lp: Loadparm context
480 reg = registry.Registry()
481 hive = registry.open_ldb(path, session_info=session_info,
482 credentials=credentials, lp_ctx=lp)
483 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
484 provision_reg = setup_path("provision.reg")
485 assert os.path.exists(provision_reg)
486 reg.diff_apply(provision_reg)
488 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
489 """Setup the idmap database.
491 :param path: path to the idmap database
492 :param setup_path: Function that returns a path to a setup file
493 :param session_info: Session information
494 :param credentials: Credentials
495 :param lp: Loadparm context
497 if os.path.exists(path):
500 idmap_ldb = Ldb(path, session_info=session_info, credentials=credentials,
504 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
507 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
508 dnsdomain, realm, rootdn, configdn, netbiosname,
510 """Setup the SamDB rootdse.
512 :param samdb: Sam Database handle
513 :param setup_path: Obtain setup path
515 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
516 "SCHEMADN": schemadn,
517 "NETBIOSNAME": netbiosname,
518 "DNSDOMAIN": dnsdomain,
519 "DEFAULTSITE": sitename,
521 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
522 "DOMAINDN": domaindn,
524 "CONFIGDN": configdn,
525 "VERSION": samba.version(),
529 def setup_self_join(samdb, configdn, schemadn, domaindn,
530 netbiosname, hostname, dnsdomain, machinepass, dnspass,
531 realm, domainname, domainsid, invocationid, setup_path,
532 policyguid, sitename, hostguid=None):
533 """Join a host to its own domain."""
534 if hostguid is not None:
535 hostguid_add = "objectGUID: %s" % hostguid
539 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
540 "CONFIGDN": configdn,
541 "SCHEMADN": schemadn,
542 "DOMAINDN": domaindn,
543 "INVOCATIONID": invocationid,
544 "NETBIOSNAME": netbiosname,
545 "DEFAULTSITE": sitename,
546 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
547 "MACHINEPASS_B64": b64encode(machinepass),
548 "DNSPASS_B64": b64encode(dnspass),
550 "DOMAIN": domainname,
551 "HOSTGUID_ADD": hostguid_add,
552 "DNSDOMAIN": dnsdomain})
553 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
554 "POLICYGUID": policyguid,
555 "DNSDOMAIN": dnsdomain,
556 "DOMAINSID": str(domainsid),
557 "DOMAINDN": domaindn})
560 def setup_samdb(path, setup_path, session_info, credentials, lp,
561 schemadn, configdn, domaindn, dnsdomain, realm,
562 netbiosname, message, hostname, rootdn,
563 domainsid, aci, domainguid, policyguid,
564 domainname, fill, adminpass, krbtgtpass,
565 machinepass, hostguid, invocationid, dnspass,
566 serverrole, sitename, ldap_backend=None,
567 ldap_backend_type=None):
568 """Setup a complete SAM Database.
570 :note: This will wipe the main SAM database file!
573 assert serverrole in ("domain controller", "member server")
575 erase = (fill != FILL_DRS)
577 # Also wipes the database
578 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
579 domaindn=domaindn, message=message, lp=lp,
580 credentials=credentials, session_info=session_info,
581 hostname=hostname, netbiosname=netbiosname,
582 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
583 ldap_backend=ldap_backend, serverrole=serverrole,
584 ldap_backend_type=ldap_backend_type, erase=erase,
587 samdb = SamDB(path, session_info=session_info,
588 credentials=credentials, lp=lp)
591 # We want to finish here, but setup the index before we do so
592 message("Setting up sam.ldb index")
593 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
596 message("Pre-loading the Samba 4 and AD schema")
597 samdb = SamDB(path, session_info=session_info,
598 credentials=credentials, lp=lp)
599 samdb.set_domain_sid(domainsid)
600 if lp.get("server role") == "domain controller":
601 samdb.set_invocation_id(invocationid)
603 load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename)
605 samdb.transaction_start()
608 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
609 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
610 "DOMAINDN": domaindn,
614 message("Modifying DomainDN: " + domaindn + "")
615 if domainguid is not None:
616 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
620 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
621 "LDAPTIME": timestring(int(time.time())),
622 "DOMAINSID": str(domainsid),
623 "SCHEMADN": schemadn,
624 "NETBIOSNAME": netbiosname,
625 "DEFAULTSITE": sitename,
626 "CONFIGDN": configdn,
627 "POLICYGUID": policyguid,
628 "DOMAINDN": domaindn,
629 "DOMAINGUID_MOD": domainguid_mod,
632 message("Adding configuration container (permitted to fail)")
633 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
634 "CONFIGDN": configdn,
636 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
638 message("Modifying configuration container")
639 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
640 "CONFIGDN": configdn,
641 "SCHEMADN": schemadn,
644 message("Adding schema container (permitted to fail)")
645 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
646 "SCHEMADN": schemadn,
648 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
650 message("Modifying schema container")
651 setup_modify_ldif(samdb,
652 setup_path("provision_schema_basedn_modify.ldif"), {
653 "SCHEMADN": schemadn,
654 "NETBIOSNAME": netbiosname,
655 "DEFAULTSITE": sitename,
656 "CONFIGDN": configdn,
659 message("Setting up sam.ldb Samba4 schema")
660 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
661 {"SCHEMADN": schemadn })
662 message("Setting up sam.ldb AD schema")
663 setup_add_ldif(samdb, setup_path("schema.ldif"),
664 {"SCHEMADN": schemadn})
666 message("Setting up sam.ldb configuration data")
667 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
668 "CONFIGDN": configdn,
669 "NETBIOSNAME": netbiosname,
670 "DEFAULTSITE": sitename,
671 "DNSDOMAIN": dnsdomain,
672 "DOMAIN": domainname,
673 "SCHEMADN": schemadn,
674 "DOMAINDN": domaindn,
677 message("Setting up display specifiers")
678 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
679 {"CONFIGDN": configdn})
681 message("Adding users container (permitted to fail)")
682 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
683 "DOMAINDN": domaindn})
684 message("Modifying users container")
685 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
686 "DOMAINDN": domaindn})
687 message("Adding computers container (permitted to fail)")
688 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
689 "DOMAINDN": domaindn})
690 message("Modifying computers container")
691 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
692 "DOMAINDN": domaindn})
693 message("Setting up sam.ldb data")
694 setup_add_ldif(samdb, setup_path("provision.ldif"), {
695 "DOMAINDN": domaindn,
696 "NETBIOSNAME": netbiosname,
697 "DEFAULTSITE": sitename,
698 "CONFIGDN": configdn,
701 if fill == FILL_FULL:
702 message("Setting up sam.ldb users and groups")
703 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
704 "DOMAINDN": domaindn,
705 "DOMAINSID": str(domainsid),
706 "CONFIGDN": configdn,
707 "ADMINPASS_B64": b64encode(adminpass),
708 "KRBTGTPASS_B64": b64encode(krbtgtpass),
711 if lp.get("server role") == "domain controller":
712 message("Setting up self join")
713 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
714 domaindn=domaindn, invocationid=invocationid,
715 dnspass=dnspass, netbiosname=netbiosname,
716 dnsdomain=dnsdomain, realm=realm,
717 machinepass=machinepass, domainname=domainname,
718 domainsid=domainsid, policyguid=policyguid,
719 hostname=hostname, hostguid=hostguid,
720 setup_path=setup_path, sitename=sitename)
722 #We want to setup the index last, as adds are faster unindexed
723 message("Setting up sam.ldb index")
724 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
726 samdb.transaction_cancel()
729 samdb.transaction_commit()
734 FILL_NT4SYNC = "NT4SYNC"
737 def provision(lp, setup_dir, message, paths, session_info,
738 credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None,
739 domain=None, hostname=None, hostip=None, domainsid=None,
740 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
741 policyguid=None, invocationid=None, machinepass=None,
742 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
743 wheel=None, backup=None, aci=None, serverrole=None,
744 ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
747 :note: caution, this wipes all existing data!
750 def setup_path(file):
751 return os.path.join(setup_dir, file)
753 if domainsid is None:
754 domainsid = security.random_sid()
755 if policyguid is None:
756 policyguid = uuid.random()
757 if adminpass is None:
758 adminpass = misc.random_password(12)
759 if krbtgtpass is None:
760 krbtgtpass = misc.random_password(12)
761 if machinepass is None:
762 machinepass = misc.random_password(12)
764 dnspass = misc.random_password(12)
766 root = findnss(pwd.getpwnam, ["root"])[0]
768 nobody = findnss(pwd.getpwnam, ["nobody"])[0]
770 nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
772 users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown",
775 wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
777 backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
779 aci = "# no aci for local ldb"
780 if serverrole is None:
781 serverrole = lp.get("server role")
782 assert serverrole in ("domain controller", "member server")
783 if invocationid is None and serverrole == "domain controller":
784 invocationid = uuid.random()
787 realm = lp.get("realm")
789 if lp.get("realm").upper() != realm.upper():
790 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
791 (lp.get("realm"), realm))
793 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
795 if ldap_backend == "ldapi":
796 # provision-backend will set this path suggested slapd command line / fedorads.inf
797 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
799 assert realm is not None
800 realm = realm.upper()
803 hostname = gethostname().split(".")[0].lower()
806 hostip = gethostbyname(hostname)
808 netbiosname = hostname.upper()
809 if not valid_netbios_name(netbiosname):
810 raise InvalidNetbiosName(netbiosname)
812 dnsdomain = realm.lower()
813 if serverrole == "domain controller":
814 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
816 domain = lp.get("workgroup")
818 if lp.get("workgroup").upper() != domain.upper():
819 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
820 lp.get("workgroup"), domain)
822 assert domain is not None
823 domain = domain.upper()
824 if not valid_netbios_name(domain):
825 raise InvalidNetbiosName(domain)
827 domaindn = "CN=" + netbiosname
833 configdn = "CN=Configuration," + rootdn
834 schemadn = "CN=Schema," + configdn
836 message("set DOMAIN SID: %s" % str(domainsid))
837 message("Provisioning for %s in realm %s" % (domain, realm))
838 message("Using administrator password: %s" % adminpass)
840 assert paths.smbconf is not None
842 # only install a new smb.conf if there isn't one there already
843 if not os.path.exists(paths.smbconf):
844 message("Setting up smb.conf")
845 if serverrole == "domain controller":
847 elif serverrole == "member server":
848 smbconfsuffix = "member"
849 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
851 "HOSTNAME": hostname,
852 "DOMAIN_CONF": domain,
854 "SERVERROLE": serverrole,
855 "NETLOGONPATH": paths.netlogon,
856 "SYSVOLPATH": paths.sysvol,
858 lp.load(paths.smbconf)
860 # only install a new shares config db if there is none
861 if not os.path.exists(paths.shareconf):
862 message("Setting up share.ldb")
863 share_ldb = Ldb(paths.shareconf, session_info=session_info,
864 credentials=credentials, lp=lp)
865 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
868 message("Setting up secrets.ldb")
869 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
870 session_info=session_info,
871 credentials=credentials, lp=lp)
873 message("Setting up the registry")
874 setup_registry(paths.hklm, setup_path, session_info,
875 credentials=credentials, lp=lp)
877 message("Setting up templates db")
878 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
879 credentials=credentials, lp=lp)
881 message("Setting up idmap db")
882 setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
883 credentials=credentials, lp=lp)
885 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
886 credentials=credentials, lp=lp, schemadn=schemadn,
887 configdn=configdn, domaindn=domaindn,
888 dnsdomain=dnsdomain, netbiosname=netbiosname,
889 realm=realm, message=message, hostname=hostname,
890 rootdn=rootdn, domainsid=domainsid,
891 aci=aci, domainguid=domainguid, policyguid=policyguid,
892 domainname=domain, fill=samdb_fill,
893 adminpass=adminpass, krbtgtpass=krbtgtpass,
894 hostguid=hostguid, invocationid=invocationid,
895 machinepass=machinepass, dnspass=dnspass,
896 serverrole=serverrole, ldap_backend=ldap_backend,
897 ldap_backend_type=ldap_backend_type, sitename=sitename)
899 if lp.get("server role") == "domain controller":
900 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
901 "{" + policyguid + "}")
902 os.makedirs(policy_path, 0755)
903 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
904 os.makedirs(os.path.join(policy_path, "User"), 0755)
905 if not os.path.isdir(paths.netlogon):
906 os.makedirs(paths.netlogon, 0755)
907 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
908 credentials=credentials, lp=lp)
909 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
910 netbiosname=netbiosname, domainsid=domainsid,
911 keytab_path=paths.keytab, samdb_url=paths.samdb,
912 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
913 machinepass=machinepass, dnsdomain=dnsdomain)
915 if samdb_fill == FILL_FULL:
916 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
917 nobody=nobody, nogroup=nogroup, wheel=wheel,
918 users=users, backup=backup)
920 message("Setting up sam.ldb rootDSE marking as synchronized")
921 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
923 message("Setting up phpLDAPadmin configuration")
924 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
927 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
929 if lp.get("server role") == "domain controller":
930 samdb = SamDB(paths.samdb, session_info=session_info,
931 credentials=credentials, lp=lp)
933 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
934 assert isinstance(domainguid, str)
935 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
936 expression="(&(objectClass=computer)(cn=%s))" % hostname,
938 assert isinstance(hostguid, str)
940 message("Setting up DNS zone: %s" % dnsdomain)
941 create_zone_file(paths.dns, setup_path, samdb,
942 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
943 domaindn=domaindn, dnspass=dnspass, realm=realm,
944 domainguid=domainguid, hostguid=hostguid)
945 message("Please install the zone located in %s into your DNS server" % paths.dns)
950 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
951 """Create a PHP LDAP admin configuration file.
953 :param path: Path to write the configuration to.
954 :param setup_path: Function to generate setup paths.
956 setup_file(setup_path("phpldapadmin-config.php"), path,
957 {"S4_LDAPI_URI": ldapi_uri})
960 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
961 hostip, hostname, dnspass, realm, domainguid, hostguid):
962 """Write out a DNS zone file, from the info in the current database.
964 :param path: Path of the new file.
965 :param setup_path": Setup path function.
966 :param samdb: SamDB object
967 :param dnsdomain: DNS Domain name
968 :param domaindn: DN of the Domain
969 :param hostip: Local IP
970 :param hostname: Local hostname
971 :param dnspass: Password for DNS
972 :param realm: Realm name
973 :param domainguid: GUID of the domain.
974 :param hostguid: GUID of the host.
976 assert isinstance(domainguid, str)
978 setup_file(setup_path("provision.zone"), path, {
979 "DNSPASS_B64": b64encode(dnspass),
980 "HOSTNAME": hostname,
981 "DNSDOMAIN": dnsdomain,
984 "DOMAINGUID": domainguid,
985 "DATESTRING": time.strftime("%Y%m%d%H"),
986 "DEFAULTSITE": DEFAULTSITE,
987 "HOSTGUID": hostguid,
991 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
992 """Load schema for the SamDB.
994 :param samdb: Load a schema into a SamDB.
995 :param setup_path: Setup path function.
996 :param schemadn: DN of the schema
997 :param netbiosname: NetBIOS name of the host.
998 :param configdn: DN of the configuration
1000 schema_data = open(setup_path("schema.ldif"), 'r').read()
1001 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1002 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1003 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1004 head_data = substitute_var(head_data, {
1005 "SCHEMADN": schemadn,
1006 "NETBIOSNAME": netbiosname,
1007 "CONFIGDN": configdn,
1008 "DEFAULTSITE":sitename
1010 samdb.attach_schema_from_ldif(head_data, schema_data)