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
67 self.ldap_basedn_ldif = None
68 self.ldap_config_basedn_ldif = None
69 self.ldap_schema_basedn_ldif = None
72 def check_install(lp, session_info, credentials):
73 """Check whether the current install seems ok.
75 :param lp: Loadparm context
76 :param session_info: Session information
77 :param credentials: Credentials
79 if lp.get("realm") == "":
80 raise Error("Realm empty")
81 ldb = Ldb(lp.get("sam database"), session_info=session_info,
82 credentials=credentials, lp=lp)
83 if len(ldb.search("(cn=Administrator)")) != 1:
84 raise "No administrator account found"
87 def findnss(nssfn, *names):
88 """Find a user or group from a list of possibilities."""
94 raise Exception("Unable to find user/group for %s" % arguments[1])
97 def open_ldb(session_info, credentials, lp, dbname):
98 """Open a LDB, thrashing it if it is corrupt.
100 :param session_info: auth session information
101 :param credentials: credentials
102 :param lp: Loadparm context
103 :param dbname: Path of the database to open.
104 :return: a Ldb object
106 assert session_info is not None
108 return Ldb(dbname, session_info=session_info, credentials=credentials,
113 return Ldb(dbname, session_info=session_info, credentials=credentials,
117 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
118 """Setup a ldb in the private dir.
120 :param ldb: LDB file to import data into
121 :param ldif_path: Path of the LDIF file to load
122 :param subst_vars: Optional variables to subsitute in LDIF.
124 assert isinstance(ldif_path, str)
126 data = open(ldif_path, 'r').read()
127 if subst_vars is not None:
128 data = substitute_var(data, subst_vars)
130 check_all_substituted(data)
135 def setup_modify_ldif(ldb, ldif_path, substvars=None):
136 """Modify a ldb in the private dir.
138 :param ldb: LDB object.
139 :param ldif_path: LDIF file path.
140 :param substvars: Optional dictionary with substitution variables.
142 data = open(ldif_path, 'r').read()
143 if substvars is not None:
144 data = substitute_var(data, substvars)
146 check_all_substituted(data)
148 ldb.modify_ldif(data)
151 def setup_ldb(ldb, ldif_path, subst_vars):
152 assert ldb is not None
153 ldb.transaction_start()
155 setup_add_ldif(ldb, ldif_path, subst_vars)
157 ldb.transaction_cancel()
159 ldb.transaction_commit()
162 def setup_file(template, fname, substvars):
163 """Setup a file in the private dir.
165 :param template: Path of the template file.
166 :param fname: Path of the file to create.
167 :param substvars: Substitution variables.
171 if os.path.exists(f):
174 data = open(template, 'r').read()
176 data = substitute_var(data, substvars)
177 check_all_substituted(data)
179 open(f, 'w').write(data)
182 def provision_paths_from_lp(lp, dnsdomain, private_dir=None):
183 """Set the default paths for provisioning.
185 :param lp: Loadparm context.
186 :param dnsdomain: DNS Domain name
188 paths = ProvisionPaths()
189 if private_dir is None:
190 private_dir = lp.get("private dir")
191 paths.keytab = "secrets.keytab"
192 paths.dns_keytab = "dns.keytab"
194 paths.keytab = os.path.join(private_dir, "secrets.keytab")
195 paths.dns_keytab = os.path.join(private_dir, "dns.keytab")
197 paths.shareconf = os.path.join(private_dir, "share.ldb")
198 paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb")
199 paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb")
200 paths.templates = os.path.join(private_dir, "templates.ldb")
201 paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
202 paths.winsdb = os.path.join(private_dir, "wins.ldb")
203 paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
204 paths.phpldapadminconfig = os.path.join(private_dir,
205 "phpldapadmin-config.php")
206 paths.hklm = "hklm.ldb"
207 paths.hkcr = "hkcr.ldb"
208 paths.hkcu = "hkcu.ldb"
209 paths.hku = "hku.ldb"
210 paths.hkpd = "hkpd.ldb"
211 paths.hkpt = "hkpt.ldb"
213 paths.sysvol = lp.get("sysvol", "path")
214 if paths.sysvol is None:
215 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
217 paths.netlogon = lp.get("netlogon", "path")
218 if paths.netlogon is None:
219 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
224 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
226 """setup reasonable name mappings for sam names to unix names.
228 :param ldb: SamDB object.
229 :param sid: The domain sid.
230 :param domaindn: The domain DN.
231 :param root: Name of the UNIX root user.
232 :param nobody: Name of the UNIX nobody user.
233 :param nogroup: Name of the unix nobody group.
234 :param users: Name of the unix users group.
235 :param wheel: Name of the wheel group (users that can become root).
236 :param backup: Name of the backup group."""
237 # add some foreign sids if they are not present already
238 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
239 ldb.add_foreign(domaindn, "S-1-1-0", "World")
240 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
241 ldb.add_foreign(domaindn, "S-1-5-18", "System")
242 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
244 # some well known sids
245 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
246 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
247 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
248 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
249 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
250 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
251 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
252 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
253 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
255 # and some well known domain rids
256 ldb.setup_name_mapping(domaindn, sid + "-500", root)
257 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
258 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
259 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
260 ldb.setup_name_mapping(domaindn, sid + "-513", users)
261 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
264 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
265 credentials, configdn, schemadn, domaindn,
266 hostname, netbiosname, dnsdomain, realm,
267 rootdn, serverrole, ldap_backend=None,
268 ldap_backend_type=None, erase=False):
269 """Setup the partitions for the SAM database.
271 Alternatively, provision() may call this, and then populate the database.
273 :param erase: Remove the existing data present in the database.
276 :note: This will wipe the Sam Database!
278 :note: This function always removes the local SAM LDB file. The erase
279 parameter controls whether to erase the existing data, which
280 may not be stored locally but in LDAP.
282 assert session_info is not None
284 if os.path.exists(samdb_path):
285 os.unlink(samdb_path)
287 # Also wipes the database
288 samdb = SamDB(samdb_path, session_info=session_info,
289 credentials=credentials, lp=lp)
291 #Add modules to the list to activate them by default
292 #beware often order is important
294 # Some Known ordering constraints:
295 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
296 # - objectclass must be before password_hash, because password_hash checks
297 # that the objectclass is of type person (filled in by objectclass
298 # module when expanding the objectclass list)
299 # - partition must be last
300 # - each partition has its own module list then
301 modules_list = ["rootdse",
317 modules_list2 = ["show_deleted",
320 domaindn_ldb = "users.ldb"
321 if ldap_backend is not None:
322 domaindn_ldb = ldap_backend
323 configdn_ldb = "configuration.ldb"
324 if ldap_backend is not None:
325 configdn_ldb = ldap_backend
326 schemadn_ldb = "schema.ldb"
327 if ldap_backend is not None:
328 schemadn_ldb = ldap_backend
330 if ldap_backend_type == "fedora-ds":
331 backend_modules = ["nsuniqueid","paged_searches"]
332 elif ldap_backend_type == "openldap":
333 backend_modules = ["normalise","entryuuid","paged_searches"]
334 elif serverrole == "domain controller":
335 backend_modules = ["repl_meta_data"]
337 backend_modules = ["objectguid"]
339 samdb.transaction_start()
341 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
342 "SCHEMADN": schemadn,
343 "SCHEMADN_LDB": schemadn_ldb,
344 "SCHEMADN_MOD2": ",objectguid",
345 "CONFIGDN": configdn,
346 "CONFIGDN_LDB": configdn_ldb,
347 "DOMAINDN": domaindn,
348 "DOMAINDN_LDB": domaindn_ldb,
349 "SCHEMADN_MOD": "schema_fsmo,instancetype",
350 "CONFIGDN_MOD": "naming_fsmo,instancetype",
351 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
352 "MODULES_LIST": ",".join(modules_list),
353 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
354 "MODULES_LIST2": ",".join(modules_list2),
355 "BACKEND_MOD": ",".join(backend_modules),
359 samdb.transaction_cancel()
362 samdb.transaction_commit()
364 samdb = SamDB(samdb_path, session_info=session_info,
365 credentials=credentials, lp=lp)
367 samdb.transaction_start()
369 message("Setting up sam.ldb attributes")
370 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
372 message("Setting up sam.ldb rootDSE")
373 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
374 dnsdomain, realm, rootdn, configdn, netbiosname)
377 message("Erasing data from partitions")
378 samdb.erase_partitions()
381 samdb.transaction_cancel()
384 samdb.transaction_commit()
389 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
390 netbiosname, domainsid, keytab_path, samdb_url,
391 dns_keytab_path, dnspass, machinepass):
392 """Add DC-specific bits to a secrets database.
394 :param secretsdb: Ldb Handle to the secrets database
395 :param setup_path: Setup path function
396 :param machinepass: Machine password
398 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
399 "MACHINEPASS_B64": b64encode(machinepass),
402 "DNSDOMAIN": dnsdomain,
403 "DOMAINSID": str(domainsid),
404 "SECRETS_KEYTAB": keytab_path,
405 "NETBIOSNAME": netbiosname,
406 "SAM_LDB": samdb_url,
407 "DNS_KEYTAB": dns_keytab_path,
408 "DNSPASS_B64": b64encode(dnspass),
412 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
413 """Setup the secrets database.
415 :param path: Path to the secrets database.
416 :param setup_path: Get the path to a setup file.
417 :param session_info: Session info.
418 :param credentials: Credentials
419 :param lp: Loadparm context
420 :return: LDB handle for the created secrets database
422 if os.path.exists(path):
424 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
427 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
428 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
430 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
434 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
435 """Setup the templates database.
437 :param path: Path to the database.
438 :param setup_path: Function for obtaining the path to setup files.
439 :param session_info: Session info
440 :param credentials: Credentials
441 :param lp: Loadparm context
443 templates_ldb = SamDB(path, session_info=session_info,
444 credentials=credentials, lp=lp)
445 templates_ldb.erase()
446 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
449 def setup_registry(path, setup_path, session_info, credentials, lp):
450 """Setup the registry.
452 :param path: Path to the registry database
453 :param setup_path: Function that returns the path to a setup.
454 :param session_info: Session information
455 :param credentials: Credentials
456 :param lp: Loadparm context
458 reg = registry.Registry()
460 hive = registry.open_ldb(path, session_info=session_info,
461 credentials=credentials, lp_ctx=lp)
462 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
463 provision_reg = setup_path("provision.reg")
464 assert os.path.exists(provision_reg)
465 reg.diff_apply(provision_reg)
468 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
469 dnsdomain, realm, rootdn, configdn, netbiosname):
470 """Setup the SamDB rootdse.
472 :param samdb: Sam Database handle
473 :param setup_path: Obtain setup path
476 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
477 "SCHEMADN": schemadn,
478 "NETBIOSNAME": netbiosname,
479 "DNSDOMAIN": dnsdomain,
480 "DEFAULTSITE": DEFAULTSITE,
482 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
483 "DOMAINDN": domaindn,
485 "CONFIGDN": configdn,
486 "VERSION": samba.version(),
490 def setup_self_join(samdb, configdn, schemadn, domaindn,
491 netbiosname, hostname, dnsdomain, machinepass, dnspass,
492 realm, domainname, domainsid, invocationid, setup_path,
493 policyguid, hostguid=None):
494 """Join a host to its own domain."""
495 if hostguid is not None:
496 hostguid_add = "objectGUID: %s" % hostguid
500 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
501 "CONFIGDN": configdn,
502 "SCHEMADN": schemadn,
503 "DOMAINDN": domaindn,
504 "INVOCATIONID": invocationid,
505 "NETBIOSNAME": netbiosname,
506 "DEFAULTSITE": DEFAULTSITE,
507 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
508 "MACHINEPASS_B64": b64encode(machinepass),
509 "DNSPASS_B64": b64encode(dnspass),
511 "DOMAIN": domainname,
512 "HOSTGUID_ADD": hostguid_add,
513 "DNSDOMAIN": dnsdomain})
514 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
515 "POLICYGUID": policyguid,
516 "DNSDOMAIN": dnsdomain,
517 "DOMAINSID": str(domainsid),
518 "DOMAINDN": domaindn})
521 def setup_samdb(path, setup_path, session_info, credentials, lp,
522 schemadn, configdn, domaindn, dnsdomain, realm,
523 netbiosname, message, hostname, rootdn, erase,
524 domainsid, aci, domainguid, policyguid,
525 domainname, fill, adminpass, krbtgtpass,
526 machinepass, hostguid, invocationid, dnspass,
527 serverrole, ldap_backend=None, ldap_backend_type=None):
528 """Setup a complete SAM Database.
532 # Also wipes the database
533 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
534 domaindn=domaindn, message=message, lp=lp,
535 credentials=credentials, session_info=session_info,
536 hostname=hostname, netbiosname=netbiosname,
537 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
538 ldap_backend=ldap_backend, serverrole=serverrole,
539 ldap_backend_type=ldap_backend_type, erase=erase)
541 samdb = SamDB(path, session_info=session_info,
542 credentials=credentials, lp=lp)
545 # We want to finish here, but setup the index before we do so
546 message("Setting up sam.ldb index")
547 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
550 message("Pre-loading the Samba 4 and AD schema")
551 samdb = SamDB(path, session_info=session_info,
552 credentials=credentials, lp=lp)
553 samdb.set_domain_sid(domainsid)
554 if lp.get("server role") == "domain controller":
555 samdb.set_invocation_id(invocationid)
557 load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
559 samdb.transaction_start()
562 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
563 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
564 "DOMAINDN": domaindn,
568 message("Modifying DomainDN: " + domaindn + "")
569 if domainguid is not None:
570 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
574 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
575 "LDAPTIME": timestring(int(time.time())),
576 "DOMAINSID": str(domainsid),
577 "SCHEMADN": schemadn,
578 "NETBIOSNAME": netbiosname,
579 "DEFAULTSITE": DEFAULTSITE,
580 "CONFIGDN": configdn,
581 "POLICYGUID": policyguid,
582 "DOMAINDN": domaindn,
583 "DOMAINGUID_MOD": domainguid_mod,
586 message("Adding configuration container (permitted to fail)")
587 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
588 "CONFIGDN": configdn,
590 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
592 message("Modifying configuration container")
593 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
594 "CONFIGDN": configdn,
595 "SCHEMADN": schemadn,
598 message("Adding schema container (permitted to fail)")
599 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
600 "SCHEMADN": schemadn,
602 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
604 message("Modifying schema container")
605 setup_modify_ldif(samdb,
606 setup_path("provision_schema_basedn_modify.ldif"), {
607 "SCHEMADN": schemadn,
608 "NETBIOSNAME": netbiosname,
609 "DEFAULTSITE": DEFAULTSITE,
610 "CONFIGDN": configdn,
613 message("Setting up sam.ldb Samba4 schema")
614 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
615 {"SCHEMADN": schemadn })
616 message("Setting up sam.ldb AD schema")
617 setup_add_ldif(samdb, setup_path("schema.ldif"),
618 {"SCHEMADN": schemadn})
620 message("Setting up sam.ldb configuration data")
621 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
622 "CONFIGDN": configdn,
623 "NETBIOSNAME": netbiosname,
624 "DEFAULTSITE": DEFAULTSITE,
625 "DNSDOMAIN": dnsdomain,
626 "DOMAIN": domainname,
627 "SCHEMADN": schemadn,
628 "DOMAINDN": domaindn,
631 message("Setting up display specifiers")
632 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
633 {"CONFIGDN": configdn})
635 message("Adding users container (permitted to fail)")
636 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
637 "DOMAINDN": domaindn})
638 message("Modifying users container")
639 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
640 "DOMAINDN": domaindn})
641 message("Adding computers container (permitted to fail)")
642 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
643 "DOMAINDN": domaindn})
644 message("Modifying computers container")
645 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
646 "DOMAINDN": domaindn})
647 message("Setting up sam.ldb data")
648 setup_add_ldif(samdb, setup_path("provision.ldif"), {
649 "DOMAINDN": domaindn,
650 "NETBIOSNAME": netbiosname,
651 "DEFAULTSITE": DEFAULTSITE,
652 "CONFIGDN": configdn,
655 if fill == FILL_FULL:
656 message("Setting up sam.ldb users and groups")
657 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
658 "DOMAINDN": domaindn,
659 "DOMAINSID": str(domainsid),
660 "CONFIGDN": configdn,
661 "ADMINPASS_B64": b64encode(adminpass),
662 "KRBTGTPASS_B64": b64encode(krbtgtpass),
665 if lp.get("server role") == "domain controller":
666 message("Setting up self join")
667 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
668 domaindn=domaindn, invocationid=invocationid,
669 dnspass=dnspass, netbiosname=netbiosname,
670 dnsdomain=dnsdomain, realm=realm,
671 machinepass=machinepass, domainname=domainname,
672 domainsid=domainsid, policyguid=policyguid,
673 hostname=hostname, hostguid=hostguid,
674 setup_path=setup_path)
676 #We want to setup the index last, as adds are faster unindexed
677 message("Setting up sam.ldb index")
678 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
680 samdb.transaction_cancel()
683 samdb.transaction_commit()
687 FILL_NT4SYNC = "NT4SYNC"
690 def provision(lp, setup_dir, message, paths, session_info,
691 credentials, samdb_fill=FILL_FULL, realm=None, rootdn=None,
692 domain=None, hostname=None, hostip=None, domainsid=None,
693 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
694 policyguid=None, invocationid=None, machinepass=None,
695 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
696 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
697 ldap_backend=None, ldap_backend_type=None):
700 :note: caution, this wipes all existing data!
703 def setup_path(file):
704 return os.path.join(setup_dir, file)
706 if domainsid is None:
707 domainsid = security.random_sid()
708 if policyguid is None:
709 policyguid = uuid.random()
710 if adminpass is None:
711 adminpass = misc.random_password(12)
712 if krbtgtpass is None:
713 krbtgtpass = misc.random_password(12)
714 if machinepass is None:
715 machinepass = misc.random_password(12)
717 dnspass = misc.random_password(12)
719 root = findnss(pwd.getpwnam, "root")[0]
721 nobody = findnss(pwd.getpwnam, "nobody")[0]
723 nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[0]
725 users = findnss(grp.getgrnam, "users", "guest", "other", "unknown",
728 wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[0]
730 backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[0]
732 aci = "# no aci for local ldb"
733 if serverrole is None:
734 serverrole = lp.get("server role")
735 if invocationid is None and serverrole == "domain controller":
736 invocationid = uuid.random()
739 realm = lp.get("realm")
741 if lp.get("realm").upper() != realm.upper():
742 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
743 (lp.get("realm"), realm))
745 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
747 if ldap_backend == "ldapi":
748 # provision-backend will set this path suggested slapd command line / fedorads.inf
749 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), safe="")
751 assert realm is not None
752 realm = realm.upper()
755 hostname = gethostname().split(".")[0].lower()
758 hostip = gethostbyname(hostname)
760 netbiosname = hostname.upper()
761 if not valid_netbios_name(netbiosname):
762 raise InvalidNetbiosName(netbiosname)
764 dnsdomain = realm.lower()
765 if serverrole == "domain controller":
766 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
768 domain = lp.get("workgroup")
770 if lp.get("workgroup").upper() != domain.upper():
771 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
772 lp.get("workgroup"), domain)
774 assert domain is not None
775 domain = domain.upper()
776 if not valid_netbios_name(domain):
777 raise InvalidNetbiosName(domain)
780 domaindn = "CN=" + netbiosname
786 configdn = "CN=Configuration," + rootdn
787 schemadn = "CN=Schema," + configdn
789 message("set DOMAIN SID: %s" % str(domainsid))
790 message("Provisioning for %s in realm %s" % (domain, realm))
791 message("Using administrator password: %s" % adminpass)
793 assert paths.smbconf is not None
795 # only install a new smb.conf if there isn't one there already
796 if not os.path.exists(paths.smbconf):
797 message("Setting up smb.conf")
798 if serverrole == "domain controller":
800 elif serverrole == "member":
801 smbconfsuffix = "member"
803 assert "Invalid server role setting: %s" % serverrole
804 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
806 "HOSTNAME": hostname,
807 "DOMAIN_CONF": domain,
809 "SERVERROLE": serverrole,
810 "NETLOGONPATH": paths.netlogon,
811 "SYSVOLPATH": paths.sysvol,
815 # only install a new shares config db if there is none
816 if not os.path.exists(paths.shareconf):
817 message("Setting up share.ldb")
818 share_ldb = Ldb(paths.shareconf, session_info=session_info,
819 credentials=credentials, lp=lp)
820 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
823 message("Setting up secrets.ldb")
824 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
825 session_info=session_info,
826 credentials=credentials, lp=lp)
828 message("Setting up the registry")
829 setup_registry(paths.hklm, setup_path, session_info,
830 credentials=credentials, lp=lp)
832 message("Setting up templates db")
833 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
834 credentials=credentials, lp=lp)
836 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
837 credentials=credentials, lp=lp, schemadn=schemadn,
838 configdn=configdn, domaindn=domaindn,
839 dnsdomain=dnsdomain, netbiosname=netbiosname,
840 realm=realm, message=message, hostname=hostname,
841 rootdn=rootdn, erase=erase, domainsid=domainsid,
842 aci=aci, domainguid=domainguid, policyguid=policyguid,
843 domainname=domain, fill=samdb_fill,
844 adminpass=adminpass, krbtgtpass=krbtgtpass,
845 hostguid=hostguid, invocationid=invocationid,
846 machinepass=machinepass, dnspass=dnspass,
847 serverrole=serverrole, ldap_backend=ldap_backend,
848 ldap_backend_type=ldap_backend_type)
850 if lp.get("server role") == "domain controller":
851 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
852 "{" + policyguid + "}")
853 os.makedirs(policy_path, 0755)
854 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
855 os.makedirs(os.path.join(policy_path, "User"), 0755)
856 if not os.path.isdir(paths.netlogon):
857 os.makedirs(paths.netlogon, 0755)
858 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
859 credentials=credentials, lp=lp)
860 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
861 netbiosname=netbiosname, domainsid=domainsid,
862 keytab_path=paths.keytab, samdb_url=paths.samdb,
863 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
864 machinepass=machinepass, dnsdomain=dnsdomain)
866 if samdb_fill == FILL_FULL:
867 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
868 nobody=nobody, nogroup=nogroup, wheel=wheel,
869 users=users, backup=backup)
871 message("Setting up sam.ldb rootDSE marking as synchronized")
872 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
874 message("Setting up phpLDAPadmin configuration")
875 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
878 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
880 if lp.get("server role") == "domain controller":
881 samdb = SamDB(paths.samdb, session_info=session_info,
882 credentials=credentials, lp=lp)
884 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
885 assert isinstance(domainguid, str)
886 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
887 expression="(&(objectClass=computer)(cn=%s))" % hostname,
889 assert isinstance(hostguid, str)
891 message("Setting up DNS zone: %s" % dnsdomain)
892 create_zone_file(paths.dns, setup_path, samdb,
893 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
894 domaindn=domaindn, dnspass=dnspass, realm=realm,
895 domainguid=domainguid, hostguid=hostguid)
896 message("Please install the zone located in %s into your DNS server" % paths.dns)
901 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
902 """Create a PHP LDAP admin configuration file.
904 :param path: Path to write the configuration to.
905 :param setup_path: Function to generate setup paths.
907 setup_file(setup_path("phpldapadmin-config.php"), path,
908 {"S4_LDAPI_URI": ldapi_uri})
911 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
912 hostip, hostname, dnspass, realm, domainguid, hostguid):
913 """Write out a DNS zone file, from the info in the current database.
915 :param path: Path of the new file.
916 :param setup_path": Setup path function.
917 :param samdb: SamDB object
918 :param dnsdomain: DNS Domain name
919 :param domaindn: DN of the Domain
920 :param hostip: Local IP
921 :param hostname: Local hostname
922 :param dnspass: Password for DNS
923 :param realm: Realm name
924 :param domainguid: GUID of the domain.
925 :param hostguid: GUID of the host.
927 assert isinstance(domainguid, str)
929 setup_file(setup_path("provision.zone"), path, {
930 "DNSPASS_B64": b64encode(dnspass),
931 "HOSTNAME": hostname,
932 "DNSDOMAIN": dnsdomain,
935 "DOMAINGUID": domainguid,
936 "DATESTRING": time.strftime("%Y%m%d%H"),
937 "DEFAULTSITE": DEFAULTSITE,
938 "HOSTGUID": hostguid,
942 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
945 :param samdb: Load a schema into a SamDB.
946 :param setup_path: Setup path function.
947 :param schemadn: DN of the schema
948 :param netbiosname: NetBIOS name of the host.
949 :param configdn: DN of the configuration
951 schema_data = open(setup_path("schema.ldif"), 'r').read()
952 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
953 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
954 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
955 head_data = substitute_var(head_data, {
956 "SCHEMADN": schemadn,
957 "NETBIOSNAME": netbiosname,
958 "CONFIGDN": configdn,
959 "DEFAULTSITE": DEFAULTSITE
961 samdb.attach_schema_from_ldif(head_data, schema_data)