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 = "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 = Ldb(path, session_info=session_info, credentials=credentials,
412 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
416 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
417 """Setup the templates database.
419 :param path: Path to the database.
420 :param setup_path: Function for obtaining the path to setup files.
421 :param session_info: Session info
422 :param credentials: Credentials
423 :param lp: Loadparm context
425 templates_ldb = SamDB(path, session_info=session_info,
426 credentials=credentials, lp=lp)
427 templates_ldb.erase()
428 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
431 def setup_registry(path, setup_path, session_info, credentials, lp):
432 """Setup the registry.
434 :param path: Path to the registry database
435 :param setup_path: Function that returns the path to a setup.
436 :param session_info: Session information
437 :param credentials: Credentials
438 :param lp: Loadparm context
440 reg = registry.Registry()
441 hive = registry.open_ldb(path, session_info=session_info,
442 credentials=credentials, lp_ctx=lp)
443 reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
444 provision_reg = setup_path("provision.reg")
445 assert os.path.exists(provision_reg)
446 reg.diff_apply(provision_reg)
449 def setup_samdb_rootdse(samdb, setup_path, schemadn, domaindn, hostname,
450 dnsdomain, realm, rootdn, configdn, netbiosname):
451 """Setup the SamDB rootdse.
453 :param samdb: Sam Database handle
454 :param setup_path: Obtain setup path
457 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
458 "SCHEMADN": schemadn,
459 "NETBIOSNAME": netbiosname,
460 "DNSDOMAIN": dnsdomain,
461 "DEFAULTSITE": DEFAULTSITE,
463 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
464 "DOMAINDN": domaindn,
466 "CONFIGDN": configdn,
467 "VERSION": samba.version(),
471 def setup_self_join(samdb, configdn, schemadn, domaindn,
472 netbiosname, hostname, dnsdomain, machinepass, dnspass,
473 realm, domainname, domainsid, invocationid, setup_path,
474 policyguid, hostguid=None):
475 """Join a host to its own domain."""
476 if hostguid is not None:
477 hostguid_add = "objectGUID: %s" % hostguid
481 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
482 "CONFIGDN": configdn,
483 "SCHEMADN": schemadn,
484 "DOMAINDN": domaindn,
485 "INVOCATIONID": invocationid,
486 "NETBIOSNAME": netbiosname,
487 "DEFAULTSITE": DEFAULTSITE,
488 "DNSNAME": "%s.%s" % (hostname, dnsdomain),
489 "MACHINEPASS_B64": b64encode(machinepass),
490 "DNSPASS_B64": b64encode(dnspass),
492 "DOMAIN": domainname,
493 "HOSTGUID_ADD": hostguid_add,
494 "DNSDOMAIN": dnsdomain})
495 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
496 "POLICYGUID": policyguid,
497 "DNSDOMAIN": dnsdomain,
498 "DOMAINSID": str(domainsid),
499 "DOMAINDN": domaindn})
502 def setup_samdb(path, setup_path, session_info, credentials, lp,
503 schemadn, configdn, domaindn, dnsdomain, realm,
504 netbiosname, message, hostname, rootdn, erase,
505 domainsid, aci, domainguid, policyguid,
506 domainname, fill, adminpass, krbtgtpass,
507 machinepass, hostguid, invocationid, dnspass,
508 serverrole, ldap_backend=None, ldap_backend_type=None):
509 """Setup a complete SAM Database.
513 # Also wipes the database
514 setup_samdb_partitions(path, setup_path, schemadn=schemadn, configdn=configdn,
515 domaindn=domaindn, message=message, lp=lp,
516 credentials=credentials, session_info=session_info,
517 hostname=hostname, netbiosname=netbiosname,
518 dnsdomain=dnsdomain, realm=realm, rootdn=rootdn,
519 ldap_backend=ldap_backend, serverrole=serverrole,
520 ldap_backend_type=ldap_backend_type, erase=erase)
522 samdb = SamDB(path, session_info=session_info,
523 credentials=credentials, lp=lp)
526 # We want to finish here, but setup the index before we do so
527 message("Setting up sam.ldb index")
528 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
531 message("Pre-loading the Samba 4 and AD schema")
532 samdb = SamDB(path, session_info=session_info,
533 credentials=credentials, lp=lp)
534 samdb.set_domain_sid(domainsid)
535 if lp.get("server role") == "domain controller":
536 samdb.set_invocation_id(invocationid)
538 load_schema(setup_path, samdb, schemadn, netbiosname, configdn)
540 samdb.transaction_start()
543 message("Adding DomainDN: %s (permitted to fail)" % domaindn)
544 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
545 "DOMAINDN": domaindn,
549 message("Modifying DomainDN: " + domaindn + "")
550 if domainguid is not None:
551 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
555 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
556 "LDAPTIME": timestring(int(time.time())),
557 "DOMAINSID": str(domainsid),
558 "SCHEMADN": schemadn,
559 "NETBIOSNAME": netbiosname,
560 "DEFAULTSITE": DEFAULTSITE,
561 "CONFIGDN": configdn,
562 "POLICYGUID": policyguid,
563 "DOMAINDN": domaindn,
564 "DOMAINGUID_MOD": domainguid_mod,
567 message("Adding configuration container (permitted to fail)")
568 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
569 "CONFIGDN": configdn,
571 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb",
573 message("Modifying configuration container")
574 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
575 "CONFIGDN": configdn,
576 "SCHEMADN": schemadn,
579 message("Adding schema container (permitted to fail)")
580 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
581 "SCHEMADN": schemadn,
583 "EXTENSIBLEOBJECT": "# no objectClass: extensibleObject for local ldb"
585 message("Modifying schema container")
586 setup_modify_ldif(samdb,
587 setup_path("provision_schema_basedn_modify.ldif"), {
588 "SCHEMADN": schemadn,
589 "NETBIOSNAME": netbiosname,
590 "DEFAULTSITE": DEFAULTSITE,
591 "CONFIGDN": configdn,
594 message("Setting up sam.ldb Samba4 schema")
595 setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
596 {"SCHEMADN": schemadn })
597 message("Setting up sam.ldb AD schema")
598 setup_add_ldif(samdb, setup_path("schema.ldif"),
599 {"SCHEMADN": schemadn})
601 message("Setting up sam.ldb configuration data")
602 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
603 "CONFIGDN": configdn,
604 "NETBIOSNAME": netbiosname,
605 "DEFAULTSITE": DEFAULTSITE,
606 "DNSDOMAIN": dnsdomain,
607 "DOMAIN": domainname,
608 "SCHEMADN": schemadn,
609 "DOMAINDN": domaindn,
612 message("Setting up display specifiers")
613 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
614 {"CONFIGDN": configdn})
616 message("Adding users container (permitted to fail)")
617 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
618 "DOMAINDN": domaindn})
619 message("Modifying users container")
620 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
621 "DOMAINDN": domaindn})
622 message("Adding computers container (permitted to fail)")
623 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
624 "DOMAINDN": domaindn})
625 message("Modifying computers container")
626 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
627 "DOMAINDN": domaindn})
628 message("Setting up sam.ldb data")
629 setup_add_ldif(samdb, setup_path("provision.ldif"), {
630 "DOMAINDN": domaindn,
631 "NETBIOSNAME": netbiosname,
632 "DEFAULTSITE": DEFAULTSITE,
633 "CONFIGDN": configdn,
636 if fill == FILL_FULL:
637 message("Setting up sam.ldb users and groups")
638 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
639 "DOMAINDN": domaindn,
640 "DOMAINSID": str(domainsid),
641 "CONFIGDN": configdn,
642 "ADMINPASS_B64": b64encode(adminpass),
643 "KRBTGTPASS_B64": b64encode(krbtgtpass),
646 if lp.get("server role") == "domain controller":
647 message("Setting up self join")
648 setup_self_join(samdb, configdn=configdn, schemadn=schemadn,
649 domaindn=domaindn, invocationid=invocationid,
650 dnspass=dnspass, netbiosname=netbiosname,
651 dnsdomain=dnsdomain, realm=realm,
652 machinepass=machinepass, domainname=domainname,
653 domainsid=domainsid, policyguid=policyguid,
654 hostname=hostname, hostguid=hostguid,
655 setup_path=setup_path)
657 #We want to setup the index last, as adds are faster unindexed
658 message("Setting up sam.ldb index")
659 samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
661 samdb.transaction_cancel()
664 samdb.transaction_commit()
668 FILL_NT4SYNC = "NT4SYNC"
671 def provision(lp, setup_dir, message, paths, session_info,
672 credentials, ldapbackend, samdb_fill=FILL_FULL, realm=None, rootdn=None,
673 domain=None, hostname=None, hostip=None, domainsid=None,
674 hostguid=None, adminpass=None, krbtgtpass=None, domainguid=None,
675 policyguid=None, invocationid=None, machinepass=None,
676 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
677 wheel=None, backup=None, aci=None, serverrole=None, erase=False,
678 ldap_backend=None, ldap_backend_type=None):
681 :note: caution, this wipes all existing data!
684 def setup_path(file):
685 return os.path.join(setup_dir, file)
687 if domainsid is None:
688 domainsid = security.random_sid()
689 if policyguid is None:
690 policyguid = uuid.random()
691 if adminpass is None:
692 adminpass = misc.random_password(12)
693 if krbtgtpass is None:
694 krbtgtpass = misc.random_password(12)
695 if machinepass is None:
696 machinepass = misc.random_password(12)
698 dnspass = misc.random_password(12)
700 root = findnss(pwd.getpwnam, "root")[0]
702 nobody = findnss(pwd.getpwnam, "nobody")[0]
704 nogroup = findnss(grp.getgrnam, "nogroup", "nobody")[0]
706 users = findnss(grp.getgrnam, "users", "guest", "other", "unknown",
709 wheel = findnss(grp.getgrnam, "wheel", "root", "staff", "adm")[0]
711 backup = findnss(grp.getgrnam, "backup", "wheel", "root", "staff")[0]
713 aci = "# no aci for local ldb"
714 if serverrole is None:
715 serverrole = lp.get("server role")
716 if invocationid is None and serverrole == "domain controller":
717 invocationid = uuid.random()
720 realm = lp.get("realm")
722 if lp.get("realm").upper() != realm.upper():
723 raise Exception("realm '%s' in smb.conf must match chosen realm '%s'" %
724 (lp.get("realm"), realm))
726 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path)
728 if ldap_backend == "ldapi":
729 # provision-backend will set this path suggested slapd command line / fedorads.inf
730 ldap_backend = "ldapi://" % urllib.quote(os.path.join(lp.get("private dir"), "ldap", "ldapi"), "")
732 assert realm is not None
733 realm = realm.upper()
736 hostname = gethostname().split(".")[0].lower()
739 hostip = gethostbyname(hostname)
741 netbiosname = hostname.upper()
742 if not valid_netbios_name(netbiosname):
743 raise InvalidNetbiosName(netbiosname)
745 dnsdomain = realm.lower()
746 if serverrole == "domain controller":
747 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
749 domain = lp.get("workgroup")
751 if lp.get("workgroup").upper() != domain.upper():
752 raise Error("workgroup '%s' in smb.conf must match chosen domain '%s'",
753 lp.get("workgroup"), domain)
755 assert domain is not None
756 domain = domain.upper()
757 if not valid_netbios_name(domain):
758 raise InvalidNetbiosName(domain)
761 domaindn = "CN=" + netbiosname
767 configdn = "CN=Configuration," + rootdn
768 schemadn = "CN=Schema," + configdn
770 message("set DOMAIN SID: %s" % str(domainsid))
771 message("Provisioning for %s in realm %s" % (domain, realm))
772 message("Using administrator password: %s" % adminpass)
774 assert paths.smbconf is not None
776 # only install a new smb.conf if there isn't one there already
777 if not os.path.exists(paths.smbconf):
778 message("Setting up smb.conf")
779 if serverrole == "domain controller":
781 elif serverrole == "member":
782 smbconfsuffix = "member"
784 assert "Invalid server role setting: %s" % serverrole
785 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
787 "HOSTNAME": hostname,
788 "DOMAIN_CONF": domain,
790 "SERVERROLE": serverrole,
791 "NETLOGONPATH": paths.netlogon,
792 "SYSVOLPATH": paths.sysvol,
796 # only install a new shares config db if there is none
797 if not os.path.exists(paths.shareconf):
798 message("Setting up share.ldb")
799 share_ldb = Ldb(paths.shareconf, session_info=session_info,
800 credentials=credentials, lp=lp)
801 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
804 message("Setting up secrets.ldb")
805 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
806 session_info=session_info,
807 credentials=credentials, lp=lp)
809 message("Setting up the registry")
810 setup_registry(paths.hklm, setup_path, session_info,
811 credentials=credentials, lp=lp)
813 message("Setting up templates db")
814 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
815 credentials=credentials, lp=lp)
817 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
818 credentials=credentials, lp=lp, schemadn=schemadn,
819 configdn=configdn, domaindn=domaindn,
820 dnsdomain=dnsdomain, netbiosname=netbiosname,
821 realm=realm, message=message, hostname=hostname,
822 rootdn=rootdn, erase=erase, domainsid=domainsid,
823 aci=aci, domainguid=domainguid, policyguid=policyguid,
824 domainname=domain, fill=samdb_fill,
825 adminpass=adminpass, krbtgtpass=krbtgtpass,
826 hostguid=hostguid, invocationid=invocationid,
827 machinepass=machinepass, dnspass=dnspass,
828 serverrole=serverrole, ldap_backend=ldap_backend,
829 ldap_backend_type=ldap_backend_type)
831 if lp.get("server role") == "domain controller":
832 policy_path = os.path.join(paths.sysvol, dnsdomain, "Policies",
833 "{" + policyguid + "}")
834 os.makedirs(policy_path, 0755)
835 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
836 os.makedirs(os.path.join(policy_path, "User"), 0755)
837 if not os.path.isdir(paths.netlogon):
838 os.makedirs(paths.netlogon, 0755)
839 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
840 credentials=credentials, lp=lp)
841 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=realm,
842 netbiosname=netbiosname, domainsid=domainsid,
843 keytab_path=paths.keytab, samdb_url=paths.samdb,
844 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
845 machinepass=machinepass, dnsdomain=dnsdomain)
847 if samdb_fill == FILL_FULL:
848 setup_name_mappings(samdb, str(domainsid), domaindn, root=root,
849 nobody=nobody, nogroup=nogroup, wheel=wheel,
850 users=users, backup=backup)
852 message("Setting up sam.ldb rootDSE marking as synchronized")
853 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
855 message("Setting up phpLDAPadmin configuration")
856 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
859 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
861 if lp.get("server role") == "domain controller":
862 samdb = SamDB(paths.samdb, session_info=session_info,
863 credentials=credentials, lp=lp)
865 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
866 assert isinstance(domainguid, str)
867 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
868 expression="(&(objectClass=computer)(cn=%s))" % hostname,
870 assert isinstance(hostguid, str)
872 message("Setting up DNS zone: %s" % dnsdomain)
873 create_zone_file(paths.dns, setup_path, samdb,
874 hostname=hostname, hostip=hostip, dnsdomain=dnsdomain,
875 domaindn=domaindn, dnspass=dnspass, realm=realm,
876 domainguid=domainguid, hostguid=hostguid)
877 message("Please install the zone located in %s into your DNS server" % paths.dns)
882 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
883 """Create a PHP LDAP admin configuration file.
885 :param path: Path to write the configuration to.
886 :param setup_path: Function to generate setup paths.
888 setup_file(setup_path("phpldapadmin-config.php"), path,
889 {"S4_LDAPI_URI": ldapi_uri})
892 def create_zone_file(path, setup_path, samdb, dnsdomain, domaindn,
893 hostip, hostname, dnspass, realm, domainguid, hostguid):
894 """Write out a DNS zone file, from the info in the current database.
896 :param path: Path of the new file.
897 :param setup_path": Setup path function.
898 :param samdb: SamDB object
899 :param dnsdomain: DNS Domain name
900 :param domaindn: DN of the Domain
901 :param hostip: Local IP
902 :param hostname: Local hostname
903 :param dnspass: Password for DNS
904 :param realm: Realm name
905 :param domainguid: GUID of the domain.
906 :param hostguid: GUID of the host.
908 assert isinstance(domainguid, str)
910 setup_file(setup_path("provision.zone"), path, {
911 "DNSPASS_B64": b64encode(dnspass),
912 "HOSTNAME": hostname,
913 "DNSDOMAIN": dnsdomain,
916 "DOMAINGUID": domainguid,
917 "DATESTRING": time.strftime("%Y%m%d%H"),
918 "DEFAULTSITE": DEFAULTSITE,
919 "HOSTGUID": hostguid,
923 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn):
926 :param samdb: Load a schema into a SamDB.
927 :param setup_path: Setup path function.
928 :param schemadn: DN of the schema
929 :param netbiosname: NetBIOS name of the host.
930 :param configdn: DN of the configuration
932 schema_data = open(setup_path("schema.ldif"), 'r').read()
933 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
934 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
935 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
936 head_data = substitute_var(head_data, {
937 "SCHEMADN": schemadn,
938 "NETBIOSNAME": netbiosname,
939 "CONFIGDN": configdn,
940 "DEFAULTSITE": DEFAULTSITE
942 samdb.attach_schema_from_ldif(head_data, schema_data)