2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
8 # Based on the original in EJS:
9 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from base64 import b64encode
31 from socket import gethostname, gethostbyname
35 from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
36 from samba.samdb import SamDB
39 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
40 LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
42 """Functions for setting up a Samba configuration."""
44 DEFAULTSITE = "Default-First-Site-Name"
46 class InvalidNetbiosName(Exception):
47 def __init__(self, name):
48 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
64 self.dns_keytab = None
69 def check_install(lp, session_info, credentials):
70 """Check whether the current install seems ok.
72 :param lp: Loadparm context
73 :param session_info: Session information
74 :param credentials: Credentials
76 if lp.get("realm") == "":
77 raise Error("Realm empty")
78 ldb = Ldb(lp.get("sam database"), session_info=session_info,
79 credentials=credentials, lp=lp)
80 if len(ldb.search("(cn=Administrator)")) != 1:
81 raise "No administrator account found"
84 def findnss(nssfn, names):
85 """Find a user or group from a list of possibilities.
87 :param nssfn: NSS Function to try (should raise KeyError if not found)
88 :param names: Names to check.
89 :return: Value return by first names list.
96 raise KeyError("Unable to find user/group %r" % names)
99 def open_ldb(session_info, credentials, lp, dbname):
100 """Open a LDB, thrashing it if it is corrupt.
102 :param session_info: auth session information
103 :param credentials: credentials
104 :param lp: Loadparm context
105 :param dbname: Path of the database to open.
106 :return: a Ldb object
108 assert session_info is not None
110 return Ldb(dbname, session_info=session_info, credentials=credentials,
115 return Ldb(dbname, session_info=session_info, credentials=credentials,
119 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
120 """Setup a ldb in the private dir.
122 :param ldb: LDB file to import data into
123 :param ldif_path: Path of the LDIF file to load
124 :param subst_vars: Optional variables to subsitute in LDIF.
126 assert isinstance(ldif_path, str)
128 data = open(ldif_path, 'r').read()
129 if subst_vars is not None:
130 data = substitute_var(data, subst_vars)
132 check_all_substituted(data)
137 def setup_modify_ldif(ldb, ldif_path, substvars=None):
138 """Modify a ldb in the private dir.
140 :param ldb: LDB object.
141 :param ldif_path: LDIF file path.
142 :param substvars: Optional dictionary with substitution variables.
144 data = open(ldif_path, 'r').read()
145 if substvars is not None:
146 data = substitute_var(data, substvars)
148 check_all_substituted(data)
150 ldb.modify_ldif(data)
153 def setup_ldb(ldb, ldif_path, subst_vars):
154 """Import a LDIF a file into a LDB handle, optionally substituting variables.
156 :note: Either all LDIF data will be added or none (using transactions).
158 :param ldb: LDB file to import into.
159 :param ldif_path: Path to the LDIF file.
160 :param subst_vars: Dictionary with substitution variables.
162 assert ldb is not None
163 ldb.transaction_start()
165 setup_add_ldif(ldb, ldif_path, subst_vars)
167 ldb.transaction_cancel()
169 ldb.transaction_commit()
172 def setup_file(template, fname, substvars):
173 """Setup a file in the private dir.
175 :param template: Path of the template file.
176 :param fname: Path of the file to create.
177 :param substvars: Substitution variables.
181 if os.path.exists(f):
184 data = open(template, 'r').read()
186 data = substitute_var(data, substvars)
187 check_all_substituted(data)
189 open(f, 'w').write(data)
192 def provision_paths_from_lp(lp, dnsdomain):
193 """Set the default paths for provisioning.
195 :param lp: Loadparm context.
196 :param dnsdomain: DNS Domain name
198 paths = ProvisionPaths()
199 private_dir = lp.get("private dir")
200 paths.keytab = "secrets.keytab"
201 paths.dns_keytab = "dns.keytab"
203 paths.shareconf = os.path.join(private_dir, "share.ldb")
204 paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb")
205 paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb")
206 paths.templates = os.path.join(private_dir, "templates.ldb")
207 paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
208 paths.winsdb = os.path.join(private_dir, "wins.ldb")
209 paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
210 paths.smbconf = os.path.join(private_dir, "smb.conf")
211 paths.phpldapadminconfig = os.path.join(private_dir,
212 "phpldapadmin-config.php")
213 paths.hklm = "hklm.ldb"
214 paths.hkcr = "hkcr.ldb"
215 paths.hkcu = "hkcu.ldb"
216 paths.hku = "hku.ldb"
217 paths.hkpd = "hkpd.ldb"
218 paths.hkpt = "hkpt.ldb"
220 paths.sysvol = lp.get("sysvol", "path")
221 if paths.sysvol is None:
222 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
224 paths.netlogon = lp.get("netlogon", "path")
225 if paths.netlogon is None:
226 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
231 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
233 """setup reasonable name mappings for sam names to unix names.
235 :param ldb: SamDB object.
236 :param sid: The domain sid.
237 :param domaindn: The domain DN.
238 :param root: Name of the UNIX root user.
239 :param nobody: Name of the UNIX nobody user.
240 :param nogroup: Name of the unix nobody group.
241 :param users: Name of the unix users group.
242 :param wheel: Name of the wheel group (users that can become root).
243 :param backup: Name of the backup group."""
244 # add some foreign sids if they are not present already
245 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
246 ldb.add_foreign(domaindn, "S-1-1-0", "World")
247 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
248 ldb.add_foreign(domaindn, "S-1-5-18", "System")
249 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
251 # some well known sids
252 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
253 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
254 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
255 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
256 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
257 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
258 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
259 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
260 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
262 # and some well known domain rids
263 ldb.setup_name_mapping(domaindn, sid + "-500", root)
264 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
265 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
266 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
267 ldb.setup_name_mapping(domaindn, sid + "-513", users)
268 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
271 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
272 credentials, configdn, schemadn, domaindn,
273 hostname, netbiosname, dnsdomain, realm,
274 rootdn, serverrole, sitename, ldap_backend=None,
275 ldap_backend_type=None, erase=False):
276 """Setup the partitions for the SAM database.
278 Alternatively, provision() may call this, and then populate the database.
280 :param erase: Remove the existing data present in the database.
282 :note: This will wipe the Sam Database!
284 :note: This function always removes the local SAM LDB file. The erase
285 parameter controls whether to erase the existing data, which
286 may not be stored locally but in LDAP.
288 assert session_info is not None
290 if os.path.exists(samdb_path):
291 os.unlink(samdb_path)
293 # Also wipes the database
294 samdb = SamDB(samdb_path, session_info=session_info,
295 credentials=credentials, lp=lp)
297 #Add modules to the list to activate them by default
298 #beware often order is important
300 # Some Known ordering constraints:
301 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
302 # - objectclass must be before password_hash, because password_hash checks
303 # that the objectclass is of type person (filled in by objectclass
304 # module when expanding the objectclass list)
305 # - partition must be last
306 # - each partition has its own module list then
307 modules_list = ["rootdse",
323 modules_list2 = ["show_deleted",
326 domaindn_ldb = "users.ldb"
327 if ldap_backend is not None:
328 domaindn_ldb = ldap_backend
329 configdn_ldb = "configuration.ldb"
330 if ldap_backend is not None:
331 configdn_ldb = ldap_backend
332 schemadn_ldb = "schema.ldb"
333 if ldap_backend is not None:
334 schema_ldb = ldap_backend
335 schemadn_ldb = ldap_backend
337 if ldap_backend_type == "fedora-ds":
338 backend_modules = ["nsuniqueid", "paged_searches"]
339 elif ldap_backend_type == "openldap":
340 backend_modules = ["normalise", "entryuuid", "paged_searches"]
341 elif serverrole == "domain controller":
342 backend_modules = ["repl_meta_data"]
344 backend_modules = ["objectguid"]
346 samdb.transaction_start()
348 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
349 "SCHEMADN": schemadn,
350 "SCHEMADN_LDB": schemadn_ldb,
351 "SCHEMADN_MOD2": ",objectguid",
352 "CONFIGDN": configdn,
353 "CONFIGDN_LDB": configdn_ldb,
354 "DOMAINDN": domaindn,
355 "DOMAINDN_LDB": domaindn_ldb,
356 "SCHEMADN_MOD": "schema_fsmo,instancetype",
357 "CONFIGDN_MOD": "naming_fsmo,instancetype",
358 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
359 "MODULES_LIST": ",".join(modules_list),
360 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
361 "MODULES_LIST2": ",".join(modules_list2),
362 "BACKEND_MOD": ",".join(backend_modules),
366 samdb.transaction_cancel()
369 samdb.transaction_commit()
371 samdb = SamDB(samdb_path, session_info=session_info,
372 credentials=credentials, lp=lp)
374 samdb.transaction_start()
376 message("Setting up sam.ldb attributes")
377 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
379 message("Setting up sam.ldb rootDSE")
380 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
381 dnsdomain, realm, rootdn, configdn, netbiosname,
385 message("Erasing data from partitions")
386 samdb.erase_partitions()
389 samdb.transaction_cancel()
392 samdb.transaction_commit()
397 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
398 netbiosname, domainsid, keytab_path, samdb_url,
399 dns_keytab_path, dnspass, machinepass):
400 """Add DC-specific bits to a secrets database.
402 :param secretsdb: Ldb Handle to the secrets database
403 :param setup_path: Setup path function
404 :param machinepass: Machine password
406 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
407 "MACHINEPASS_B64": b64encode(machinepass),
410 "DNSDOMAIN": dnsdomain,
411 "DOMAINSID": str(domainsid),
412 "SECRETS_KEYTAB": keytab_path,
413 "NETBIOSNAME": netbiosname,
414 "SAM_LDB": samdb_url,
415 "DNS_KEYTAB": dns_keytab_path,
416 "DNSPASS_B64": b64encode(dnspass),
420 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
421 """Setup the secrets database.
423 :param path: Path to the secrets database.
424 :param setup_path: Get the path to a setup file.
425 :param session_info: Session info.
426 :param credentials: Credentials
427 :param lp: Loadparm context
428 :return: LDB handle for the created secrets database
430 if os.path.exists(path):
432 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
435 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
436 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
438 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
442 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
443 """Setup the templates database.
445 :param path: Path to the database.
446 :param setup_path: Function for obtaining the path to setup files.
447 :param session_info: Session info
448 :param credentials: Credentials
449 :param lp: Loadparm context
451 templates_ldb = SamDB(path, session_info=session_info,
452 credentials=credentials, lp=lp)
453 templates_ldb.erase()
454 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
457 def setup_registry(path, setup_path, session_info, credentials, lp):
458 """Setup the registry.
460 :param path: Path to the registry database
461 :param setup_path: Function that returns the path to a setup.
462 :param session_info: Session information
463 :param credentials: Credentials
464 :param lp: Loadparm context
466 reg = registry.Registry()
467 hive = registry.open_ldb(path, session_info=session_info,
468 credentials=credentials, lp_ctx=lp)
469 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
470 provision_reg = setup_path("provision.reg")
471 assert os.path.exists(provision_reg)
472 reg.diff_apply(provision_reg)
475 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
476 dnsdomain, realm, rootdn, configdn, netbiosname,
478 """Setup the SamDB rootdse.
480 :param samdb: Sam Database handle
481 :param setup_path: Obtain setup path
483 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
484 "SCHEMADN": schemadn,
485 "NETBIOSNAME": netbiosname,
486 "DNSDOMAIN": dnsdomain,
487 "DEFAULTSITE": sitename,
489 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
490 "DOMAINDN": domaindn,
492 "CONFIGDN": configdn,
493 "VERSION": samba.version(),
497 def setup_self_join(samdb, configdn, schemadn, domaindn,
498 netbiosname, hostname, dnsdomain, machinepass, dnspass,
499 realm, domainname, domainsid, invocationid, setup_path,
500 policyguid, sitename, hostguid=None):
501 """Join a host to its own domain."""
502 if hostguid is not None:
503 hostguid_add = "objectGUID: %s" % hostguid
507 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
508 "CONFIGDN": configdn,
509 "SCHEMADN": schemadn,
510 "DOMAINDN": domaindn,
511 "INVOCATIONID": invocationid,
512 "NETBIOSNAME": netbiosname,
513 "DEFAULTSITE": sitename,
514 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
515 "MACHINEPASS_B64": b64encode(machinepass),
516 "DNSPASS_B64": b64encode(dnspass),
518 "DOMAIN": domainname,
519 "HOSTGUID_ADD": hostguid_add,
520 "DNSDOMAIN": dnsdomain})
521 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
522 "POLICYGUID": policyguid,
523 "DNSDOMAIN": dnsdomain,
524 "DOMAINSID": str(domainsid),
525 "DOMAINDN": domaindn})
528 def setup_samdb(path, setup_path, session_info, credentials, lp,
529 schemadn, configdn, domaindn, dnsdomain, realm,
530 netbiosname, message, hostname, rootdn, erase,
531 domainsid, aci, domainguid, policyguid,
532 domainname, fill, adminpass, krbtgtpass,
533 machinepass, hostguid, invocationid, dnspass,
534 serverrole, sitename, ldap_backend=None,
535 ldap_backend_type=None):
536 """Setup a complete SAM Database.
538 :note: This will wipe the main SAM database file!
541 assert serverrole in ("domain controller", "member server")
543 # Also wipes the database
544 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
545 domaindn=domaindn, message=message, lp=lp,
546 credentials=credentials, session_info=session_info,
547 hostname=hostname, netbiosname=netbiosname,
548 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
549 ldap_backend=ldap_backend, serverrole=serverrole,
550 ldap_backend_type=ldap_backend_type, erase=erase,
553 samdb = SamDB(path, session_info=session_info,
554 credentials=credentials, lp=lp)
557 # We want to finish here, but setup the index before we do so
558 message("Setting up sam.ldb index")
559 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
562 message("Pre-loading the Samba 4 and AD schema")
563 samdb = SamDB(path, session_info=session_info,
564 credentials=credentials, lp=lp)
565 samdb.set_domain_sid(domainsid)
566 if lp.get("server role") == "domain controller":
567 samdb.set_invocation_id(invocationid)
569 load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename)
571 samdb.transaction_start()
574 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
575 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
576 "DOMAINDN": domaindn,
580 message("Modifying DomainDN: " + domaindn + "")
581 if domainguid is not None:
582 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
586 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
587 "LDAPTIME": timestring(int(time.time())),
588 "DOMAINSID": str(domainsid),
589 "SCHEMADN": schemadn,
590 "NETBIOSNAME": netbiosname,
591 "DEFAULTSITE": sitename,
592 "CONFIGDN": configdn,
593 "POLICYGUID": policyguid,
594 "DOMAINDN": domaindn,
595 "DOMAINGUID_MOD": domainguid_mod,
598 message("Adding configuration container (permitted to fail)")
599 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
600 "CONFIGDN": configdn,
602 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
604 message("Modifying configuration container")
605 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
606 "CONFIGDN": configdn,
607 "SCHEMADN": schemadn,
610 message("Adding schema container (permitted to fail)")
611 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
612 "SCHEMADN": schemadn,
614 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
616 message("Modifying schema container")
617 setup_modify_ldif(samdb,
618 setup_path("provision_schema_basedn_modify.ldif"), {
619 "SCHEMADN": schemadn,
620 "NETBIOSNAME": netbiosname,
621 "DEFAULTSITE": sitename,
622 "CONFIGDN": configdn,
625 message("Setting up sam.ldb Samba4 schema")
626 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
627 {"SCHEMADN": schemadn })
628 message("Setting up sam.ldb AD schema")
629 setup_add_ldif(samdb, setup_path("schema.ldif"),
630 {"SCHEMADN": schemadn})
632 message("Setting up sam.ldb configuration data")
633 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
634 "CONFIGDN": configdn,
635 "NETBIOSNAME": netbiosname,
636 "DEFAULTSITE": sitename,
637 "DNSDOMAIN": dnsdomain,
638 "DOMAIN": domainname,
639 "SCHEMADN": schemadn,
640 "DOMAINDN": domaindn,
643 message("Setting up display specifiers")
644 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
645 {"CONFIGDN": configdn})
647 message("Adding users container (permitted to fail)")
648 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
649 "DOMAINDN": domaindn})
650 message("Modifying users container")
651 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
652 "DOMAINDN": domaindn})
653 message("Adding computers container (permitted to fail)")
654 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
655 "DOMAINDN": domaindn})
656 message("Modifying computers container")
657 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
658 "DOMAINDN": domaindn})
659 message("Setting up sam.ldb data")
660 setup_add_ldif(samdb, setup_path("provision.ldif"), {
661 "DOMAINDN": domaindn,
662 "NETBIOSNAME": netbiosname,
663 "DEFAULTSITE": sitename,
664 "CONFIGDN": configdn,
667 if fill == FILL_FULL:
668 message("Setting up sam.ldb users and groups")
669 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
670 "DOMAINDN": domaindn,
671 "DOMAINSID": str(domainsid),
672 "CONFIGDN": configdn,
673 "ADMINPASS_B64": b64encode(adminpass),
674 "KRBTGTPASS_B64": b64encode(krbtgtpass),
677 if lp.get("server role") == "domain controller":
678 message("Setting up self join")
679 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
680 domaindn=domaindn, invocationid=invocationid,
681 dnspass=dnspass, netbiosname=netbiosname,
682 dnsdomain=dnsdomain, realm=realm,
683 machinepass=machinepass, domainname=domainname,
684 domainsid=domainsid, policyguid=policyguid,
685 hostname=hostname, hostguid=hostguid,
686 setup_path=setup_path, sitename=sitename)
688 #We want to setup the index last, as adds are faster unindexed
689 message("Setting up sam.ldb index")
690 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
692 samdb.transaction_cancel()
695 samdb.transaction_commit()
700 FILL_NT4SYNC = "NT4SYNC"
703 def provision(lp, setup_dir, message, paths, session_info,
704 credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None,
705 domain=None, hostname=None, hostip=None, domainsid=None,
706 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
707 policyguid=None, invocationid=None, machinepass=None,
708 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
709 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
710 ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
713 :note: caution, this wipes all existing data!
716 def setup_path(file):
717 return os.path.join(setup_dir, file)
719 if domainsid is None:
720 domainsid = security.random_sid()
721 if policyguid is None:
722 policyguid = uuid.random()
723 if adminpass is None:
724 adminpass = misc.random_password(12)
725 if krbtgtpass is None:
726 krbtgtpass = misc.random_password(12)
727 if machinepass is None:
728 machinepass = misc.random_password(12)
730 dnspass = misc.random_password(12)
732 root = findnss(pwd.getpwnam, ["root"])[0]
734 nobody = findnss(pwd.getpwnam, ["nobody"])[0]
736 nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
738 users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown",
741 wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
743 backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
745 aci = "# no aci for local ldb"
746 if serverrole is None:
747 serverrole = lp.get("server role")
748 assert serverrole in ("domain controller", "member server")
749 if invocationid is None and serverrole == "domain controller":
750 invocationid = uuid.random()
753 realm = lp.get("realm")
755 if lp.get("realm").upper() != realm.upper():
756 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
757 (lp.get("realm"), realm))
759 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
761 if ldap_backend == "ldapi":
762 # provision-backend will set this path suggested slapd command line / fedorads.inf
763 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
765 assert realm is not None
766 realm = realm.upper()
769 hostname = gethostname().split(".")[0].lower()
772 hostip = gethostbyname(hostname)
774 netbiosname = hostname.upper()
775 if not valid_netbios_name(netbiosname):
776 raise InvalidNetbiosName(netbiosname)
778 dnsdomain = realm.lower()
779 if serverrole == "domain controller":
780 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
782 domain = lp.get("workgroup")
784 if lp.get("workgroup").upper() != domain.upper():
785 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
786 lp.get("workgroup"), domain)
788 assert domain is not None
789 domain = domain.upper()
790 if not valid_netbios_name(domain):
791 raise InvalidNetbiosName(domain)
793 domaindn = "CN=" + netbiosname
799 configdn = "CN=Configuration," + rootdn
800 schemadn = "CN=Schema," + configdn
802 message("set DOMAIN SID: %s" % str(domainsid))
803 message("Provisioning for %s in realm %s" % (domain, realm))
804 message("Using administrator password: %s" % adminpass)
806 assert paths.smbconf is not None
808 # only install a new smb.conf if there isn't one there already
809 if not os.path.exists(paths.smbconf):
810 message("Setting up smb.conf")
811 if serverrole == "domain controller":
813 elif serverrole == "member":
814 smbconfsuffix = "member"
815 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
817 "HOSTNAME": hostname,
818 "DOMAIN_CONF": domain,
820 "SERVERROLE": serverrole,
821 "NETLOGONPATH": paths.netlogon,
822 "SYSVOLPATH": paths.sysvol,
824 lp.load(paths.smbconf)
826 # only install a new shares config db if there is none
827 if not os.path.exists(paths.shareconf):
828 message("Setting up share.ldb")
829 share_ldb = Ldb(paths.shareconf, session_info=session_info,
830 credentials=credentials, lp=lp)
831 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
834 message("Setting up secrets.ldb")
835 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
836 session_info=session_info,
837 credentials=credentials, lp=lp)
839 message("Setting up the registry")
840 setup_registry(paths.hklm, setup_path, session_info,
841 credentials=credentials, lp=lp)
843 message("Setting up templates db")
844 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
845 credentials=credentials, lp=lp)
847 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
848 credentials=credentials, lp=lp, schemadn=schemadn,
849 configdn=configdn, domaindn=domaindn,
850 dnsdomain=dnsdomain, netbiosname=netbiosname,
851 realm=realm, message=message, hostname=hostname,
852 rootdn=rootdn, erase=erase, domainsid=domainsid,
853 aci=aci, domainguid=domainguid, policyguid=policyguid,
854 domainname=domain, fill=samdb_fill,
855 adminpass=adminpass, krbtgtpass=krbtgtpass,
856 hostguid=hostguid, invocationid=invocationid,
857 machinepass=machinepass, dnspass=dnspass,
858 serverrole=serverrole, ldap_backend=ldap_backend,
859 ldap_backend_type=ldap_backend_type, sitename=sitename)
861 if lp.get("server role") == "domain controller":
862 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
863 "{" + policyguid + "}")
864 os.makedirs(policy_path, 0755)
865 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
866 os.makedirs(os.path.join(policy_path, "User"), 0755)
867 if not os.path.isdir(paths.netlogon):
868 os.makedirs(paths.netlogon, 0755)
869 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
870 credentials=credentials, lp=lp)
871 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
872 netbiosname=netbiosname, domainsid=domainsid,
873 keytab_path=paths.keytab, samdb_url=paths.samdb,
874 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
875 machinepass=machinepass, dnsdomain=dnsdomain)
877 if samdb_fill == FILL_FULL:
878 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
879 nobody=nobody, nogroup=nogroup, wheel=wheel,
880 users=users, backup=backup)
882 message("Setting up sam.ldb rootDSE marking as synchronized")
883 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
885 message("Setting up phpLDAPadmin configuration")
886 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
889 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
891 if lp.get("server role") == "domain controller":
892 samdb = SamDB(paths.samdb, session_info=session_info,
893 credentials=credentials, lp=lp)
895 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
896 assert isinstance(domainguid, str)
897 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
898 expression="(&(objectClass=computer)(cn=%s))" % hostname,
900 assert isinstance(hostguid, str)
902 message("Setting up DNS zone: %s" % dnsdomain)
903 create_zone_file(paths.dns, setup_path, samdb,
904 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
905 domaindn=domaindn, dnspass=dnspass, realm=realm,
906 domainguid=domainguid, hostguid=hostguid)
907 message("Please install the zone located in %s into your DNS server" % paths.dns)
912 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
913 """Create a PHP LDAP admin configuration file.
915 :param path: Path to write the configuration to.
916 :param setup_path: Function to generate setup paths.
918 setup_file(setup_path("phpldapadmin-config.php"), path,
919 {"S4_LDAPI_URI": ldapi_uri})
922 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
923 hostip, hostname, dnspass, realm, domainguid, hostguid):
924 """Write out a DNS zone file, from the info in the current database.
926 :param path: Path of the new file.
927 :param setup_path": Setup path function.
928 :param samdb: SamDB object
929 :param dnsdomain: DNS Domain name
930 :param domaindn: DN of the Domain
931 :param hostip: Local IP
932 :param hostname: Local hostname
933 :param dnspass: Password for DNS
934 :param realm: Realm name
935 :param domainguid: GUID of the domain.
936 :param hostguid: GUID of the host.
938 assert isinstance(domainguid, str)
940 setup_file(setup_path("provision.zone"), path, {
941 "DNSPASS_B64": b64encode(dnspass),
942 "HOSTNAME": hostname,
943 "DNSDOMAIN": dnsdomain,
946 "DOMAINGUID": domainguid,
947 "DATESTRING": time.strftime("%Y%m%d%H"),
948 "DEFAULTSITE": DEFAULTSITE,
949 "HOSTGUID": hostguid,
953 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
954 """Load schema for the SamDB.
956 :param samdb: Load a schema into a SamDB.
957 :param setup_path: Setup path function.
958 :param schemadn: DN of the schema
959 :param netbiosname: NetBIOS name of the host.
960 :param configdn: DN of the configuration
962 schema_data = open(setup_path("schema.ldif"), 'r').read()
963 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
964 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
965 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
966 head_data = substitute_var(head_data, {
967 "SCHEMADN": schemadn,
968 "NETBIOSNAME": netbiosname,
969 "CONFIGDN": configdn,
970 "DEFAULTSITE":sitename
972 samdb.attach_schema_from_ldif(head_data, schema_data)