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, 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)
384 message("Erasing data from partitions")
385 samdb.erase_partitions()
388 samdb.transaction_cancel()
391 samdb.transaction_commit()
396 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
397 netbiosname, domainsid, keytab_path, samdb_url,
398 dns_keytab_path, dnspass, machinepass):
399 """Add DC-specific bits to a secrets database.
401 :param secretsdb: Ldb Handle to the secrets database
402 :param setup_path: Setup path function
403 :param machinepass: Machine password
405 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
406 "MACHINEPASS_B64": b64encode(machinepass),
409 "DNSDOMAIN": dnsdomain,
410 "DOMAINSID": str(domainsid),
411 "SECRETS_KEYTAB": keytab_path,
412 "NETBIOSNAME": netbiosname,
413 "SAM_LDB": samdb_url,
414 "DNS_KEYTAB": dns_keytab_path,
415 "DNSPASS_B64": b64encode(dnspass),
419 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
420 """Setup the secrets database.
422 :param path: Path to the secrets database.
423 :param setup_path: Get the path to a setup file.
424 :param session_info: Session info.
425 :param credentials: Credentials
426 :param lp: Loadparm context
427 :return: LDB handle for the created secrets database
429 if os.path.exists(path):
431 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
434 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
435 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
437 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
441 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
442 """Setup the templates database.
444 :param path: Path to the database.
445 :param setup_path: Function for obtaining the path to setup files.
446 :param session_info: Session info
447 :param credentials: Credentials
448 :param lp: Loadparm context
450 templates_ldb = SamDB(path, session_info=session_info,
451 credentials=credentials, lp=lp)
452 templates_ldb.erase()
453 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
456 def setup_registry(path, setup_path, session_info, credentials, lp):
457 """Setup the registry.
459 :param path: Path to the registry database
460 :param setup_path: Function that returns the path to a setup.
461 :param session_info: Session information
462 :param credentials: Credentials
463 :param lp: Loadparm context
465 reg = registry.Registry()
466 hive = registry.open_ldb(path, session_info=session_info,
467 credentials=credentials, lp_ctx=lp)
468 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
469 provision_reg = setup_path("provision.reg")
470 assert os.path.exists(provision_reg)
471 reg.diff_apply(provision_reg)
474 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
475 dnsdomain, realm, rootdn, configdn, netbiosname):
476 """Setup the SamDB rootdse.
478 :param samdb: Sam Database handle
479 :param setup_path: Obtain setup path
481 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
482 "SCHEMADN": schemadn,
483 "NETBIOSNAME": netbiosname,
484 "DNSDOMAIN": dnsdomain,
485 "DEFAULTSITE": DEFAULTSITE,
487 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
488 "DOMAINDN": domaindn,
490 "CONFIGDN": configdn,
491 "VERSION": samba.version(),
495 def setup_self_join(samdb, configdn, schemadn, domaindn,
496 netbiosname, hostname, dnsdomain, machinepass, dnspass,
497 realm, domainname, domainsid, invocationid, setup_path,
498 policyguid, hostguid=None):
499 """Join a host to its own domain."""
500 if hostguid is not None:
501 hostguid_add = "objectGUID: %s" % hostguid
505 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
506 "CONFIGDN": configdn,
507 "SCHEMADN": schemadn,
508 "DOMAINDN": domaindn,
509 "INVOCATIONID": invocationid,
510 "NETBIOSNAME": netbiosname,
511 "DEFAULTSITE": DEFAULTSITE,
512 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
513 "MACHINEPASS_B64": b64encode(machinepass),
514 "DNSPASS_B64": b64encode(dnspass),
516 "DOMAIN": domainname,
517 "HOSTGUID_ADD": hostguid_add,
518 "DNSDOMAIN": dnsdomain})
519 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
520 "POLICYGUID": policyguid,
521 "DNSDOMAIN": dnsdomain,
522 "DOMAINSID": str(domainsid),
523 "DOMAINDN": domaindn})
526 def setup_samdb(path, setup_path, session_info, credentials, lp,
527 schemadn, configdn, domaindn, dnsdomain, realm,
528 netbiosname, message, hostname, rootdn, erase,
529 domainsid, aci, domainguid, policyguid,
530 domainname, fill, adminpass, krbtgtpass,
531 machinepass, hostguid, invocationid, dnspass,
532 serverrole, ldap_backend=None, ldap_backend_type=None):
533 """Setup a complete SAM Database.
535 :note: This will wipe the main SAM database file!
538 assert serverrole in ("domain controller", "member server")
540 # Also wipes the database
541 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
542 domaindn=domaindn, message=message, lp=lp,
543 credentials=credentials, session_info=session_info,
544 hostname=hostname, netbiosname=netbiosname,
545 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
546 ldap_backend=ldap_backend, serverrole=serverrole,
547 ldap_backend_type=ldap_backend_type, erase=erase)
549 samdb = SamDB(path, session_info=session_info,
550 credentials=credentials, lp=lp)
553 # We want to finish here, but setup the index before we do so
554 message("Setting up sam.ldb index")
555 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
558 message("Pre-loading the Samba 4 and AD schema")
559 samdb = SamDB(path, session_info=session_info,
560 credentials=credentials, lp=lp)
561 samdb.set_domain_sid(domainsid)
562 if lp.get("server role") == "domain controller":
563 samdb.set_invocation_id(invocationid)
565 load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
567 samdb.transaction_start()
570 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
571 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
572 "DOMAINDN": domaindn,
576 message("Modifying DomainDN: " + domaindn + "")
577 if domainguid is not None:
578 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
582 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
583 "LDAPTIME": timestring(int(time.time())),
584 "DOMAINSID": str(domainsid),
585 "SCHEMADN": schemadn,
586 "NETBIOSNAME": netbiosname,
587 "DEFAULTSITE": DEFAULTSITE,
588 "CONFIGDN": configdn,
589 "POLICYGUID": policyguid,
590 "DOMAINDN": domaindn,
591 "DOMAINGUID_MOD": domainguid_mod,
594 message("Adding configuration container (permitted to fail)")
595 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
596 "CONFIGDN": configdn,
598 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
600 message("Modifying configuration container")
601 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
602 "CONFIGDN": configdn,
603 "SCHEMADN": schemadn,
606 message("Adding schema container (permitted to fail)")
607 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
608 "SCHEMADN": schemadn,
610 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
612 message("Modifying schema container")
613 setup_modify_ldif(samdb,
614 setup_path("provision_schema_basedn_modify.ldif"), {
615 "SCHEMADN": schemadn,
616 "NETBIOSNAME": netbiosname,
617 "DEFAULTSITE": DEFAULTSITE,
618 "CONFIGDN": configdn,
621 message("Setting up sam.ldb Samba4 schema")
622 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
623 {"SCHEMADN": schemadn })
624 message("Setting up sam.ldb AD schema")
625 setup_add_ldif(samdb, setup_path("schema.ldif"),
626 {"SCHEMADN": schemadn})
628 message("Setting up sam.ldb configuration data")
629 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
630 "CONFIGDN": configdn,
631 "NETBIOSNAME": netbiosname,
632 "DEFAULTSITE": DEFAULTSITE,
633 "DNSDOMAIN": dnsdomain,
634 "DOMAIN": domainname,
635 "SCHEMADN": schemadn,
636 "DOMAINDN": domaindn,
639 message("Setting up display specifiers")
640 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
641 {"CONFIGDN": configdn})
643 message("Adding users container (permitted to fail)")
644 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
645 "DOMAINDN": domaindn})
646 message("Modifying users container")
647 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
648 "DOMAINDN": domaindn})
649 message("Adding computers container (permitted to fail)")
650 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
651 "DOMAINDN": domaindn})
652 message("Modifying computers container")
653 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
654 "DOMAINDN": domaindn})
655 message("Setting up sam.ldb data")
656 setup_add_ldif(samdb, setup_path("provision.ldif"), {
657 "DOMAINDN": domaindn,
658 "NETBIOSNAME": netbiosname,
659 "DEFAULTSITE": DEFAULTSITE,
660 "CONFIGDN": configdn,
663 if fill == FILL_FULL:
664 message("Setting up sam.ldb users and groups")
665 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
666 "DOMAINDN": domaindn,
667 "DOMAINSID": str(domainsid),
668 "CONFIGDN": configdn,
669 "ADMINPASS_B64": b64encode(adminpass),
670 "KRBTGTPASS_B64": b64encode(krbtgtpass),
673 if lp.get("server role") == "domain controller":
674 message("Setting up self join")
675 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
676 domaindn=domaindn, invocationid=invocationid,
677 dnspass=dnspass, netbiosname=netbiosname,
678 dnsdomain=dnsdomain, realm=realm,
679 machinepass=machinepass, domainname=domainname,
680 domainsid=domainsid, policyguid=policyguid,
681 hostname=hostname, hostguid=hostguid,
682 setup_path=setup_path)
684 #We want to setup the index last, as adds are faster unindexed
685 message("Setting up sam.ldb index")
686 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
688 samdb.transaction_cancel()
691 samdb.transaction_commit()
695 FILL_NT4SYNC = "NT4SYNC"
698 def provision(lp, setup_dir, message, paths, session_info,
699 credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None,
700 domain=None, hostname=None, hostip=None, domainsid=None,
701 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
702 policyguid=None, invocationid=None, machinepass=None,
703 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
704 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
705 ldap_backend=None, ldap_backend_type=None):
708 :note: caution, this wipes all existing data!
711 def setup_path(file):
712 return os.path.join(setup_dir, file)
714 if domainsid is None:
715 domainsid = security.random_sid()
716 if policyguid is None:
717 policyguid = uuid.random()
718 if adminpass is None:
719 adminpass = misc.random_password(12)
720 if krbtgtpass is None:
721 krbtgtpass = misc.random_password(12)
722 if machinepass is None:
723 machinepass = misc.random_password(12)
725 dnspass = misc.random_password(12)
727 root = findnss(pwd.getpwnam, ["root"])[0]
729 nobody = findnss(pwd.getpwnam, ["nobody"])[0]
731 nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
733 users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown",
736 wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
738 backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
740 aci = "# no aci for local ldb"
741 if serverrole is None:
742 serverrole = lp.get("server role")
743 assert serverrole in ("domain controller", "member server")
744 if invocationid is None and serverrole == "domain controller":
745 invocationid = uuid.random()
748 realm = lp.get("realm")
750 if lp.get("realm").upper() != realm.upper():
751 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
752 (lp.get("realm"), realm))
754 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
756 if ldap_backend == "ldapi":
757 # provision-backend will set this path suggested slapd command line / fedorads.inf
758 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
760 assert realm is not None
761 realm = realm.upper()
764 hostname = gethostname().split(".")[0].lower()
767 hostip = gethostbyname(hostname)
769 netbiosname = hostname.upper()
770 if not valid_netbios_name(netbiosname):
771 raise InvalidNetbiosName(netbiosname)
773 dnsdomain = realm.lower()
774 if serverrole == "domain controller":
775 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
777 domain = lp.get("workgroup")
779 if lp.get("workgroup").upper() != domain.upper():
780 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
781 lp.get("workgroup"), domain)
783 assert domain is not None
784 domain = domain.upper()
785 if not valid_netbios_name(domain):
786 raise InvalidNetbiosName(domain)
788 domaindn = "CN=" + netbiosname
794 configdn = "CN=Configuration," + rootdn
795 schemadn = "CN=Schema," + configdn
797 message("set DOMAIN SID: %s" % str(domainsid))
798 message("Provisioning for %s in realm %s" % (domain, realm))
799 message("Using administrator password: %s" % adminpass)
801 assert paths.smbconf is not None
803 # only install a new smb.conf if there isn't one there already
804 if not os.path.exists(paths.smbconf):
805 message("Setting up smb.conf")
806 if serverrole == "domain controller":
808 elif serverrole == "member":
809 smbconfsuffix = "member"
810 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
812 "HOSTNAME": hostname,
813 "DOMAIN_CONF": domain,
815 "SERVERROLE": serverrole,
816 "NETLOGONPATH": paths.netlogon,
817 "SYSVOLPATH": paths.sysvol,
819 lp.load(paths.smbconf)
821 # only install a new shares config db if there is none
822 if not os.path.exists(paths.shareconf):
823 message("Setting up share.ldb")
824 share_ldb = Ldb(paths.shareconf, session_info=session_info,
825 credentials=credentials, lp=lp)
826 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
829 message("Setting up secrets.ldb")
830 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
831 session_info=session_info,
832 credentials=credentials, lp=lp)
834 message("Setting up the registry")
835 setup_registry(paths.hklm, setup_path, session_info,
836 credentials=credentials, lp=lp)
838 message("Setting up templates db")
839 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
840 credentials=credentials, lp=lp)
842 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
843 credentials=credentials, lp=lp, schemadn=schemadn,
844 configdn=configdn, domaindn=domaindn,
845 dnsdomain=dnsdomain, netbiosname=netbiosname,
846 realm=realm, message=message, hostname=hostname,
847 rootdn=rootdn, erase=erase, domainsid=domainsid,
848 aci=aci, domainguid=domainguid, policyguid=policyguid,
849 domainname=domain, fill=samdb_fill,
850 adminpass=adminpass, krbtgtpass=krbtgtpass,
851 hostguid=hostguid, invocationid=invocationid,
852 machinepass=machinepass, dnspass=dnspass,
853 serverrole=serverrole, ldap_backend=ldap_backend,
854 ldap_backend_type=ldap_backend_type)
856 if lp.get("server role") == "domain controller":
857 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
858 "{" + policyguid + "}")
859 os.makedirs(policy_path, 0755)
860 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
861 os.makedirs(os.path.join(policy_path, "User"), 0755)
862 if not os.path.isdir(paths.netlogon):
863 os.makedirs(paths.netlogon, 0755)
864 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
865 credentials=credentials, lp=lp)
866 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
867 netbiosname=netbiosname, domainsid=domainsid,
868 keytab_path=paths.keytab, samdb_url=paths.samdb,
869 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
870 machinepass=machinepass, dnsdomain=dnsdomain)
872 if samdb_fill == FILL_FULL:
873 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
874 nobody=nobody, nogroup=nogroup, wheel=wheel,
875 users=users, backup=backup)
877 message("Setting up sam.ldb rootDSE marking as synchronized")
878 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
880 message("Setting up phpLDAPadmin configuration")
881 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
884 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
886 if lp.get("server role") == "domain controller":
887 samdb = SamDB(paths.samdb, session_info=session_info,
888 credentials=credentials, lp=lp)
890 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
891 assert isinstance(domainguid, str)
892 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
893 expression="(&(objectClass=computer)(cn=%s))" % hostname,
895 assert isinstance(hostguid, str)
897 message("Setting up DNS zone: %s" % dnsdomain)
898 create_zone_file(paths.dns, setup_path, samdb,
899 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
900 domaindn=domaindn, dnspass=dnspass, realm=realm,
901 domainguid=domainguid, hostguid=hostguid)
902 message("Please install the zone located in %s into your DNS server" % paths.dns)
907 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
908 """Create a PHP LDAP admin configuration file.
910 :param path: Path to write the configuration to.
911 :param setup_path: Function to generate setup paths.
913 setup_file(setup_path("phpldapadmin-config.php"), path,
914 {"S4_LDAPI_URI": ldapi_uri})
917 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
918 hostip, hostname, dnspass, realm, domainguid, hostguid):
919 """Write out a DNS zone file, from the info in the current database.
921 :param path: Path of the new file.
922 :param setup_path": Setup path function.
923 :param samdb: SamDB object
924 :param dnsdomain: DNS Domain name
925 :param domaindn: DN of the Domain
926 :param hostip: Local IP
927 :param hostname: Local hostname
928 :param dnspass: Password for DNS
929 :param realm: Realm name
930 :param domainguid: GUID of the domain.
931 :param hostguid: GUID of the host.
933 assert isinstance(domainguid, str)
935 setup_file(setup_path("provision.zone"), path, {
936 "DNSPASS_B64": b64encode(dnspass),
937 "HOSTNAME": hostname,
938 "DNSDOMAIN": dnsdomain,
941 "DOMAINGUID": domainguid,
942 "DATESTRING": time.strftime("%Y%m%d%H"),
943 "DEFAULTSITE": DEFAULTSITE,
944 "HOSTGUID": hostguid,
948 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
949 """Load schema for the SamDB.
951 :param samdb: Load a schema into a SamDB.
952 :param setup_path: Setup path function.
953 :param schemadn: DN of the schema
954 :param netbiosname: NetBIOS name of the host.
955 :param configdn: DN of the configuration
957 schema_data = open(setup_path("schema.ldif"), 'r').read()
958 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
959 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
960 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
961 head_data = substitute_var(head_data, {
962 "SCHEMADN": schemadn,
963 "NETBIOSNAME": netbiosname,
964 "CONFIGDN": configdn,
965 "DEFAULTSITE": DEFAULTSITE
967 samdb.attach_schema_from_ldif(head_data, schema_data)