2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from base64 import b64encode
31 from socket import gethostname, gethostbyname
35 from auth import system_session
36 from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
37 from samba.samdb import SamDB
40 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
41 LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
43 """Functions for setting up a Samba configuration."""
45 DEFAULTSITE = "Default-First-Site-Name"
47 class InvalidNetbiosName(Exception):
48 def __init__(self, name):
49 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
66 self.dns_keytab = None
69 self.private_dir = None
72 def check_install(lp, session_info, credentials):
73 """Check whether the current install seems ok.
75 :param lp: Loadparm context
76 :param session_info: Session information
77 :param credentials: Credentials
79 if lp.get("realm") == "":
80 raise Error("Realm empty")
81 ldb = Ldb(lp.get("sam database"), session_info=session_info,
82 credentials=credentials, lp=lp)
83 if len(ldb.search("(cn=Administrator)")) != 1:
84 raise "No administrator account found"
87 def findnss(nssfn, names):
88 """Find a user or group from a list of possibilities.
90 :param nssfn: NSS Function to try (should raise KeyError if not found)
91 :param names: Names to check.
92 :return: Value return by first names list.
99 raise KeyError("Unable to find user/group %r" % names)
102 def open_ldb(session_info, credentials, lp, dbname):
103 """Open a LDB, thrashing it if it is corrupt.
105 :param session_info: auth session information
106 :param credentials: credentials
107 :param lp: Loadparm context
108 :param dbname: Path of the database to open.
109 :return: a Ldb object
111 assert session_info is not None
113 return Ldb(dbname, session_info=session_info, credentials=credentials,
118 return Ldb(dbname, session_info=session_info, credentials=credentials,
122 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
123 """Setup a ldb in the private dir.
125 :param ldb: LDB file to import data into
126 :param ldif_path: Path of the LDIF file to load
127 :param subst_vars: Optional variables to subsitute in LDIF.
129 assert isinstance(ldif_path, str)
131 data = open(ldif_path, 'r').read()
132 if subst_vars is not None:
133 data = substitute_var(data, subst_vars)
135 check_all_substituted(data)
140 def setup_modify_ldif(ldb, ldif_path, substvars=None):
141 """Modify a ldb in the private dir.
143 :param ldb: LDB object.
144 :param ldif_path: LDIF file path.
145 :param substvars: Optional dictionary with substitution variables.
147 data = open(ldif_path, 'r').read()
148 if substvars is not None:
149 data = substitute_var(data, substvars)
151 check_all_substituted(data)
153 ldb.modify_ldif(data)
156 def setup_ldb(ldb, ldif_path, subst_vars):
157 """Import a LDIF a file into a LDB handle, optionally substituting variables.
159 :note: Either all LDIF data will be added or none (using transactions).
161 :param ldb: LDB file to import into.
162 :param ldif_path: Path to the LDIF file.
163 :param subst_vars: Dictionary with substitution variables.
165 assert ldb is not None
166 ldb.transaction_start()
168 setup_add_ldif(ldb, ldif_path, subst_vars)
170 ldb.transaction_cancel()
172 ldb.transaction_commit()
175 def setup_file(template, fname, substvars):
176 """Setup a file in the private dir.
178 :param template: Path of the template file.
179 :param fname: Path of the file to create.
180 :param substvars: Substitution variables.
184 if os.path.exists(f):
187 data = open(template, 'r').read()
189 data = substitute_var(data, substvars)
190 check_all_substituted(data)
192 open(f, 'w').write(data)
195 def provision_paths_from_lp(lp, dnsdomain):
196 """Set the default paths for provisioning.
198 :param lp: Loadparm context.
199 :param dnsdomain: DNS Domain name
201 paths = ProvisionPaths()
202 paths.private_dir = lp.get("private dir")
203 paths.keytab = "secrets.keytab"
204 paths.dns_keytab = "dns.keytab"
206 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
207 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
208 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
209 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
210 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
211 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
212 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
213 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
214 paths.smbconf = os.path.join(paths.private_dir, "smb.conf")
215 paths.phpldapadminconfig = os.path.join(paths.private_dir,
216 "phpldapadmin-config.php")
217 paths.hklm = "hklm.ldb"
218 paths.hkcr = "hkcr.ldb"
219 paths.hkcu = "hkcu.ldb"
220 paths.hku = "hku.ldb"
221 paths.hkpd = "hkpd.ldb"
222 paths.hkpt = "hkpt.ldb"
224 paths.sysvol = lp.get("sysvol", "path")
225 if paths.sysvol is None:
226 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
228 paths.netlogon = lp.get("netlogon", "path")
229 if paths.netlogon is None:
230 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
235 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
237 """setup reasonable name mappings for sam names to unix names.
239 :param ldb: SamDB object.
240 :param sid: The domain sid.
241 :param domaindn: The domain DN.
242 :param root: Name of the UNIX root user.
243 :param nobody: Name of the UNIX nobody user.
244 :param nogroup: Name of the unix nobody group.
245 :param users: Name of the unix users group.
246 :param wheel: Name of the wheel group (users that can become root).
247 :param backup: Name of the backup group."""
248 # add some foreign sids if they are not present already
249 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
250 ldb.add_foreign(domaindn, "S-1-1-0", "World")
251 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
252 ldb.add_foreign(domaindn, "S-1-5-18", "System")
253 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
255 # some well known sids
256 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
257 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
258 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
259 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
260 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
261 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
262 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
263 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
264 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
266 # and some well known domain rids
267 ldb.setup_name_mapping(domaindn, sid + "-500", root)
268 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
269 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
270 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
271 ldb.setup_name_mapping(domaindn, sid + "-513", users)
272 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
275 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
276 credentials, configdn, schemadn, domaindn,
277 hostname, netbiosname, dnsdomain, realm,
278 rootdn, serverrole, sitename, ldap_backend=None,
279 ldap_backend_type=None, erase=False):
280 """Setup the partitions for the SAM database.
282 Alternatively, provision() may call this, and then populate 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 samdb = SamDB(samdb_path, session_info=session_info,
293 credentials=credentials, lp=lp)
299 os.unlink(samdb_path)
301 samdb = SamDB(samdb_path, session_info=session_info,
302 credentials=credentials, lp=lp)
304 #Add modules to the list to activate them by default
305 #beware often order is important
307 # Some Known ordering constraints:
308 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
309 # - objectclass must be before password_hash, because password_hash checks
310 # that the objectclass is of type person (filled in by objectclass
311 # module when expanding the objectclass list)
312 # - partition must be last
313 # - each partition has its own module list then
314 modules_list = ["rootdse",
330 modules_list2 = ["show_deleted",
333 domaindn_ldb = "users.ldb"
334 if ldap_backend is not None:
335 domaindn_ldb = ldap_backend
336 configdn_ldb = "configuration.ldb"
337 if ldap_backend is not None:
338 configdn_ldb = ldap_backend
339 schemadn_ldb = "schema.ldb"
340 if ldap_backend is not None:
341 schema_ldb = ldap_backend
342 schemadn_ldb = ldap_backend
344 if ldap_backend_type == "fedora-ds":
345 backend_modules = ["nsuniqueid", "paged_searches"]
346 elif ldap_backend_type == "openldap":
347 backend_modules = ["normalise", "entryuuid", "paged_searches"]
348 elif serverrole == "domain controller":
349 backend_modules = ["repl_meta_data"]
351 backend_modules = ["objectguid"]
353 samdb.transaction_start()
355 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
356 "SCHEMADN": schemadn,
357 "SCHEMADN_LDB": schemadn_ldb,
358 "SCHEMADN_MOD2": ",objectguid",
359 "CONFIGDN": configdn,
360 "CONFIGDN_LDB": configdn_ldb,
361 "DOMAINDN": domaindn,
362 "DOMAINDN_LDB": domaindn_ldb,
363 "SCHEMADN_MOD": "schema_fsmo,instancetype",
364 "CONFIGDN_MOD": "naming_fsmo,instancetype",
365 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
366 "MODULES_LIST": ",".join(modules_list),
367 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
368 "MODULES_LIST2": ",".join(modules_list2),
369 "BACKEND_MOD": ",".join(backend_modules),
373 samdb.transaction_cancel()
376 samdb.transaction_commit()
378 samdb = SamDB(samdb_path, session_info=session_info,
379 credentials=credentials, lp=lp)
381 samdb.transaction_start()
383 message("Setting up sam.ldb attributes")
384 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
386 message("Setting up sam.ldb rootDSE")
387 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
388 dnsdomain, realm, rootdn, configdn, netbiosname,
392 message("Erasing data from partitions")
393 samdb.erase_partitions()
396 samdb.transaction_cancel()
399 samdb.transaction_commit()
404 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
405 netbiosname, domainsid, keytab_path, samdb_url,
406 dns_keytab_path, dnspass, machinepass):
407 """Add DC-specific bits to a secrets database.
409 :param secretsdb: Ldb Handle to the secrets database
410 :param setup_path: Setup path function
411 :param machinepass: Machine password
413 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
414 "MACHINEPASS_B64": b64encode(machinepass),
417 "DNSDOMAIN": dnsdomain,
418 "DOMAINSID": str(domainsid),
419 "SECRETS_KEYTAB": keytab_path,
420 "NETBIOSNAME": netbiosname,
421 "SAM_LDB": samdb_url,
422 "DNS_KEYTAB": dns_keytab_path,
423 "DNSPASS_B64": b64encode(dnspass),
427 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
428 """Setup the secrets database.
430 :param path: Path to the secrets database.
431 :param setup_path: Get the path to a setup file.
432 :param session_info: Session info.
433 :param credentials: Credentials
434 :param lp: Loadparm context
435 :return: LDB handle for the created secrets database
437 if os.path.exists(path):
439 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
442 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
443 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
445 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
449 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
450 """Setup the templates database.
452 :param path: Path to the database.
453 :param setup_path: Function for obtaining the path to setup files.
454 :param session_info: Session info
455 :param credentials: Credentials
456 :param lp: Loadparm context
458 templates_ldb = SamDB(path, session_info=session_info,
459 credentials=credentials, lp=lp)
460 templates_ldb.erase()
461 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
464 def setup_registry(path, setup_path, session_info, credentials, lp):
465 """Setup the registry.
467 :param path: Path to the registry database
468 :param setup_path: Function that returns the path to a setup.
469 :param session_info: Session information
470 :param credentials: Credentials
471 :param lp: Loadparm context
473 reg = registry.Registry()
474 hive = registry.open_ldb(path, session_info=session_info,
475 credentials=credentials, lp_ctx=lp)
476 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
477 provision_reg = setup_path("provision.reg")
478 assert os.path.exists(provision_reg)
479 reg.diff_apply(provision_reg)
481 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
482 """Setup the idmap database.
484 :param path: path to the idmap database
485 :param setup_path: Function that returns a path to a setup file
486 :param session_info: Session information
487 :param credentials: Credentials
488 :param lp: Loadparm context
490 if os.path.exists(path):
493 idmap_ldb = Ldb(path, session_info=session_info, credentials=credentials,
497 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
500 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
501 dnsdomain, realm, rootdn, configdn, netbiosname,
503 """Setup the SamDB rootdse.
505 :param samdb: Sam Database handle
506 :param setup_path: Obtain setup path
508 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
509 "SCHEMADN": schemadn,
510 "NETBIOSNAME": netbiosname,
511 "DNSDOMAIN": dnsdomain,
512 "DEFAULTSITE": sitename,
514 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
515 "DOMAINDN": domaindn,
517 "CONFIGDN": configdn,
518 "VERSION": samba.version(),
522 def setup_self_join(samdb, configdn, schemadn, domaindn,
523 netbiosname, hostname, dnsdomain, machinepass, dnspass,
524 realm, domainname, domainsid, invocationid, setup_path,
525 policyguid, sitename, hostguid=None):
526 """Join a host to its own domain."""
527 if hostguid is not None:
528 hostguid_add = "objectGUID: %s" % hostguid
532 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
533 "CONFIGDN": configdn,
534 "SCHEMADN": schemadn,
535 "DOMAINDN": domaindn,
536 "INVOCATIONID": invocationid,
537 "NETBIOSNAME": netbiosname,
538 "DEFAULTSITE": sitename,
539 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
540 "MACHINEPASS_B64": b64encode(machinepass),
541 "DNSPASS_B64": b64encode(dnspass),
543 "DOMAIN": domainname,
544 "HOSTGUID_ADD": hostguid_add,
545 "DNSDOMAIN": dnsdomain})
546 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
547 "POLICYGUID": policyguid,
548 "DNSDOMAIN": dnsdomain,
549 "DOMAINSID": str(domainsid),
550 "DOMAINDN": domaindn})
553 def setup_samdb(path, setup_path, session_info, credentials, lp,
554 schemadn, configdn, domaindn, dnsdomain, realm,
555 netbiosname, message, hostname, rootdn,
556 domainsid, aci, domainguid, policyguid,
557 domainname, fill, adminpass, krbtgtpass,
558 machinepass, hostguid, invocationid, dnspass,
559 serverrole, sitename, ldap_backend=None,
560 ldap_backend_type=None):
561 """Setup a complete SAM Database.
563 :note: This will wipe the main SAM database file!
566 assert serverrole in ("domain controller", "member server")
568 erase = (fill != FILL_DRS)
570 # Also wipes the database
571 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
572 domaindn=domaindn, message=message, lp=lp,
573 credentials=credentials, session_info=session_info,
574 hostname=hostname, netbiosname=netbiosname,
575 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
576 ldap_backend=ldap_backend, serverrole=serverrole,
577 ldap_backend_type=ldap_backend_type, erase=erase,
580 samdb = SamDB(path, session_info=session_info,
581 credentials=credentials, lp=lp)
584 # We want to finish here, but setup the index before we do so
585 message("Setting up sam.ldb index")
586 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
589 message("Pre-loading the Samba 4 and AD schema")
590 samdb = SamDB(path, session_info=session_info,
591 credentials=credentials, lp=lp)
592 samdb.set_domain_sid(domainsid)
593 if serverrole == "domain controller":
594 samdb.set_invocation_id(invocationid)
596 load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename)
598 samdb.transaction_start()
601 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
602 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
603 "DOMAINDN": domaindn,
607 message("Modifying DomainDN: " + domaindn + "")
608 if domainguid is not None:
609 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
613 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
614 "LDAPTIME": timestring(int(time.time())),
615 "DOMAINSID": str(domainsid),
616 "SCHEMADN": schemadn,
617 "NETBIOSNAME": netbiosname,
618 "DEFAULTSITE": sitename,
619 "CONFIGDN": configdn,
620 "POLICYGUID": policyguid,
621 "DOMAINDN": domaindn,
622 "DOMAINGUID_MOD": domainguid_mod,
625 message("Adding configuration container (permitted to fail)")
626 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
627 "CONFIGDN": configdn,
629 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
631 message("Modifying configuration container")
632 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
633 "CONFIGDN": configdn,
634 "SCHEMADN": schemadn,
637 message("Adding schema container (permitted to fail)")
638 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
639 "SCHEMADN": schemadn,
641 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
643 message("Modifying schema container")
644 setup_modify_ldif(samdb,
645 setup_path("provision_schema_basedn_modify.ldif"), {
646 "SCHEMADN": schemadn,
647 "NETBIOSNAME": netbiosname,
648 "DEFAULTSITE": sitename,
649 "CONFIGDN": configdn,
652 message("Setting up sam.ldb Samba4 schema")
653 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
654 {"SCHEMADN": schemadn })
655 message("Setting up sam.ldb AD schema")
656 setup_add_ldif(samdb, setup_path("schema.ldif"),
657 {"SCHEMADN": schemadn})
659 message("Setting up sam.ldb configuration data")
660 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
661 "CONFIGDN": configdn,
662 "NETBIOSNAME": netbiosname,
663 "DEFAULTSITE": sitename,
664 "DNSDOMAIN": dnsdomain,
665 "DOMAIN": domainname,
666 "SCHEMADN": schemadn,
667 "DOMAINDN": domaindn,
670 message("Setting up display specifiers")
671 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
672 {"CONFIGDN": configdn})
674 message("Adding users container (permitted to fail)")
675 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
676 "DOMAINDN": domaindn})
677 message("Modifying users container")
678 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
679 "DOMAINDN": domaindn})
680 message("Adding computers container (permitted to fail)")
681 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
682 "DOMAINDN": domaindn})
683 message("Modifying computers container")
684 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
685 "DOMAINDN": domaindn})
686 message("Setting up sam.ldb data")
687 setup_add_ldif(samdb, setup_path("provision.ldif"), {
688 "DOMAINDN": domaindn,
689 "NETBIOSNAME": netbiosname,
690 "DEFAULTSITE": sitename,
691 "CONFIGDN": configdn,
694 if fill == FILL_FULL:
695 message("Setting up sam.ldb users and groups")
696 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
697 "DOMAINDN": domaindn,
698 "DOMAINSID": str(domainsid),
699 "CONFIGDN": configdn,
700 "ADMINPASS_B64": b64encode(adminpass),
701 "KRBTGTPASS_B64": b64encode(krbtgtpass),
704 if serverrole == "domain controller":
705 message("Setting up self join")
706 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
707 domaindn=domaindn, invocationid=invocationid,
708 dnspass=dnspass, netbiosname=netbiosname,
709 dnsdomain=dnsdomain, realm=realm,
710 machinepass=machinepass, domainname=domainname,
711 domainsid=domainsid, policyguid=policyguid,
712 hostname=hostname, hostguid=hostguid,
713 setup_path=setup_path, sitename=sitename)
715 #We want to setup the index last, as adds are faster unindexed
716 message("Setting up sam.ldb index")
717 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
719 samdb.transaction_cancel()
722 samdb.transaction_commit()
727 FILL_NT4SYNC = "NT4SYNC"
730 def provision(setup_dir, message, session_info,
731 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
732 rootdn=None, domaindn=None, schemadn=None, configdn=None,
733 domain=None, hostname=None, hostip=None, domainsid=None,
734 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
735 policyguid=None, invocationid=None, machinepass=None,
736 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
737 wheel=None, backup=None, aci=None, serverrole=None,
738 ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
741 :note: caution, this wipes all existing data!
744 def setup_path(file):
745 return os.path.join(setup_dir, file)
747 if domainsid is None:
748 domainsid = security.random_sid()
749 if policyguid is None:
750 policyguid = uuid.random()
751 if adminpass is None:
752 adminpass = misc.random_password(12)
753 if krbtgtpass is None:
754 krbtgtpass = misc.random_password(12)
755 if machinepass is None:
756 machinepass = misc.random_password(12)
758 dnspass = misc.random_password(12)
760 root = findnss(pwd.getpwnam, ["root"])[0]
762 nobody = findnss(pwd.getpwnam, ["nobody"])[0]
764 nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
766 users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown",
769 wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
771 backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
773 aci = "# no aci for local ldb"
775 hostname = gethostname().split(".")[0].lower()
778 hostip = gethostbyname(hostname)
780 netbiosname = hostname.upper()
781 if not valid_netbios_name(netbiosname):
782 raise InvalidNetbiosName(netbiosname)
784 if targetdir is not None:
785 if not os.path.exists(targetdir):
787 if not os.path.exists(os.path.join(targetdir, "etc")):
788 os.mkdir(os.path.join(targetdir, "etc"))
791 smbconf = os.path.join(targetdir, os.path.join("etc", "smb.conf"))
793 # only install a new smb.conf if there isn't one there already
794 if not os.path.exists(smbconf):
795 message("Setting up smb.conf")
796 assert serverrole is not None
797 if serverrole == "domain controller":
799 elif serverrole == "member server":
800 smbconfsuffix = "member"
802 assert domain is not None
803 assert realm is not None
805 default_lp = param.LoadParm()
806 #Load non-existant file
807 default_lp.load(smbconf)
809 if targetdir is not None:
810 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
811 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
813 default_lp.set("lock dir", os.path.abspath(targetdir))
815 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
816 netlogon = os.path.join(os.path.join(sysvol, "scripts"))
818 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
820 "HOSTNAME": hostname,
821 "DOMAIN_CONF": domain,
823 "SERVERROLE": serverrole,
824 "NETLOGONPATH": netlogon,
825 "SYSVOLPATH": sysvol,
826 "PRIVATEDIR_LINE": privatedir_line,
827 "LOCKDIR_LINE": lockdir_line
830 lp = param.LoadParm()
833 if serverrole is None:
834 serverrole = lp.get("server role")
835 assert serverrole in ("domain controller", "member server")
836 if invocationid is None and serverrole == "domain controller":
837 invocationid = uuid.random()
840 realm = lp.get("realm")
842 assert realm is not None
843 realm = realm.upper()
845 dnsdomain = realm.lower()
847 paths = provision_paths_from_lp(lp, dnsdomain)
849 if targetdir is not None:
850 if not os.path.exists(paths.private_dir):
851 os.mkdir(paths.private_dir)
853 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
855 if ldap_backend == "ldapi":
856 # provision-backend will set this path suggested slapd command line / fedorads.inf
857 ldap_backend = "ldapi://" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
859 if serverrole == "domain controller":
861 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
863 domain = lp.get("workgroup")
865 if lp.get("workgroup").upper() != domain.upper():
866 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
867 lp.get("workgroup"), domain)
869 assert domain is not None
870 domain = domain.upper()
871 if not valid_netbios_name(domain):
872 raise InvalidNetbiosName(domain)
875 domaindn = "CN=" + netbiosname
882 configdn = "CN=Configuration," + rootdn
884 schemadn = "CN=Schema," + configdn
886 message("set DOMAIN SID: %s" % str(domainsid))
887 message("Provisioning for %s in realm %s" % (domain, realm))
888 message("Using administrator password: %s" % adminpass)
890 if lp.get("realm").upper() != realm.upper():
891 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
892 (lp.get("realm"), realm))
894 # only install a new shares config db if there is none
895 if not os.path.exists(paths.shareconf):
896 message("Setting up share.ldb")
897 share_ldb = Ldb(paths.shareconf, session_info=session_info,
898 credentials=credentials, lp=lp)
899 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
902 message("Setting up secrets.ldb")
903 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
904 session_info=session_info,
905 credentials=credentials, lp=lp)
907 message("Setting up the registry")
908 setup_registry(paths.hklm, setup_path, session_info,
909 credentials=credentials, lp=lp)
911 message("Setting up templates db")
912 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
913 credentials=credentials, lp=lp)
915 message("Setting up idmap db")
916 setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
917 credentials=credentials, lp=lp)
919 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
920 credentials=credentials, lp=lp, schemadn=schemadn,
921 configdn=configdn, domaindn=domaindn,
922 dnsdomain=dnsdomain, netbiosname=netbiosname,
923 realm=realm, message=message, hostname=hostname,
924 rootdn=rootdn, domainsid=domainsid,
925 aci=aci, domainguid=domainguid, policyguid=policyguid,
926 domainname=domain, fill=samdb_fill,
927 adminpass=adminpass, krbtgtpass=krbtgtpass,
928 hostguid=hostguid, invocationid=invocationid,
929 machinepass=machinepass, dnspass=dnspass,
930 serverrole=serverrole, ldap_backend=ldap_backend,
931 ldap_backend_type=ldap_backend_type, sitename=sitename)
933 if lp.get("server role") == "domain controller":
934 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
935 "{" + policyguid + "}")
936 os.makedirs(policy_path, 0755)
937 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
938 os.makedirs(os.path.join(policy_path, "User"), 0755)
939 if not os.path.isdir(paths.netlogon):
940 os.makedirs(paths.netlogon, 0755)
941 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
942 credentials=credentials, lp=lp)
943 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
944 netbiosname=netbiosname, domainsid=domainsid,
945 keytab_path=paths.keytab, samdb_url=paths.samdb,
946 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
947 machinepass=machinepass, dnsdomain=dnsdomain)
949 if samdb_fill == FILL_FULL:
950 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
951 nobody=nobody, nogroup=nogroup, wheel=wheel,
952 users=users, backup=backup)
954 message("Setting up sam.ldb rootDSE marking as synchronized")
955 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
957 # Only make a zone file on the first DC, it should be replicated with DNS replication
958 if serverrole == "domain controller":
959 samdb = SamDB(paths.samdb, session_info=session_info,
960 credentials=credentials, lp=lp)
962 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
963 assert isinstance(domainguid, str)
964 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
965 expression="(&(objectClass=computer)(cn=%s))" % hostname,
967 assert isinstance(hostguid, str)
969 message("Setting up DNS zone: %s" % dnsdomain)
970 create_zone_file(paths.dns, setup_path, samdb,
971 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
972 domaindn=domaindn, dnspass=dnspass, realm=realm,
973 domainguid=domainguid, hostguid=hostguid)
974 message("Please install the zone located in %s into your DNS server" % paths.dns)
976 message("Setting up phpLDAPadmin configuration")
977 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
980 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
984 def provision_become_dc(setup_dir=None,
985 smbconf=None, targetdir=None, realm=None,
986 rootdn=None, domaindn=None, schemadn=None, configdn=None,
987 domain=None, hostname=None, domainsid=None,
988 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
989 policyguid=None, invocationid=None, machinepass=None,
990 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
991 wheel=None, backup=None, aci=None, serverrole=None,
992 ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
995 """print a message if quiet is not set."""
998 provision(setup_dir, message, system_session(), None,
999 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1000 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn,
1001 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename);
1004 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1005 """Create a PHP LDAP admin configuration file.
1007 :param path: Path to write the configuration to.
1008 :param setup_path: Function to generate setup paths.
1010 setup_file(setup_path("phpldapadmin-config.php"), path,
1011 {"S4_LDAPI_URI": ldapi_uri})
1014 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
1015 hostip, hostname, dnspass, realm, domainguid, hostguid):
1016 """Write out a DNS zone file, from the info in the current database.
1018 :param path: Path of the new file.
1019 :param setup_path": Setup path function.
1020 :param samdb: SamDB object
1021 :param dnsdomain: DNS Domain name
1022 :param domaindn: DN of the Domain
1023 :param hostip: Local IP
1024 :param hostname: Local hostname
1025 :param dnspass: Password for DNS
1026 :param realm: Realm name
1027 :param domainguid: GUID of the domain.
1028 :param hostguid: GUID of the host.
1030 assert isinstance(domainguid, str)
1032 setup_file(setup_path("provision.zone"), path, {
1033 "DNSPASS_B64": b64encode(dnspass),
1034 "HOSTNAME": hostname,
1035 "DNSDOMAIN": dnsdomain,
1038 "DOMAINGUID": domainguid,
1039 "DATESTRING": time.strftime("%Y%m%d%H"),
1040 "DEFAULTSITE": DEFAULTSITE,
1041 "HOSTGUID": hostguid,
1044 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
1045 """Load schema for the SamDB.
1047 :param samdb: Load a schema into a SamDB.
1048 :param setup_path: Setup path function.
1049 :param schemadn: DN of the schema
1050 :param netbiosname: NetBIOS name of the host.
1051 :param configdn: DN of the configuration
1053 schema_data = open(setup_path("schema.ldif"), 'r').read()
1054 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1055 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1056 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1057 head_data = substitute_var(head_data, {
1058 "SCHEMADN": schemadn,
1059 "NETBIOSNAME": netbiosname,
1060 "CONFIGDN": configdn,
1061 "DEFAULTSITE":sitename
1063 samdb.attach_schema_from_ldif(head_data, schema_data)