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()
699 FILL_NT4SYNC = "NT4SYNC"
702 def provision(lp, setup_dir, message, paths, session_info,
703 credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None,
704 domain=None, hostname=None, hostip=None, domainsid=None,
705 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
706 policyguid=None, invocationid=None, machinepass=None,
707 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
708 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
709 ldap_backend=None, ldap_backend_type=None, sitename=DEFAULTSITE):
712 :note: caution, this wipes all existing data!
715 def setup_path(file):
716 return os.path.join(setup_dir, file)
718 if domainsid is None:
719 domainsid = security.random_sid()
720 if policyguid is None:
721 policyguid = uuid.random()
722 if adminpass is None:
723 adminpass = misc.random_password(12)
724 if krbtgtpass is None:
725 krbtgtpass = misc.random_password(12)
726 if machinepass is None:
727 machinepass = misc.random_password(12)
729 dnspass = misc.random_password(12)
731 root = findnss(pwd.getpwnam, ["root"])[0]
733 nobody = findnss(pwd.getpwnam, ["nobody"])[0]
735 nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
737 users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown",
740 wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
742 backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
744 aci = "# no aci for local ldb"
745 if serverrole is None:
746 serverrole = lp.get("server role")
747 assert serverrole in ("domain controller", "member server")
748 if invocationid is None and serverrole == "domain controller":
749 invocationid = uuid.random()
752 realm = lp.get("realm")
754 if lp.get("realm").upper() != realm.upper():
755 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
756 (lp.get("realm"), realm))
758 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
760 if ldap_backend == "ldapi":
761 # provision-backend will set this path suggested slapd command line / fedorads.inf
762 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
764 assert realm is not None
765 realm = realm.upper()
768 hostname = gethostname().split(".")[0].lower()
771 hostip = gethostbyname(hostname)
773 netbiosname = hostname.upper()
774 if not valid_netbios_name(netbiosname):
775 raise InvalidNetbiosName(netbiosname)
777 dnsdomain = realm.lower()
778 if serverrole == "domain controller":
779 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
781 domain = lp.get("workgroup")
783 if lp.get("workgroup").upper() != domain.upper():
784 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
785 lp.get("workgroup"), domain)
787 assert domain is not None
788 domain = domain.upper()
789 if not valid_netbios_name(domain):
790 raise InvalidNetbiosName(domain)
792 domaindn = "CN=" + netbiosname
798 configdn = "CN=Configuration," + rootdn
799 schemadn = "CN=Schema," + configdn
801 message("set DOMAIN SID: %s" % str(domainsid))
802 message("Provisioning for %s in realm %s" % (domain, realm))
803 message("Using administrator password: %s" % adminpass)
805 assert paths.smbconf is not None
807 # only install a new smb.conf if there isn't one there already
808 if not os.path.exists(paths.smbconf):
809 message("Setting up smb.conf")
810 if serverrole == "domain controller":
812 elif serverrole == "member":
813 smbconfsuffix = "member"
814 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
816 "HOSTNAME": hostname,
817 "DOMAIN_CONF": domain,
819 "SERVERROLE": serverrole,
820 "NETLOGONPATH": paths.netlogon,
821 "SYSVOLPATH": paths.sysvol,
823 lp.load(paths.smbconf)
825 # only install a new shares config db if there is none
826 if not os.path.exists(paths.shareconf):
827 message("Setting up share.ldb")
828 share_ldb = Ldb(paths.shareconf, session_info=session_info,
829 credentials=credentials, lp=lp)
830 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
833 message("Setting up secrets.ldb")
834 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
835 session_info=session_info,
836 credentials=credentials, lp=lp)
838 message("Setting up the registry")
839 setup_registry(paths.hklm, setup_path, session_info,
840 credentials=credentials, lp=lp)
842 message("Setting up templates db")
843 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
844 credentials=credentials, lp=lp)
846 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
847 credentials=credentials, lp=lp, schemadn=schemadn,
848 configdn=configdn, domaindn=domaindn,
849 dnsdomain=dnsdomain, netbiosname=netbiosname,
850 realm=realm, message=message, hostname=hostname,
851 rootdn=rootdn, erase=erase, domainsid=domainsid,
852 aci=aci, domainguid=domainguid, policyguid=policyguid,
853 domainname=domain, fill=samdb_fill,
854 adminpass=adminpass, krbtgtpass=krbtgtpass,
855 hostguid=hostguid, invocationid=invocationid,
856 machinepass=machinepass, dnspass=dnspass,
857 serverrole=serverrole, ldap_backend=ldap_backend,
858 ldap_backend_type=ldap_backend_type, sitename=sitename)
860 if lp.get("server role") == "domain controller":
861 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
862 "{" + policyguid + "}")
863 os.makedirs(policy_path, 0755)
864 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
865 os.makedirs(os.path.join(policy_path, "User"), 0755)
866 if not os.path.isdir(paths.netlogon):
867 os.makedirs(paths.netlogon, 0755)
868 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
869 credentials=credentials, lp=lp)
870 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
871 netbiosname=netbiosname, domainsid=domainsid,
872 keytab_path=paths.keytab, samdb_url=paths.samdb,
873 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
874 machinepass=machinepass, dnsdomain=dnsdomain)
876 if samdb_fill == FILL_FULL:
877 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
878 nobody=nobody, nogroup=nogroup, wheel=wheel,
879 users=users, backup=backup)
881 message("Setting up sam.ldb rootDSE marking as synchronized")
882 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
884 message("Setting up phpLDAPadmin configuration")
885 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
888 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
890 if lp.get("server role") == "domain controller":
891 samdb = SamDB(paths.samdb, session_info=session_info,
892 credentials=credentials, lp=lp)
894 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
895 assert isinstance(domainguid, str)
896 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
897 expression="(&(objectClass=computer)(cn=%s))" % hostname,
899 assert isinstance(hostguid, str)
901 message("Setting up DNS zone: %s" % dnsdomain)
902 create_zone_file(paths.dns, setup_path, samdb,
903 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
904 domaindn=domaindn, dnspass=dnspass, realm=realm,
905 domainguid=domainguid, hostguid=hostguid)
906 message("Please install the zone located in %s into your DNS server" % paths.dns)
911 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
912 """Create a PHP LDAP admin configuration file.
914 :param path: Path to write the configuration to.
915 :param setup_path: Function to generate setup paths.
917 setup_file(setup_path("phpldapadmin-config.php"), path,
918 {"S4_LDAPI_URI": ldapi_uri})
921 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
922 hostip, hostname, dnspass, realm, domainguid, hostguid):
923 """Write out a DNS zone file, from the info in the current database.
925 :param path: Path of the new file.
926 :param setup_path": Setup path function.
927 :param samdb: SamDB object
928 :param dnsdomain: DNS Domain name
929 :param domaindn: DN of the Domain
930 :param hostip: Local IP
931 :param hostname: Local hostname
932 :param dnspass: Password for DNS
933 :param realm: Realm name
934 :param domainguid: GUID of the domain.
935 :param hostguid: GUID of the host.
937 assert isinstance(domainguid, str)
939 setup_file(setup_path("provision.zone"), path, {
940 "DNSPASS_B64": b64encode(dnspass),
941 "HOSTNAME": hostname,
942 "DNSDOMAIN": dnsdomain,
945 "DOMAINGUID": domainguid,
946 "DATESTRING": time.strftime("%Y%m%d%H"),
947 "DEFAULTSITE": DEFAULTSITE,
948 "HOSTGUID": hostguid,
952 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename):
953 """Load schema for the SamDB.
955 :param samdb: Load a schema into a SamDB.
956 :param setup_path: Setup path function.
957 :param schemadn: DN of the schema
958 :param netbiosname: NetBIOS name of the host.
959 :param configdn: DN of the configuration
961 schema_data = open(setup_path("schema.ldif"), 'r').read()
962 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
963 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
964 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
965 head_data = substitute_var(head_data, {
966 "SCHEMADN": schemadn,
967 "NETBIOSNAME": netbiosname,
968 "CONFIGDN": configdn,
969 "DEFAULTSITE":sitename
971 samdb.attach_schema_from_ldif(head_data, schema_data)