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.shareconf = os.path.join(private_dir, "share.ldb")
192 paths.samdb = os.path.join(private_dir, lp.get("sam database") or "samdb.ldb")
193 paths.secrets = os.path.join(private_dir, lp.get("secrets database") or "secrets.ldb")
194 paths.templates = os.path.join(private_dir, "templates.ldb")
195 paths.keytab = os.path.join(private_dir, "secrets.keytab")
196 paths.dns_keytab = os.path.join(private_dir, "dns.keytab")
197 paths.dns = os.path.join(private_dir, dnsdomain + ".zone")
198 paths.winsdb = os.path.join(private_dir, "wins.ldb")
199 paths.s4_ldapi_path = os.path.join(private_dir, "ldapi")
200 paths.phpldapadminconfig = os.path.join(private_dir,
201 "phpldapadmin-config.php")
202 paths.hklm = os.path.join(private_dir, "hklm.ldb")
203 paths.sysvol = lp.get("sysvol", "path")
204 if paths.sysvol is None:
205 paths.sysvol = os.path.join(lp.get("lock dir"), "sysvol")
207 paths.netlogon = lp.get("netlogon", "path")
208 if paths.netlogon is None:
209 paths.netlogon = os.path.join(os.path.join(paths.sysvol, "scripts"))
214 def setup_name_mappings(ldb, sid, domaindn, root, nobody, nogroup, users,
216 """setup reasonable name mappings for sam names to unix names.
218 :param ldb: SamDB object.
219 :param sid: The domain sid.
220 :param domaindn: The domain DN.
221 :param root: Name of the UNIX root user.
222 :param nobody: Name of the UNIX nobody user.
223 :param nogroup: Name of the unix nobody group.
224 :param users: Name of the unix users group.
225 :param wheel: Name of the wheel group (users that can become root).
226 :param backup: Name of the backup group."""
227 # add some foreign sids if they are not present already
228 ldb.add_foreign(domaindn, "S-1-5-7", "Anonymous")
229 ldb.add_foreign(domaindn, "S-1-1-0", "World")
230 ldb.add_foreign(domaindn, "S-1-5-2", "Network")
231 ldb.add_foreign(domaindn, "S-1-5-18", "System")
232 ldb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
234 # some well known sids
235 ldb.setup_name_mapping(domaindn, "S-1-5-7", nobody)
236 ldb.setup_name_mapping(domaindn, "S-1-1-0", nogroup)
237 ldb.setup_name_mapping(domaindn, "S-1-5-2", nogroup)
238 ldb.setup_name_mapping(domaindn, "S-1-5-18", root)
239 ldb.setup_name_mapping(domaindn, "S-1-5-11", users)
240 ldb.setup_name_mapping(domaindn, "S-1-5-32-544", wheel)
241 ldb.setup_name_mapping(domaindn, "S-1-5-32-545", users)
242 ldb.setup_name_mapping(domaindn, "S-1-5-32-546", nogroup)
243 ldb.setup_name_mapping(domaindn, "S-1-5-32-551", backup)
245 # and some well known domain rids
246 ldb.setup_name_mapping(domaindn, sid + "-500", root)
247 ldb.setup_name_mapping(domaindn, sid + "-518", wheel)
248 ldb.setup_name_mapping(domaindn, sid + "-519", wheel)
249 ldb.setup_name_mapping(domaindn, sid + "-512", wheel)
250 ldb.setup_name_mapping(domaindn, sid + "-513", users)
251 ldb.setup_name_mapping(domaindn, sid + "-520", wheel)
254 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
255 credentials, configdn, schemadn, domaindn,
256 hostname, netbiosname, dnsdomain, realm,
257 rootdn, serverrole, ldap_backend=None,
258 ldap_backend_type=None, erase=False):
259 """Setup the partitions for the SAM database.
261 Alternatively, provision() may call this, and then populate the database.
263 :param erase: Remove the existing data present in the database.
266 :note: This will wipe the Sam Database!
268 :note: This function always removes the local SAM LDB file. The erase
269 parameter controls whether to erase the existing data, which
270 may not be stored locally but in LDAP.
272 assert session_info is not None
274 if os.path.exists(samdb_path):
275 os.unlink(samdb_path)
277 # Also wipes the database
278 samdb = SamDB(samdb_path, session_info=session_info,
279 credentials=credentials, lp=lp)
281 #Add modules to the list to activate them by default
282 #beware often order is important
284 # Some Known ordering constraints:
285 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
286 # - objectclass must be before password_hash, because password_hash checks
287 # that the objectclass is of type person (filled in by objectclass
288 # module when expanding the objectclass list)
289 # - partition must be last
290 # - each partition has its own module list then
291 modules_list = ["rootdse",
307 modules_list2 = ["show_deleted",
310 domaindn_ldb = "users.ldb"
311 if ldap_backend is not None:
312 domaindn_ldb = ldap_backend
313 configdn_ldb = "configuration.ldb"
314 if ldap_backend is not None:
315 configdn_ldb = ldap_backend
316 schema_ldb = "schema.ldb"
317 if ldap_backend is not None:
318 schema_ldb = ldap_backend
320 if ldap_backend_type == "fedora-ds":
321 backend_modules = ["nsuniqueid","paged_searches"]
322 elif ldap_backend_type == "openldap":
323 backend_modules = ["normalise","entryuuid","paged_searches"]
324 elif serverrole == "domain controller":
325 backend_modules = ["repl_meta_data"]
327 backend_modules = ["objectguid"]
329 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
330 "SCHEMADN": schemadn,
331 "SCHEMADN_LDB": "schema.ldb",
332 "SCHEMADN_MOD2": ",objectguid",
333 "CONFIGDN": configdn,
334 "CONFIGDN_LDB": "configuration.ldb",
335 "DOMAINDN": domaindn,
336 "DOMAINDN_LDB": "users.ldb",
337 "SCHEMADN_MOD": "schema_fsmo,instancetype",
338 "CONFIGDN_MOD": "naming_fsmo,instancetype",
339 "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
340 "MODULES_LIST": ",".join(modules_list),
341 "TDB_MODULES_LIST": ","+",".join(tdb_modules_list),
342 "MODULES_LIST2": ",".join(modules_list2),
343 "BACKEND_MOD": ",".join(backend_modules),
346 samdb = SamDB(samdb_path, session_info=session_info,
347 credentials=credentials, lp=lp)
349 samdb.transaction_start()
351 message("Setting up sam.ldb attributes")
352 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
354 message("Setting up sam.ldb rootDSE")
355 setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
356 dnsdomain, realm, rootdn, configdn, netbiosname)
359 message("Erasing data from partitions")
360 samdb.erase_partitions()
363 samdb.transaction_cancel()
366 samdb.transaction_commit()
371 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
372 netbiosname, domainsid, keytab_path, samdb_url,
373 dns_keytab_path, dnspass, machinepass):
374 """Add DC-specific bits to a secrets database.
376 :param secretsdb: Ldb Handle to the secrets database
377 :param setup_path: Setup path function
378 :param machinepass: Machine password
380 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
381 "MACHINEPASS_B64": b64encode(machinepass),
384 "DNSDOMAIN": dnsdomain,
385 "DOMAINSID": str(domainsid),
386 "SECRETS_KEYTAB": keytab_path,
387 "NETBIOSNAME": netbiosname,
388 "SAM_LDB": samdb_url,
389 "DNS_KEYTAB": dns_keytab_path,
390 "DNSPASS_B64": b64encode(dnspass),
394 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
395 """Setup the secrets database.
397 :param path: Path to the secrets database.
398 :param setup_path: Get the path to a setup file.
399 :param session_info: Session info.
400 :param credentials: Credentials
401 :param lp: Loadparm context
402 :return: LDB handle for the created secrets database
404 if os.path.exists(path):
406 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
409 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
410 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
414 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
415 """Setup the templates database.
417 :param path: Path to the database.
418 :param setup_path: Function for obtaining the path to setup files.
419 :param session_info: Session info
420 :param credentials: Credentials
421 :param lp: Loadparm context
423 templates_ldb = SamDB(path, session_info=session_info,
424 credentials=credentials, lp=lp)
425 templates_ldb.erase()
426 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
429 def setup_registry(path, setup_path, session_info, credentials, lp):
430 """Setup the registry.
432 :param path: Path to the registry database
433 :param setup_path: Function that returns the path to a setup.
434 :param session_info: Session information
435 :param credentials: Credentials
436 :param lp: Loadparm context
438 reg = registry.Registry()
439 hive = registry.open_ldb(path, session_info=session_info,
440 credentials=credentials, lp_ctx=lp)
441 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
442 provision_reg = setup_path("provision.reg")
443 assert os.path.exists(provision_reg)
444 reg.diff_apply(provision_reg)
447 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
448 dnsdomain, realm, rootdn, configdn, netbiosname):
449 """Setup the SamDB rootdse.
451 :param samdb: Sam Database handle
452 :param setup_path: Obtain setup path
455 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
456 "SCHEMADN": schemadn,
457 "NETBIOSNAME": netbiosname,
458 "DNSDOMAIN": dnsdomain,
459 "DEFAULTSITE": DEFAULTSITE,
461 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
462 "DOMAINDN": domaindn,
464 "CONFIGDN": configdn,
465 "VERSION": samba.version(),
469 def setup_self_join(samdb, configdn, schemadn, domaindn,
470 netbiosname, hostname, dnsdomain, machinepass, dnspass,
471 realm, domainname, domainsid, invocationid, setup_path,
472 policyguid, hostguid=None):
473 """Join a host to its own domain."""
474 if hostguid is not None:
475 hostguid_add = "objectGUID: %s" % hostguid
479 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
480 "CONFIGDN": configdn,
481 "SCHEMADN": schemadn,
482 "DOMAINDN": domaindn,
483 "INVOCATIONID": invocationid,
484 "NETBIOSNAME": netbiosname,
485 "DEFAULTSITE": DEFAULTSITE,
486 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
487 "MACHINEPASS_B64": b64encode(machinepass),
488 "DNSPASS_B64": b64encode(dnspass),
490 "DOMAIN": domainname,
491 "HOSTGUID_ADD": hostguid_add,
492 "DNSDOMAIN": dnsdomain})
493 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
494 "POLICYGUID": policyguid,
495 "DNSDOMAIN": dnsdomain,
496 "DOMAINSID": str(domainsid),
497 "DOMAINDN": domaindn})
500 def setup_samdb(path, setup_path, session_info, credentials, lp,
501 schemadn, configdn, domaindn, dnsdomain, realm,
502 netbiosname, message, hostname, rootdn, erase,
503 domainsid, aci, domainguid, policyguid,
504 domainname, fill, adminpass, krbtgtpass,
505 machinepass, hostguid, invocationid, dnspass,
506 serverrole, ldap_backend=None, ldap_backend_type=None):
507 """Setup a complete SAM Database.
511 # Also wipes the database
512 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
513 domaindn=domaindn, message=message, lp=lp,
514 credentials=credentials, session_info=session_info,
515 hostname=hostname, netbiosname=netbiosname,
516 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
517 ldap_backend=ldap_backend, serverrole=serverrole,
518 ldap_backend_type=ldap_backend_type, erase=erase)
520 samdb = SamDB(path, session_info=session_info,
521 credentials=credentials, lp=lp)
524 # We want to finish here, but setup the index before we do so
525 message("Setting up sam.ldb index")
526 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
529 message("Pre-loading the Samba 4 and AD schema")
530 samdb = SamDB(path, session_info=session_info,
531 credentials=credentials, lp=lp)
532 samdb.set_domain_sid(domainsid)
533 if lp.get("server role") == "domain controller":
534 samdb.set_invocation_id(invocationid)
536 load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
538 samdb.transaction_start()
541 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
542 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
543 "DOMAINDN": domaindn,
547 message("Modifying DomainDN: " + domaindn + "")
548 if domainguid is not None:
549 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
553 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
554 "LDAPTIME": timestring(int(time.time())),
555 "DOMAINSID": str(domainsid),
556 "SCHEMADN": schemadn,
557 "NETBIOSNAME": netbiosname,
558 "DEFAULTSITE": DEFAULTSITE,
559 "CONFIGDN": configdn,
560 "POLICYGUID": policyguid,
561 "DOMAINDN": domaindn,
562 "DOMAINGUID_MOD": domainguid_mod,
565 message("Adding configuration container (permitted to fail)")
566 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
567 "CONFIGDN": configdn,
569 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
571 message("Modifying configuration container")
572 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
573 "CONFIGDN": configdn,
574 "SCHEMADN": schemadn,
577 message("Adding schema container (permitted to fail)")
578 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
579 "SCHEMADN": schemadn,
581 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
583 message("Modifying schema container")
584 setup_modify_ldif(samdb,
585 setup_path("provision_schema_basedn_modify.ldif"), {
586 "SCHEMADN": schemadn,
587 "NETBIOSNAME": netbiosname,
588 "DEFAULTSITE": DEFAULTSITE,
589 "CONFIGDN": configdn,
592 message("Setting up sam.ldb Samba4 schema")
593 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
594 {"SCHEMADN": schemadn })
595 message("Setting up sam.ldb AD schema")
596 setup_add_ldif(samdb, setup_path("schema.ldif"),
597 {"SCHEMADN": schemadn})
599 message("Setting up sam.ldb configuration data")
600 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
601 "CONFIGDN": configdn,
602 "NETBIOSNAME": netbiosname,
603 "DEFAULTSITE": DEFAULTSITE,
604 "DNSDOMAIN": dnsdomain,
605 "DOMAIN": domainname,
606 "SCHEMADN": schemadn,
607 "DOMAINDN": domaindn,
610 message("Setting up display specifiers")
611 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
612 {"CONFIGDN": configdn})
614 message("Adding users container (permitted to fail)")
615 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
616 "DOMAINDN": domaindn})
617 message("Modifying users container")
618 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
619 "DOMAINDN": domaindn})
620 message("Adding computers container (permitted to fail)")
621 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
622 "DOMAINDN": domaindn})
623 message("Modifying computers container")
624 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
625 "DOMAINDN": domaindn})
626 message("Setting up sam.ldb data")
627 setup_add_ldif(samdb, setup_path("provision.ldif"), {
628 "DOMAINDN": domaindn,
629 "NETBIOSNAME": netbiosname,
630 "DEFAULTSITE": DEFAULTSITE,
631 "CONFIGDN": configdn,
634 if fill == FILL_FULL:
635 message("Setting up sam.ldb users and groups")
636 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
637 "DOMAINDN": domaindn,
638 "DOMAINSID": str(domainsid),
639 "CONFIGDN": configdn,
640 "ADMINPASS_B64": b64encode(adminpass),
641 "KRBTGTPASS_B64": b64encode(krbtgtpass),
644 if lp.get("server role") == "domain controller":
645 message("Setting up self join")
646 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
647 domaindn=domaindn, invocationid=invocationid,
648 dnspass=dnspass, netbiosname=netbiosname,
649 dnsdomain=dnsdomain, realm=realm,
650 machinepass=machinepass, domainname=domainname,
651 domainsid=domainsid, policyguid=policyguid,
652 hostname=hostname, hostguid=hostguid,
653 setup_path=setup_path)
655 #We want to setup the index last, as adds are faster unindexed
656 message("Setting up sam.ldb index")
657 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
659 samdb.transaction_cancel()
662 samdb.transaction_commit()
666 FILL_NT4SYNC = "NT4SYNC"
669 def provision(lp, setup_dir, message, paths, session_info,
670 credentials, ldapbackend, samdb_fill=FILL_FULL, realm=None, rootdn=None,
671 domain=None, hostname=None, hostip=None, domainsid=None,
672 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
673 policyguid=None, invocationid=None, machinepass=None,
674 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
675 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
676 ldap_backend=None, ldap_backend_type=None):
679 :note: caution, this wipes all existing data!
682 def setup_path(file):
683 return os.path.join(setup_dir, file)
685 if domainsid is None:
686 domainsid = security.random_sid()
687 if policyguid is None:
688 policyguid = uuid.random()
689 if adminpass is None:
690 adminpass = misc.random_password(12)
691 if krbtgtpass is None:
692 krbtgtpass = misc.random_password(12)
693 if machinepass is None:
694 machinepass = misc.random_password(12)
696 dnspass = misc.random_password(12)
698 root = findnss(pwd.getpwnam, "root")[4]
700 nobody = findnss(pwd.getpwnam, "nobody")[4]
702 nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[2]
704 users = findnss(grp.getgrnam, "users", "guest", "other", "unknown",
707 wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[2]
709 backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[2]
711 aci = "# no aci for local ldb"
712 if serverrole is None:
713 serverrole = lp.get("server role")
714 if invocationid is None and serverrole == "domain controller":
715 invocationid = uuid.random()
718 realm = lp.get("realm")
720 if lp.get("realm").upper() != realm.upper():
721 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
722 (lp.get("realm"), realm))
724 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path)
726 if ldap_backend == "ldapi":
727 # provision-backend will set this path suggested slapd command line / fedorads.inf
728 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"))
730 assert realm is not None
731 realm = realm.upper()
734 hostname = gethostname().split(".")[0].lower()
737 hostip = gethostbyname(hostname)
739 netbiosname = hostname.upper()
740 if not valid_netbios_name(netbiosname):
741 raise InvalidNetbiosName(netbiosname)
743 dnsdomain = realm.lower()
744 if serverrole == "domain controller":
745 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
747 domain = lp.get("workgroup")
749 if lp.get("workgroup").upper() != domain.upper():
750 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
751 lp.get("workgroup"), domain)
753 assert domain is not None
754 domain = domain.upper()
755 if not valid_netbios_name(domain):
756 raise InvalidNetbiosName(domain)
759 domaindn = "CN=" + netbiosname
765 configdn = "CN=Configuration," + rootdn
766 schemadn = "CN=Schema," + configdn
768 message("set DOMAIN SID: %s" % str(domainsid))
769 message("Provisioning for %s in realm %s" % (domain, realm))
770 message("Using administrator password: %s" % adminpass)
772 assert paths.smbconf is not None
774 # only install a new smb.conf if there isn't one there already
775 if not os.path.exists(paths.smbconf):
776 message("Setting up smb.conf")
777 if serverrole == "domain controller":
779 elif serverrole == "member":
780 smbconfsuffix = "member"
782 assert "Invalid server role setting: %s" % serverrole
783 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
785 "HOSTNAME": hostname,
786 "DOMAIN_CONF": domain,
788 "SERVERROLE": serverrole,
789 "NETLOGONPATH": paths.netlogon,
790 "SYSVOLPATH": paths.sysvol,
794 # only install a new shares config db if there is none
795 if not os.path.exists(paths.shareconf):
796 message("Setting up share.ldb")
797 share_ldb = Ldb(paths.shareconf, session_info=session_info,
798 credentials=credentials, lp=lp)
799 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
802 message("Setting up secrets.ldb")
803 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
804 session_info=session_info,
805 credentials=credentials, lp=lp)
807 message("Setting up the registry")
808 setup_registry(paths.hklm, setup_path, session_info,
809 credentials=credentials, lp=lp)
811 message("Setting up templates db")
812 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
813 credentials=credentials, lp=lp)
815 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
816 credentials=credentials, lp=lp, schemadn=schemadn,
817 configdn=configdn, domaindn=domaindn,
818 dnsdomain=dnsdomain, netbiosname=netbiosname,
819 realm=realm, message=message, hostname=hostname,
820 rootdn=rootdn, erase=erase, domainsid=domainsid,
821 aci=aci, domainguid=domainguid, policyguid=policyguid,
822 domainname=domain, fill=samdb_fill,
823 adminpass=adminpass, krbtgtpass=krbtgtpass,
824 hostguid=hostguid, invocationid=invocationid,
825 machinepass=machinepass, dnspass=dnspass,
826 serverrole=serverrole, ldap_backend=ldap_backend,
827 ldap_backend_type=ldap_backend_type)
829 if lp.get("server role") == "domain controller":
830 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
831 "{" + policyguid + "}")
832 os.makedirs(policy_path, 0755)
833 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
834 os.makedirs(os.path.join(policy_path, "User"), 0755)
835 if not os.path.isdir(paths.netlogon):
836 os.makedirs(paths.netlogon, 0755)
837 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
838 credentials=credentials, lp=lp)
839 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
840 netbiosname=netbiosname, domainsid=domainsid,
841 keytab_path=paths.keytab, samdb_url=paths.samdb,
842 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
843 machinepass=machinepass, dnsdomain=dnsdomain)
845 if samdb_fill == FILL_FULL:
846 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
847 nobody=nobody, nogroup=nogroup, wheel=wheel,
848 users=users, backup=backup)
850 message("Setting up sam.ldb rootDSE marking as synchronized")
851 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
853 message("Setting up phpLDAPadmin configuration")
854 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
857 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
859 if lp.get("server role") == "domain controller":
860 samdb = SamDB(paths.samdb, session_info=session_info,
861 credentials=credentials, lp=lp)
863 domainguid = samdb.searchone(domaindn, "objectGUID")
864 assert isinstance(domainguid, str)
865 hostguid = samdb.searchone(domaindn, "objectGUID",
866 expression="(&(objectClass=computer)(cn=%s))" % hostname,
868 assert isinstance(hostguid, str)
870 message("Setting up DNS zone: %s" % dnsdomain)
871 create_zone_file(paths.dns, setup_path, samdb,
872 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
873 domaindn=domaindn, dnspass=dnspass, realm=realm,
874 domainguid=domainguid, hostguid=hostguid)
875 message("Please install the zone located in %s into your DNS server" % paths.dns)
880 def create_phpldapadmin_config(path, setup_path, ldap_backend):
881 """Create a PHP LDAP admin configuration file.
883 :param path: Path to write the configuration to.
884 :param setup_path: Function to generate setup paths.
886 setup_file(setup_path("phpldapadmin-config.php"), path,
887 {"S4_LDAPI_URI": ldap_backend})
890 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
891 hostip, hostname, dnspass, realm, domainguid, hostguid):
892 """Write out a DNS zone file, from the info in the current database.
894 :param path: Path of the new file.
895 :param setup_path": Setup path function.
896 :param samdb: SamDB object
897 :param dnsdomain: DNS Domain name
898 :param domaindn: DN of the Domain
899 :param hostip: Local IP
900 :param hostname: Local hostname
901 :param dnspass: Password for DNS
902 :param realm: Realm name
903 :param domainguid: GUID of the domain.
904 :param hostguid: GUID of the host.
907 setup_file(setup_path("provision.zone"), path, {
908 "DNSPASS_B64": b64encode(dnspass),
909 "HOSTNAME": hostname,
910 "DNSDOMAIN": dnsdomain,
913 "DOMAINGUID": domainguid,
914 "DATESTRING": time.strftime("%Y%m%d%H"),
915 "DEFAULTSITE": DEFAULTSITE,
916 "HOSTGUID": hostguid,
920 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
923 :param samdb: Load a schema into a SamDB.
924 :param setup_path: Setup path function.
925 :param schemadn: DN of the schema
926 :param netbiosname: NetBIOS name of the host.
927 :param configdn: DN of the configuration
929 schema_data = open(setup_path("schema.ldif"), 'r').read()
930 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
931 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
932 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
933 head_data = substitute_var(head_data, {
934 "SCHEMADN": schemadn,
935 "NETBIOSNAME": netbiosname,
936 "CONFIGDN": configdn,
937 "DEFAULTSITE": DEFAULTSITE
939 samdb.attach_schema_from_ldif(head_data, schema_data)