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.
283 :note: This will wipe the Sam Database!
285 :note: This function always removes the local SAM LDB file. The erase
286 parameter controls whether to erase the existing data, which
287 may not be stored locally but in LDAP.
289 assert session_info is not None
291 if os.path.exists(samdb_path):
292 os.unlink(samdb_path)
294 # Also wipes the database
295 samdb = SamDB(samdb_path, session_info=session_info,
296 credentials=credentials, lp=lp)
298 #Add modules to the list to activate them by default
299 #beware often order is important
301 # Some Known ordering constraints:
302 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
303 # - objectclass must be before password_hash, because password_hash checks
304 # that the objectclass is of type person (filled in by objectclass
305 # module when expanding the objectclass list)
306 # - partition must be last
307 # - each partition has its own module list then
308 modules_list = ["rootdse",
324 modules_list2 = ["show_deleted",
327 domaindn_ldb = "users.ldb"
328 if ldap_backend is not None:
329 domaindn_ldb = ldap_backend
330 configdn_ldb = "configuration.ldb"
331 if ldap_backend is not None:
332 configdn_ldb = ldap_backend
333 schemadn_ldb = "schema.ldb"
334 if ldap_backend is not None:
335 schema_ldb = ldap_backend
337 schemadn_ldb = ldap_backend
339 if ldap_backend_type == "fedora-ds":
340 backend_modules = ["nsuniqueid","paged_searches"]
341 elif ldap_backend_type == "openldap":
342 backend_modules = ["normalise","entryuuid","paged_searches"]
343 elif serverrole == "domain controller":
344 backend_modules = ["repl_meta_data"]
346 backend_modules = ["objectguid"]
348 samdb.transaction_start()
350 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
351 "SCHEMADN": schemadn,
352 "SCHEMADN_LDB": schemadn_ldb,
353 "SCHEMADN_MOD2": ",objectguid",
354 "CONFIGDN": configdn,
355 "CONFIGDN_LDB": configdn_ldb,
356 "DOMAINDN": domaindn,
357 "DOMAINDN_LDB": domaindn_ldb,
358 "SCHEMADN_MOD": "schema_fsmo,instancetype",
359 "CONFIGDN_MOD": "naming_fsmo,instancetype",
360 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
361 "MODULES_LIST": ",".join(modules_list),
362 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
363 "MODULES_LIST2": ",".join(modules_list2),
364 "BACKEND_MOD": ",".join(backend_modules),
368 samdb.transaction_cancel()
371 samdb.transaction_commit()
373 samdb = SamDB(samdb_path, session_info=session_info,
374 credentials=credentials, lp=lp)
376 samdb.transaction_start()
378 message("Setting up sam.ldb attributes")
379 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
381 message("Setting up sam.ldb rootDSE")
382 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
383 dnsdomain, realm, rootdn, configdn, netbiosname)
386 message("Erasing data from partitions")
387 samdb.erase_partitions()
390 samdb.transaction_cancel()
393 samdb.transaction_commit()
398 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
399 netbiosname, domainsid, keytab_path, samdb_url,
400 dns_keytab_path, dnspass, machinepass):
401 """Add DC-specific bits to a secrets database.
403 :param secretsdb: Ldb Handle to the secrets database
404 :param setup_path: Setup path function
405 :param machinepass: Machine password
407 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
408 "MACHINEPASS_B64": b64encode(machinepass),
411 "DNSDOMAIN": dnsdomain,
412 "DOMAINSID": str(domainsid),
413 "SECRETS_KEYTAB": keytab_path,
414 "NETBIOSNAME": netbiosname,
415 "SAM_LDB": samdb_url,
416 "DNS_KEYTAB": dns_keytab_path,
417 "DNSPASS_B64": b64encode(dnspass),
421 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
422 """Setup the secrets database.
424 :param path: Path to the secrets database.
425 :param setup_path: Get the path to a setup file.
426 :param session_info: Session info.
427 :param credentials: Credentials
428 :param lp: Loadparm context
429 :return: LDB handle for the created secrets database
431 if os.path.exists(path):
433 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
436 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
437 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
439 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
443 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
444 """Setup the templates database.
446 :param path: Path to the database.
447 :param setup_path: Function for obtaining the path to setup files.
448 :param session_info: Session info
449 :param credentials: Credentials
450 :param lp: Loadparm context
452 templates_ldb = SamDB(path, session_info=session_info,
453 credentials=credentials, lp=lp)
454 templates_ldb.erase()
455 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
458 def setup_registry(path, setup_path, session_info, credentials, lp):
459 """Setup the registry.
461 :param path: Path to the registry database
462 :param setup_path: Function that returns the path to a setup.
463 :param session_info: Session information
464 :param credentials: Credentials
465 :param lp: Loadparm context
467 reg = registry.Registry()
468 hive = registry.open_ldb(path, session_info=session_info,
469 credentials=credentials, lp_ctx=lp)
470 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
471 provision_reg = setup_path("provision.reg")
472 assert os.path.exists(provision_reg)
473 reg.diff_apply(provision_reg)
476 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
477 dnsdomain, realm, rootdn, configdn, netbiosname):
478 """Setup the SamDB rootdse.
480 :param samdb: Sam Database handle
481 :param setup_path: Obtain setup path
484 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
485 "SCHEMADN": schemadn,
486 "NETBIOSNAME": netbiosname,
487 "DNSDOMAIN": dnsdomain,
488 "DEFAULTSITE": DEFAULTSITE,
490 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
491 "DOMAINDN": domaindn,
493 "CONFIGDN": configdn,
494 "VERSION": samba.version(),
498 def setup_self_join(samdb, configdn, schemadn, domaindn,
499 netbiosname, hostname, dnsdomain, machinepass, dnspass,
500 realm, domainname, domainsid, invocationid, setup_path,
501 policyguid, hostguid=None):
502 """Join a host to its own domain."""
503 if hostguid is not None:
504 hostguid_add = "objectGUID: %s" % hostguid
508 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
509 "CONFIGDN": configdn,
510 "SCHEMADN": schemadn,
511 "DOMAINDN": domaindn,
512 "INVOCATIONID": invocationid,
513 "NETBIOSNAME": netbiosname,
514 "DEFAULTSITE": DEFAULTSITE,
515 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
516 "MACHINEPASS_B64": b64encode(machinepass),
517 "DNSPASS_B64": b64encode(dnspass),
519 "DOMAIN": domainname,
520 "HOSTGUID_ADD": hostguid_add,
521 "DNSDOMAIN": dnsdomain})
522 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
523 "POLICYGUID": policyguid,
524 "DNSDOMAIN": dnsdomain,
525 "DOMAINSID": str(domainsid),
526 "DOMAINDN": domaindn})
529 def setup_samdb(path, setup_path, session_info, credentials, lp,
530 schemadn, configdn, domaindn, dnsdomain, realm,
531 netbiosname, message, hostname, rootdn, erase,
532 domainsid, aci, domainguid, policyguid,
533 domainname, fill, adminpass, krbtgtpass,
534 machinepass, hostguid, invocationid, dnspass,
535 serverrole, ldap_backend=None, ldap_backend_type=None):
536 """Setup a complete SAM Database.
538 :note: This will wipe the main SAM database file!
541 # Also wipes the database
542 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
543 domaindn=domaindn, message=message, lp=lp,
544 credentials=credentials, session_info=session_info,
545 hostname=hostname, netbiosname=netbiosname,
546 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
547 ldap_backend=ldap_backend, serverrole=serverrole,
548 ldap_backend_type=ldap_backend_type, erase=erase)
550 samdb = SamDB(path, session_info=session_info,
551 credentials=credentials, lp=lp)
554 # We want to finish here, but setup the index before we do so
555 message("Setting up sam.ldb index")
556 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
559 message("Pre-loading the Samba 4 and AD schema")
560 samdb = SamDB(path, session_info=session_info,
561 credentials=credentials, lp=lp)
562 samdb.set_domain_sid(domainsid)
563 if lp.get("server role") == "domain controller":
564 samdb.set_invocation_id(invocationid)
566 load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
568 samdb.transaction_start()
571 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
572 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
573 "DOMAINDN": domaindn,
577 message("Modifying DomainDN: " + domaindn + "")
578 if domainguid is not None:
579 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
583 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
584 "LDAPTIME": timestring(int(time.time())),
585 "DOMAINSID": str(domainsid),
586 "SCHEMADN": schemadn,
587 "NETBIOSNAME": netbiosname,
588 "DEFAULTSITE": DEFAULTSITE,
589 "CONFIGDN": configdn,
590 "POLICYGUID": policyguid,
591 "DOMAINDN": domaindn,
592 "DOMAINGUID_MOD": domainguid_mod,
595 message("Adding configuration container (permitted to fail)")
596 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
597 "CONFIGDN": configdn,
599 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
601 message("Modifying configuration container")
602 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
603 "CONFIGDN": configdn,
604 "SCHEMADN": schemadn,
607 message("Adding schema container (permitted to fail)")
608 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
609 "SCHEMADN": schemadn,
611 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
613 message("Modifying schema container")
614 setup_modify_ldif(samdb,
615 setup_path("provision_schema_basedn_modify.ldif"), {
616 "SCHEMADN": schemadn,
617 "NETBIOSNAME": netbiosname,
618 "DEFAULTSITE": DEFAULTSITE,
619 "CONFIGDN": configdn,
622 message("Setting up sam.ldb Samba4 schema")
623 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
624 {"SCHEMADN": schemadn })
625 message("Setting up sam.ldb AD schema")
626 setup_add_ldif(samdb, setup_path("schema.ldif"),
627 {"SCHEMADN": schemadn})
629 message("Setting up sam.ldb configuration data")
630 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
631 "CONFIGDN": configdn,
632 "NETBIOSNAME": netbiosname,
633 "DEFAULTSITE": DEFAULTSITE,
634 "DNSDOMAIN": dnsdomain,
635 "DOMAIN": domainname,
636 "SCHEMADN": schemadn,
637 "DOMAINDN": domaindn,
640 message("Setting up display specifiers")
641 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
642 {"CONFIGDN": configdn})
644 message("Adding users container (permitted to fail)")
645 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
646 "DOMAINDN": domaindn})
647 message("Modifying users container")
648 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
649 "DOMAINDN": domaindn})
650 message("Adding computers container (permitted to fail)")
651 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
652 "DOMAINDN": domaindn})
653 message("Modifying computers container")
654 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
655 "DOMAINDN": domaindn})
656 message("Setting up sam.ldb data")
657 setup_add_ldif(samdb, setup_path("provision.ldif"), {
658 "DOMAINDN": domaindn,
659 "NETBIOSNAME": netbiosname,
660 "DEFAULTSITE": DEFAULTSITE,
661 "CONFIGDN": configdn,
664 if fill == FILL_FULL:
665 message("Setting up sam.ldb users and groups")
666 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
667 "DOMAINDN": domaindn,
668 "DOMAINSID": str(domainsid),
669 "CONFIGDN": configdn,
670 "ADMINPASS_B64": b64encode(adminpass),
671 "KRBTGTPASS_B64": b64encode(krbtgtpass),
674 if lp.get("server role") == "domain controller":
675 message("Setting up self join")
676 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
677 domaindn=domaindn, invocationid=invocationid,
678 dnspass=dnspass, netbiosname=netbiosname,
679 dnsdomain=dnsdomain, realm=realm,
680 machinepass=machinepass, domainname=domainname,
681 domainsid=domainsid, policyguid=policyguid,
682 hostname=hostname, hostguid=hostguid,
683 setup_path=setup_path)
685 #We want to setup the index last, as adds are faster unindexed
686 message("Setting up sam.ldb index")
687 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
689 samdb.transaction_cancel()
692 samdb.transaction_commit()
696 FILL_NT4SYNC = "NT4SYNC"
699 def provision(lp, setup_dir, message, paths, session_info,
700 credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None,
701 domain=None, hostname=None, hostip=None, domainsid=None,
702 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
703 policyguid=None, invocationid=None, machinepass=None,
704 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
705 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
706 ldap_backend=None, ldap_backend_type=None):
709 :note: caution, this wipes all existing data!
712 def setup_path(file):
713 return os.path.join(setup_dir, file)
715 if domainsid is None:
716 domainsid = security.random_sid()
717 if policyguid is None:
718 policyguid = uuid.random()
719 if adminpass is None:
720 adminpass = misc.random_password(12)
721 if krbtgtpass is None:
722 krbtgtpass = misc.random_password(12)
723 if machinepass is None:
724 machinepass = misc.random_password(12)
726 dnspass = misc.random_password(12)
728 root = findnss(pwd.getpwnam, ["root"])[0]
730 nobody = findnss(pwd.getpwnam, ["nobody"])[0]
732 nogroup = findnss(grp.getgrnam, ["nogroup", "nobody"])[0]
734 users = findnss(grp.getgrnam, ["users", "guest", "other", "unknown",
737 wheel = findnss(grp.getgrnam, ["wheel", "root", "staff", "adm"])[0]
739 backup = findnss(grp.getgrnam, ["backup", "wheel", "root", "staff"])[0]
741 aci = "# no aci for local ldb"
742 if serverrole is None:
743 serverrole = lp.get("server role")
744 assert serverrole in ("domain controller", "member server")
745 if invocationid is None and serverrole == "domain controller":
746 invocationid = uuid.random()
749 realm = lp.get("realm")
751 if lp.get("realm").upper() != realm.upper():
752 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
753 (lp.get("realm"), realm))
755 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
757 if ldap_backend == "ldapi":
758 # provision-backend will set this path suggested slapd command line / fedorads.inf
759 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
761 assert realm is not None
762 realm = realm.upper()
765 hostname = gethostname().split(".")[0].lower()
768 hostip = gethostbyname(hostname)
770 netbiosname = hostname.upper()
771 if not valid_netbios_name(netbiosname):
772 raise InvalidNetbiosName(netbiosname)
774 dnsdomain = realm.lower()
775 if serverrole == "domain controller":
776 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
778 domain = lp.get("workgroup")
780 if lp.get("workgroup").upper() != domain.upper():
781 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
782 lp.get("workgroup"), domain)
784 assert domain is not None
785 domain = domain.upper()
786 if not valid_netbios_name(domain):
787 raise InvalidNetbiosName(domain)
789 domaindn = "CN=" + netbiosname
795 configdn = "CN=Configuration," + rootdn
796 schemadn = "CN=Schema," + configdn
798 message("set DOMAIN SID: %s" % str(domainsid))
799 message("Provisioning for %s in realm %s" % (domain, realm))
800 message("Using administrator password: %s" % adminpass)
802 assert paths.smbconf is not None
804 # only install a new smb.conf if there isn't one there already
805 if not os.path.exists(paths.smbconf):
806 message("Setting up smb.conf")
807 if serverrole == "domain controller":
809 elif serverrole == "member":
810 smbconfsuffix = "member"
811 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
813 "HOSTNAME": hostname,
814 "DOMAIN_CONF": domain,
816 "SERVERROLE": serverrole,
817 "NETLOGONPATH": paths.netlogon,
818 "SYSVOLPATH": paths.sysvol,
820 lp.load(paths.smbconf)
822 # only install a new shares config db if there is none
823 if not os.path.exists(paths.shareconf):
824 message("Setting up share.ldb")
825 share_ldb = Ldb(paths.shareconf, session_info=session_info,
826 credentials=credentials, lp=lp)
827 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
830 message("Setting up secrets.ldb")
831 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
832 session_info=session_info,
833 credentials=credentials, lp=lp)
835 message("Setting up the registry")
836 setup_registry(paths.hklm, setup_path, session_info,
837 credentials=credentials, lp=lp)
839 message("Setting up templates db")
840 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
841 credentials=credentials, lp=lp)
843 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
844 credentials=credentials, lp=lp, schemadn=schemadn,
845 configdn=configdn, domaindn=domaindn,
846 dnsdomain=dnsdomain, netbiosname=netbiosname,
847 realm=realm, message=message, hostname=hostname,
848 rootdn=rootdn, erase=erase, domainsid=domainsid,
849 aci=aci, domainguid=domainguid, policyguid=policyguid,
850 domainname=domain, fill=samdb_fill,
851 adminpass=adminpass, krbtgtpass=krbtgtpass,
852 hostguid=hostguid, invocationid=invocationid,
853 machinepass=machinepass, dnspass=dnspass,
854 serverrole=serverrole, ldap_backend=ldap_backend,
855 ldap_backend_type=ldap_backend_type)
857 if lp.get("server role") == "domain controller":
858 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
859 "{" + policyguid + "}")
860 os.makedirs(policy_path, 0755)
861 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
862 os.makedirs(os.path.join(policy_path, "User"), 0755)
863 if not os.path.isdir(paths.netlogon):
864 os.makedirs(paths.netlogon, 0755)
865 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
866 credentials=credentials, lp=lp)
867 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
868 netbiosname=netbiosname, domainsid=domainsid,
869 keytab_path=paths.keytab, samdb_url=paths.samdb,
870 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
871 machinepass=machinepass, dnsdomain=dnsdomain)
873 if samdb_fill == FILL_FULL:
874 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
875 nobody=nobody, nogroup=nogroup, wheel=wheel,
876 users=users, backup=backup)
878 message("Setting up sam.ldb rootDSE marking as synchronized")
879 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
881 message("Setting up phpLDAPadmin configuration")
882 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
885 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
887 if lp.get("server role") == "domain controller":
888 samdb = SamDB(paths.samdb, session_info=session_info,
889 credentials=credentials, lp=lp)
891 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
892 assert isinstance(domainguid, str)
893 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
894 expression="(&(objectClass=computer)(cn=%s))" % hostname,
896 assert isinstance(hostguid, str)
898 message("Setting up DNS zone: %s" % dnsdomain)
899 create_zone_file(paths.dns, setup_path, samdb,
900 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
901 domaindn=domaindn, dnspass=dnspass, realm=realm,
902 domainguid=domainguid, hostguid=hostguid)
903 message("Please install the zone located in %s into your DNS server" % paths.dns)
908 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
909 """Create a PHP LDAP admin configuration file.
911 :param path: Path to write the configuration to.
912 :param setup_path: Function to generate setup paths.
914 setup_file(setup_path("phpldapadmin-config.php"), path,
915 {"S4_LDAPI_URI": ldapi_uri})
918 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
919 hostip, hostname, dnspass, realm, domainguid, hostguid):
920 """Write out a DNS zone file, from the info in the current database.
922 :param path: Path of the new file.
923 :param setup_path": Setup path function.
924 :param samdb: SamDB object
925 :param dnsdomain: DNS Domain name
926 :param domaindn: DN of the Domain
927 :param hostip: Local IP
928 :param hostname: Local hostname
929 :param dnspass: Password for DNS
930 :param realm: Realm name
931 :param domainguid: GUID of the domain.
932 :param hostguid: GUID of the host.
934 assert isinstance(domainguid, str)
936 setup_file(setup_path("provision.zone"), path, {
937 "DNSPASS_B64": b64encode(dnspass),
938 "HOSTNAME": hostname,
939 "DNSDOMAIN": dnsdomain,
942 "DOMAINGUID": domainguid,
943 "DATESTRING": time.strftime("%Y%m%d%H"),
944 "DEFAULTSITE": DEFAULTSITE,
945 "HOSTGUID": hostguid,
949 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
950 """Load schema for the SamDB.
952 :param samdb: Load a schema into a SamDB.
953 :param setup_path: Setup path function.
954 :param schemadn: DN of the schema
955 :param netbiosname: NetBIOS name of the host.
956 :param configdn: DN of the configuration
958 schema_data = open(setup_path("schema.ldif"), 'r').read()
959 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
960 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
961 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
962 head_data = substitute_var(head_data, {
963 "SCHEMADN": schemadn,
964 "NETBIOSNAME": netbiosname,
965 "CONFIGDN": configdn,
966 "DEFAULTSITE": DEFAULTSITE
968 samdb.attach_schema_from_ldif(head_data, schema_data)