s4:provision Make the --ol-slapd paramter take the full path to slapd
[Samba/ekacnet.git] / source4 / scripting / python / samba / provision.py
blob634904441a34f88e7912ed7bed78549f9b7fbc22
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
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
29 import os
30 import sys
31 import pwd
32 import grp
33 import time
34 import uuid, glue
35 import socket
36 import param
37 import registry
38 import samba
39 import subprocess
40 from auth import system_session
41 from samba import version, Ldb, substitute_var, valid_netbios_name, check_all_substituted, \
42 DS_BEHAVIOR_WIN2008
43 from samba.samdb import SamDB
44 from samba.idmap import IDmapDB
45 from samba.dcerpc import security
46 import urllib
47 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
48 from ms_schema import read_ms_schema
49 from signal import SIGTERM
51 __docformat__ = "restructuredText"
54 def find_setup_dir():
55 """Find the setup directory used by provision."""
56 dirname = os.path.dirname(__file__)
57 if "/site-packages/" in dirname:
58 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
59 for suffix in ["share/setup", "share/samba/setup", "setup"]:
60 ret = os.path.join(prefix, suffix)
61 if os.path.isdir(ret):
62 return ret
63 # In source tree
64 ret = os.path.join(dirname, "../../../setup")
65 if os.path.isdir(ret):
66 return ret
67 raise Exception("Unable to find setup directory.")
70 DEFAULTSITE = "Default-First-Site-Name"
72 class InvalidNetbiosName(Exception):
73 """A specified name was not a valid NetBIOS name."""
74 def __init__(self, name):
75 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
78 class ProvisionPaths(object):
79 def __init__(self):
80 self.shareconf = None
81 self.hklm = None
82 self.hkcu = None
83 self.hkcr = None
84 self.hku = None
85 self.hkpd = None
86 self.hkpt = None
87 self.samdb = None
88 self.idmapdb = None
89 self.secrets = None
90 self.keytab = None
91 self.dns_keytab = None
92 self.dns = None
93 self.winsdb = None
94 self.private_dir = None
95 self.ldapdir = None
96 self.slapdconf = None
97 self.modulesconf = None
98 self.memberofconf = None
99 self.fedoradsinf = None
100 self.fedoradspartitions = None
101 self.olmmron = None
102 self.olmmrserveridsconf = None
103 self.olmmrsyncreplconf = None
104 self.olcdir = None
105 self.olslapd = None
106 self.olcseedldif = None
109 class ProvisionNames(object):
110 def __init__(self):
111 self.rootdn = None
112 self.domaindn = None
113 self.configdn = None
114 self.schemadn = None
115 self.ldapmanagerdn = None
116 self.dnsdomain = None
117 self.realm = None
118 self.netbiosname = None
119 self.domain = None
120 self.hostname = None
121 self.sitename = None
122 self.smbconf = None
125 class ProvisionResult(object):
126 def __init__(self):
127 self.paths = None
128 self.domaindn = None
129 self.lp = None
130 self.samdb = None
132 def check_install(lp, session_info, credentials):
133 """Check whether the current install seems ok.
135 :param lp: Loadparm context
136 :param session_info: Session information
137 :param credentials: Credentials
139 if lp.get("realm") == "":
140 raise Exception("Realm empty")
141 ldb = Ldb(lp.get("sam database"), session_info=session_info,
142 credentials=credentials, lp=lp)
143 if len(ldb.search("(cn=Administrator)")) != 1:
144 raise "No administrator account found"
147 def findnss(nssfn, names):
148 """Find a user or group from a list of possibilities.
150 :param nssfn: NSS Function to try (should raise KeyError if not found)
151 :param names: Names to check.
152 :return: Value return by first names list.
154 for name in names:
155 try:
156 return nssfn(name)
157 except KeyError:
158 pass
159 raise KeyError("Unable to find user/group %r" % names)
162 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
163 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
166 def read_and_sub_file(file, subst_vars):
167 """Read a file and sub in variables found in it
169 :param file: File to be read (typically from setup directory)
170 param subst_vars: Optional variables to subsitute in the file.
172 data = open(file, 'r').read()
173 if subst_vars is not None:
174 data = substitute_var(data, subst_vars)
175 check_all_substituted(data)
176 return data
179 def setup_add_ldif(ldb, ldif_path, subst_vars=None):
180 """Setup a ldb in the private dir.
182 :param ldb: LDB file to import data into
183 :param ldif_path: Path of the LDIF file to load
184 :param subst_vars: Optional variables to subsitute in LDIF.
186 assert isinstance(ldif_path, str)
188 data = read_and_sub_file(ldif_path, subst_vars)
189 ldb.add_ldif(data)
192 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
193 """Modify a ldb in the private dir.
195 :param ldb: LDB object.
196 :param ldif_path: LDIF file path.
197 :param subst_vars: Optional dictionary with substitution variables.
199 data = read_and_sub_file(ldif_path, subst_vars)
201 ldb.modify_ldif(data)
204 def setup_ldb(ldb, ldif_path, subst_vars):
205 """Import a LDIF a file into a LDB handle, optionally substituting variables.
207 :note: Either all LDIF data will be added or none (using transactions).
209 :param ldb: LDB file to import into.
210 :param ldif_path: Path to the LDIF file.
211 :param subst_vars: Dictionary with substitution variables.
213 assert ldb is not None
214 ldb.transaction_start()
215 try:
216 setup_add_ldif(ldb, ldif_path, subst_vars)
217 except:
218 ldb.transaction_cancel()
219 raise
220 ldb.transaction_commit()
223 def setup_file(template, fname, subst_vars):
224 """Setup a file in the private dir.
226 :param template: Path of the template file.
227 :param fname: Path of the file to create.
228 :param subst_vars: Substitution variables.
230 f = fname
232 if os.path.exists(f):
233 os.unlink(f)
235 data = read_and_sub_file(template, subst_vars)
236 open(f, 'w').write(data)
239 def provision_paths_from_lp(lp, dnsdomain):
240 """Set the default paths for provisioning.
242 :param lp: Loadparm context.
243 :param dnsdomain: DNS Domain name
245 paths = ProvisionPaths()
246 paths.private_dir = lp.get("private dir")
247 paths.keytab = "secrets.keytab"
248 paths.dns_keytab = "dns.keytab"
250 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
251 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
252 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
253 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
254 paths.templates = os.path.join(paths.private_dir, "templates.ldb")
255 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
256 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
257 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
258 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
259 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
260 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
261 paths.phpldapadminconfig = os.path.join(paths.private_dir,
262 "phpldapadmin-config.php")
263 paths.ldapdir = os.path.join(paths.private_dir,
264 "ldap")
265 paths.slapdconf = os.path.join(paths.ldapdir,
266 "slapd.conf")
267 paths.slapdpid = os.path.join(paths.ldapdir,
268 "slapd.pid")
269 paths.modulesconf = os.path.join(paths.ldapdir,
270 "modules.conf")
271 paths.memberofconf = os.path.join(paths.ldapdir,
272 "memberof.conf")
273 paths.fedoradsinf = os.path.join(paths.ldapdir,
274 "fedorads.inf")
275 paths.fedoradspartitions = os.path.join(paths.ldapdir,
276 "fedorads-partitions.ldif")
277 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
278 "mmr_serverids.conf")
279 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
280 "mmr_syncrepl.conf")
281 paths.olcdir = os.path.join(paths.ldapdir,
282 "slapd.d")
283 paths.olcseedldif = os.path.join(paths.ldapdir,
284 "olc_seed.ldif")
285 paths.hklm = "hklm.ldb"
286 paths.hkcr = "hkcr.ldb"
287 paths.hkcu = "hkcu.ldb"
288 paths.hku = "hku.ldb"
289 paths.hkpd = "hkpd.ldb"
290 paths.hkpt = "hkpt.ldb"
292 paths.sysvol = lp.get("path", "sysvol")
294 paths.netlogon = lp.get("path", "netlogon")
296 paths.smbconf = lp.configfile
298 return paths
301 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
302 rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
303 sitename=None):
304 """Guess configuration settings to use."""
306 if hostname is None:
307 hostname = socket.gethostname().split(".")[0].lower()
309 netbiosname = hostname.upper()
310 if not valid_netbios_name(netbiosname):
311 raise InvalidNetbiosName(netbiosname)
313 hostname = hostname.lower()
315 if dnsdomain is None:
316 dnsdomain = lp.get("realm")
318 if serverrole is None:
319 serverrole = lp.get("server role")
321 assert dnsdomain is not None
322 realm = dnsdomain.upper()
324 if lp.get("realm").upper() != realm:
325 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
326 (lp.get("realm"), lp.configfile, realm))
328 dnsdomain = dnsdomain.lower()
330 if serverrole == "domain controller":
331 if domain is None:
332 domain = lp.get("workgroup")
333 if domaindn is None:
334 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
335 if lp.get("workgroup").upper() != domain.upper():
336 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
337 lp.get("workgroup"), domain)
338 else:
339 domain = netbiosname
340 if domaindn is None:
341 domaindn = "CN=" + netbiosname
343 assert domain is not None
344 domain = domain.upper()
345 if not valid_netbios_name(domain):
346 raise InvalidNetbiosName(domain)
348 if rootdn is None:
349 rootdn = domaindn
351 if configdn is None:
352 configdn = "CN=Configuration," + rootdn
353 if schemadn is None:
354 schemadn = "CN=Schema," + configdn
356 if sitename is None:
357 sitename=DEFAULTSITE
359 names = ProvisionNames()
360 names.rootdn = rootdn
361 names.domaindn = domaindn
362 names.configdn = configdn
363 names.schemadn = schemadn
364 names.ldapmanagerdn = "CN=Manager," + rootdn
365 names.dnsdomain = dnsdomain
366 names.domain = domain
367 names.realm = realm
368 names.netbiosname = netbiosname
369 names.hostname = hostname
370 names.sitename = sitename
371 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
373 return names
376 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
377 targetdir):
378 """Create a new smb.conf file based on a couple of basic settings.
380 assert smbconf is not None
381 if hostname is None:
382 hostname = socket.gethostname().split(".")[0].lower()
384 if serverrole is None:
385 serverrole = "standalone"
387 assert serverrole in ("domain controller", "member server", "standalone")
388 if serverrole == "domain controller":
389 smbconfsuffix = "dc"
390 elif serverrole == "member server":
391 smbconfsuffix = "member"
392 elif serverrole == "standalone":
393 smbconfsuffix = "standalone"
395 assert domain is not None
396 assert realm is not None
398 default_lp = param.LoadParm()
399 #Load non-existant file
400 if os.path.exists(smbconf):
401 default_lp.load(smbconf)
403 if targetdir is not None:
404 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
405 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
407 default_lp.set("lock dir", os.path.abspath(targetdir))
408 else:
409 privatedir_line = ""
410 lockdir_line = ""
412 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
413 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
415 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
416 smbconf, {
417 "HOSTNAME": hostname,
418 "DOMAIN": domain,
419 "REALM": realm,
420 "SERVERROLE": serverrole,
421 "NETLOGONPATH": netlogon,
422 "SYSVOLPATH": sysvol,
423 "PRIVATEDIR_LINE": privatedir_line,
424 "LOCKDIR_LINE": lockdir_line
428 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
429 users_gid, wheel_gid):
430 """setup reasonable name mappings for sam names to unix names.
432 :param samdb: SamDB object.
433 :param idmap: IDmap db object.
434 :param sid: The domain sid.
435 :param domaindn: The domain DN.
436 :param root_uid: uid of the UNIX root user.
437 :param nobody_uid: uid of the UNIX nobody user.
438 :param users_gid: gid of the UNIX users group.
439 :param wheel_gid: gid of the UNIX wheel group."""
440 # add some foreign sids if they are not present already
441 samdb.add_stock_foreign_sids()
443 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
444 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
446 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
447 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
450 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
451 credentials, names,
452 serverrole, ldap_backend=None,
453 ldap_backend_type=None, erase=False):
454 """Setup the partitions for the SAM database.
456 Alternatively, provision() may call this, and then populate the database.
458 :note: This will wipe the Sam Database!
460 :note: This function always removes the local SAM LDB file. The erase
461 parameter controls whether to erase the existing data, which
462 may not be stored locally but in LDAP.
464 assert session_info is not None
466 try:
467 samdb = SamDB(samdb_path, session_info=session_info,
468 credentials=credentials, lp=lp)
469 # Wipes the database
470 samdb.erase()
471 except LdbError:
472 os.unlink(samdb_path)
473 samdb = SamDB(samdb_path, session_info=session_info,
474 credentials=credentials, lp=lp)
475 # Wipes the database
476 samdb.erase()
479 #Add modules to the list to activate them by default
480 #beware often order is important
482 # Some Known ordering constraints:
483 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
484 # - objectclass must be before password_hash, because password_hash checks
485 # that the objectclass is of type person (filled in by objectclass
486 # module when expanding the objectclass list)
487 # - partition must be last
488 # - each partition has its own module list then
489 modules_list = ["rootdse",
490 "paged_results",
491 "ranged_results",
492 "anr",
493 "server_sort",
494 "asq",
495 "extended_dn_store",
496 "extended_dn_in",
497 "rdn_name",
498 "objectclass",
499 "samldb",
500 "kludge_acl",
501 "password_hash",
502 "operational"]
503 tdb_modules_list = [
504 "subtree_rename",
505 "subtree_delete",
506 "linked_attributes",
507 "extended_dn_out_ldb"]
508 modules_list2 = ["show_deleted",
509 "partition"]
511 domaindn_ldb = "users.ldb"
512 if ldap_backend is not None:
513 domaindn_ldb = ldap_backend
514 configdn_ldb = "configuration.ldb"
515 if ldap_backend is not None:
516 configdn_ldb = ldap_backend
517 schemadn_ldb = "schema.ldb"
518 if ldap_backend is not None:
519 schema_ldb = ldap_backend
520 schemadn_ldb = ldap_backend
522 if ldap_backend_type == "fedora-ds":
523 backend_modules = ["nsuniqueid", "paged_searches"]
524 # We can handle linked attributes here, as we don't have directory-side subtree operations
525 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
526 elif ldap_backend_type == "openldap":
527 backend_modules = ["entryuuid", "paged_searches"]
528 # OpenLDAP handles subtree renames, so we don't want to do any of these things
529 tdb_modules_list = ["extended_dn_out_dereference"]
530 elif ldap_backend is not None:
531 raise "LDAP Backend specified, but LDAP Backend Type not specified"
532 elif serverrole == "domain controller":
533 backend_modules = ["repl_meta_data"]
534 else:
535 backend_modules = ["objectguid"]
537 if tdb_modules_list is None:
538 tdb_modules_list_as_string = ""
539 else:
540 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
542 samdb.transaction_start()
543 try:
544 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
545 "SCHEMADN": names.schemadn,
546 "SCHEMADN_LDB": schemadn_ldb,
547 "SCHEMADN_MOD2": ",objectguid",
548 "CONFIGDN": names.configdn,
549 "CONFIGDN_LDB": configdn_ldb,
550 "DOMAINDN": names.domaindn,
551 "DOMAINDN_LDB": domaindn_ldb,
552 "SCHEMADN_MOD": "schema_fsmo,instancetype",
553 "CONFIGDN_MOD": "naming_fsmo,instancetype",
554 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
555 "MODULES_LIST": ",".join(modules_list),
556 "TDB_MODULES_LIST": tdb_modules_list_as_string,
557 "MODULES_LIST2": ",".join(modules_list2),
558 "BACKEND_MOD": ",".join(backend_modules),
561 except:
562 samdb.transaction_cancel()
563 raise
565 samdb.transaction_commit()
567 samdb = SamDB(samdb_path, session_info=session_info,
568 credentials=credentials, lp=lp)
570 samdb.transaction_start()
571 try:
572 message("Setting up sam.ldb attributes")
573 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
575 message("Setting up sam.ldb rootDSE")
576 setup_samdb_rootdse(samdb, setup_path, names)
578 if erase:
579 message("Erasing data from partitions")
580 samdb.erase_partitions()
582 except:
583 samdb.transaction_cancel()
584 raise
586 samdb.transaction_commit()
588 return samdb
591 def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
592 netbiosname, domainsid, keytab_path, samdb_url,
593 dns_keytab_path, dnspass, machinepass):
594 """Add DC-specific bits to a secrets database.
596 :param secretsdb: Ldb Handle to the secrets database
597 :param setup_path: Setup path function
598 :param machinepass: Machine password
600 setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
601 "MACHINEPASS_B64": b64encode(machinepass),
602 "DOMAIN": domain,
603 "REALM": realm,
604 "DNSDOMAIN": dnsdomain,
605 "DOMAINSID": str(domainsid),
606 "SECRETS_KEYTAB": keytab_path,
607 "NETBIOSNAME": netbiosname,
608 "SAM_LDB": samdb_url,
609 "DNS_KEYTAB": dns_keytab_path,
610 "DNSPASS_B64": b64encode(dnspass),
614 def setup_secretsdb(path, setup_path, session_info, credentials, lp):
615 """Setup the secrets database.
617 :param path: Path to the secrets database.
618 :param setup_path: Get the path to a setup file.
619 :param session_info: Session info.
620 :param credentials: Credentials
621 :param lp: Loadparm context
622 :return: LDB handle for the created secrets database
624 if os.path.exists(path):
625 os.unlink(path)
626 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
627 lp=lp)
628 secrets_ldb.erase()
629 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
630 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
631 lp=lp)
632 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
634 if credentials is not None and credentials.authentication_requested():
635 if credentials.get_bind_dn() is not None:
636 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
637 "LDAPMANAGERDN": credentials.get_bind_dn(),
638 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
640 else:
641 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
642 "LDAPADMINUSER": credentials.get_username(),
643 "LDAPADMINREALM": credentials.get_realm(),
644 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
647 return secrets_ldb
650 def setup_templatesdb(path, setup_path, session_info, credentials, lp):
651 """Setup the templates database.
653 :param path: Path to the database.
654 :param setup_path: Function for obtaining the path to setup files.
655 :param session_info: Session info
656 :param credentials: Credentials
657 :param lp: Loadparm context
659 templates_ldb = SamDB(path, session_info=session_info,
660 credentials=credentials, lp=lp)
661 # Wipes the database
662 try:
663 templates_ldb.erase()
664 # This should be 'except LdbError', but on a re-provision the assert in ldb.erase fires, and we need to catch that too
665 except:
666 os.unlink(path)
668 templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
670 templates_ldb = SamDB(path, session_info=session_info,
671 credentials=credentials, lp=lp)
673 templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
676 def setup_registry(path, setup_path, session_info, credentials, lp):
677 """Setup the registry.
679 :param path: Path to the registry database
680 :param setup_path: Function that returns the path to a setup.
681 :param session_info: Session information
682 :param credentials: Credentials
683 :param lp: Loadparm context
685 reg = registry.Registry()
686 hive = registry.open_ldb(path, session_info=session_info,
687 credentials=credentials, lp_ctx=lp)
688 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
689 provision_reg = setup_path("provision.reg")
690 assert os.path.exists(provision_reg)
691 reg.diff_apply(provision_reg)
694 def setup_idmapdb(path, setup_path, session_info, credentials, lp):
695 """Setup the idmap database.
697 :param path: path to the idmap database
698 :param setup_path: Function that returns a path to a setup file
699 :param session_info: Session information
700 :param credentials: Credentials
701 :param lp: Loadparm context
703 if os.path.exists(path):
704 os.unlink(path)
706 idmap_ldb = IDmapDB(path, session_info=session_info,
707 credentials=credentials, lp=lp)
709 idmap_ldb.erase()
710 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
711 return idmap_ldb
714 def setup_samdb_rootdse(samdb, setup_path, names):
715 """Setup the SamDB rootdse.
717 :param samdb: Sam Database handle
718 :param setup_path: Obtain setup path
720 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
721 "SCHEMADN": names.schemadn,
722 "NETBIOSNAME": names.netbiosname,
723 "DNSDOMAIN": names.dnsdomain,
724 "REALM": names.realm,
725 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
726 "DOMAINDN": names.domaindn,
727 "ROOTDN": names.rootdn,
728 "CONFIGDN": names.configdn,
729 "SERVERDN": names.serverdn,
733 def setup_self_join(samdb, names,
734 machinepass, dnspass,
735 domainsid, invocationid, setup_path,
736 policyguid, domainControllerFunctionality):
737 """Join a host to its own domain."""
738 assert isinstance(invocationid, str)
739 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
740 "CONFIGDN": names.configdn,
741 "SCHEMADN": names.schemadn,
742 "DOMAINDN": names.domaindn,
743 "SERVERDN": names.serverdn,
744 "INVOCATIONID": invocationid,
745 "NETBIOSNAME": names.netbiosname,
746 "DEFAULTSITE": names.sitename,
747 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
748 "MACHINEPASS_B64": b64encode(machinepass),
749 "DNSPASS_B64": b64encode(dnspass),
750 "REALM": names.realm,
751 "DOMAIN": names.domain,
752 "DNSDOMAIN": names.dnsdomain,
753 "SAMBA_VERSION_STRING": version,
754 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
755 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
756 "POLICYGUID": policyguid,
757 "DNSDOMAIN": names.dnsdomain,
758 "DOMAINSID": str(domainsid),
759 "DOMAINDN": names.domaindn})
762 def setup_samdb(path, setup_path, session_info, credentials, lp,
763 names, message,
764 domainsid, aci, domainguid, policyguid,
765 fill, adminpass, krbtgtpass,
766 machinepass, invocationid, dnspass,
767 serverrole, ldap_backend=None,
768 ldap_backend_type=None):
769 """Setup a complete SAM Database.
771 :note: This will wipe the main SAM database file!
774 domainFunctionality = DS_BEHAVIOR_WIN2008
775 forestFunctionality = DS_BEHAVIOR_WIN2008
776 domainControllerFunctionality = DS_BEHAVIOR_WIN2008
778 erase = (fill != FILL_DRS)
780 # Also wipes the database
781 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
782 credentials=credentials, session_info=session_info,
783 names=names,
784 ldap_backend=ldap_backend, serverrole=serverrole,
785 ldap_backend_type=ldap_backend_type, erase=erase)
787 samdb = SamDB(path, session_info=session_info,
788 credentials=credentials, lp=lp)
789 if fill == FILL_DRS:
790 return samdb
792 message("Pre-loading the Samba 4 and AD schema")
794 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
795 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
796 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
798 samdb.set_domain_sid(str(domainsid))
799 if serverrole == "domain controller":
800 samdb.set_invocation_id(invocationid)
802 schema_data = load_schema(setup_path, samdb, names.schemadn, names.netbiosname,
803 names.configdn, names.sitename, names.serverdn)
804 samdb.transaction_start()
806 try:
807 message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
808 if serverrole == "domain controller":
809 domain_oc = "domainDNS"
810 else:
811 domain_oc = "samba4LocalDomain"
813 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
814 "DOMAINDN": names.domaindn,
815 "ACI": aci,
816 "DOMAIN_OC": domain_oc
819 message("Modifying DomainDN: " + names.domaindn + "")
820 if domainguid is not None:
821 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
822 else:
823 domainguid_mod = ""
825 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
826 "LDAPTIME": timestring(int(time.time())),
827 "DOMAINSID": str(domainsid),
828 "SCHEMADN": names.schemadn,
829 "NETBIOSNAME": names.netbiosname,
830 "DEFAULTSITE": names.sitename,
831 "CONFIGDN": names.configdn,
832 "SERVERDN": names.serverdn,
833 "POLICYGUID": policyguid,
834 "DOMAINDN": names.domaindn,
835 "DOMAINGUID_MOD": domainguid_mod,
836 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
839 message("Adding configuration container (permitted to fail)")
840 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
841 "CONFIGDN": names.configdn,
842 "ACI": aci,
844 message("Modifying configuration container")
845 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
846 "CONFIGDN": names.configdn,
847 "SCHEMADN": names.schemadn,
850 message("Adding schema container (permitted to fail)")
851 setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
852 "SCHEMADN": names.schemadn,
853 "ACI": aci,
855 message("Modifying schema container")
857 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
859 setup_modify_ldif(samdb,
860 setup_path("provision_schema_basedn_modify.ldif"), {
861 "SCHEMADN": names.schemadn,
862 "NETBIOSNAME": names.netbiosname,
863 "DEFAULTSITE": names.sitename,
864 "CONFIGDN": names.configdn,
865 "SERVERDN": names.serverdn,
866 "PREFIXMAP_B64": b64encode(prefixmap)
869 message("Setting up sam.ldb schema")
870 samdb.add_ldif(schema_data)
871 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
872 {"SCHEMADN": names.schemadn})
874 message("Setting up sam.ldb configuration data")
875 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
876 "CONFIGDN": names.configdn,
877 "NETBIOSNAME": names.netbiosname,
878 "DEFAULTSITE": names.sitename,
879 "DNSDOMAIN": names.dnsdomain,
880 "DOMAIN": names.domain,
881 "SCHEMADN": names.schemadn,
882 "DOMAINDN": names.domaindn,
883 "SERVERDN": names.serverdn,
884 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
887 message("Setting up display specifiers")
888 setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
889 {"CONFIGDN": names.configdn})
891 message("Adding users container (permitted to fail)")
892 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
893 "DOMAINDN": names.domaindn})
894 message("Modifying users container")
895 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
896 "DOMAINDN": names.domaindn})
897 message("Adding computers container (permitted to fail)")
898 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
899 "DOMAINDN": names.domaindn})
900 message("Modifying computers container")
901 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
902 "DOMAINDN": names.domaindn})
903 message("Setting up sam.ldb data")
904 setup_add_ldif(samdb, setup_path("provision.ldif"), {
905 "DOMAINDN": names.domaindn,
906 "NETBIOSNAME": names.netbiosname,
907 "DEFAULTSITE": names.sitename,
908 "CONFIGDN": names.configdn,
909 "SERVERDN": names.serverdn
912 if fill == FILL_FULL:
913 message("Setting up sam.ldb users and groups")
914 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
915 "DOMAINDN": names.domaindn,
916 "DOMAINSID": str(domainsid),
917 "CONFIGDN": names.configdn,
918 "ADMINPASS_B64": b64encode(adminpass),
919 "KRBTGTPASS_B64": b64encode(krbtgtpass),
922 if serverrole == "domain controller":
923 message("Setting up self join")
924 setup_self_join(samdb, names=names, invocationid=invocationid,
925 dnspass=dnspass,
926 machinepass=machinepass,
927 domainsid=domainsid, policyguid=policyguid,
928 setup_path=setup_path, domainControllerFunctionality=domainControllerFunctionality)
930 except:
931 samdb.transaction_cancel()
932 raise
934 samdb.transaction_commit()
935 return samdb
938 FILL_FULL = "FULL"
939 FILL_NT4SYNC = "NT4SYNC"
940 FILL_DRS = "DRS"
943 def provision(setup_dir, message, session_info,
944 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
945 rootdn=None, domaindn=None, schemadn=None, configdn=None,
946 serverdn=None,
947 domain=None, hostname=None, hostip=None, hostip6=None,
948 domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
949 policyguid=None, invocationid=None, machinepass=None,
950 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
951 wheel=None, backup=None, aci=None, serverrole=None,
952 ldap_backend=None, ldap_backend_type=None, sitename=None):
953 """Provision samba4
955 :note: caution, this wipes all existing data!
958 def setup_path(file):
959 return os.path.join(setup_dir, file)
961 if domainsid is None:
962 domainsid = security.random_sid()
964 if policyguid is None:
965 policyguid = str(uuid.uuid4())
966 if adminpass is None:
967 adminpass = glue.generate_random_str(12)
968 if krbtgtpass is None:
969 krbtgtpass = glue.generate_random_str(12)
970 if machinepass is None:
971 machinepass = glue.generate_random_str(12)
972 if dnspass is None:
973 dnspass = glue.generate_random_str(12)
974 root_uid = findnss_uid([root or "root"])
975 nobody_uid = findnss_uid([nobody or "nobody"])
976 users_gid = findnss_gid([users or "users"])
977 if wheel is None:
978 wheel_gid = findnss_gid(["wheel", "adm"])
979 else:
980 wheel_gid = findnss_gid([wheel])
981 if aci is None:
982 aci = "# no aci for local ldb"
984 if targetdir is not None:
985 if (not os.path.exists(os.path.join(targetdir, "etc"))):
986 os.makedirs(os.path.join(targetdir, "etc"))
987 smbconf = os.path.join(targetdir, "etc", "smb.conf")
988 elif smbconf is None:
989 smbconf = param.default_path()
991 # only install a new smb.conf if there isn't one there already
992 if not os.path.exists(smbconf):
993 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
994 targetdir)
996 lp = param.LoadParm()
997 lp.load(smbconf)
999 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1000 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1001 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1002 serverdn=serverdn)
1004 paths = provision_paths_from_lp(lp, names.dnsdomain)
1006 if hostip is None:
1007 try:
1008 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1009 except socket.gaierror, (socket.EAI_NODATA, msg):
1010 hostip = None
1012 if hostip6 is None:
1013 try:
1014 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1015 except socket.gaierror, (socket.EAI_NODATA, msg):
1016 hostip6 = None
1018 if serverrole is None:
1019 serverrole = lp.get("server role")
1021 assert serverrole in ("domain controller", "member server", "standalone")
1022 if invocationid is None and serverrole == "domain controller":
1023 invocationid = str(uuid.uuid4())
1025 if not os.path.exists(paths.private_dir):
1026 os.mkdir(paths.private_dir)
1028 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1030 if ldap_backend is not None:
1031 if ldap_backend == "ldapi":
1032 # provision-backend will set this path suggested slapd command line / fedorads.inf
1033 ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1035 # only install a new shares config db if there is none
1036 if not os.path.exists(paths.shareconf):
1037 message("Setting up share.ldb")
1038 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1039 credentials=credentials, lp=lp)
1040 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1043 message("Setting up secrets.ldb")
1044 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1045 session_info=session_info,
1046 credentials=credentials, lp=lp)
1048 message("Setting up the registry")
1049 setup_registry(paths.hklm, setup_path, session_info,
1050 credentials=credentials, lp=lp)
1052 message("Setting up templates db")
1053 setup_templatesdb(paths.templates, setup_path, session_info=session_info,
1054 credentials=credentials, lp=lp)
1056 message("Setting up idmap db")
1057 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1058 credentials=credentials, lp=lp)
1060 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1061 credentials=credentials, lp=lp, names=names,
1062 message=message,
1063 domainsid=domainsid,
1064 aci=aci, domainguid=domainguid, policyguid=policyguid,
1065 fill=samdb_fill,
1066 adminpass=adminpass, krbtgtpass=krbtgtpass,
1067 invocationid=invocationid,
1068 machinepass=machinepass, dnspass=dnspass,
1069 serverrole=serverrole, ldap_backend=ldap_backend,
1070 ldap_backend_type=ldap_backend_type)
1072 if serverrole == "domain controller":
1073 if paths.netlogon is None:
1074 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1075 message("Please either remove %s or see the template at %s" %
1076 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1077 assert(paths.netlogon is not None)
1079 if paths.sysvol is None:
1080 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1081 message("Please either remove %s or see the template at %s" %
1082 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1083 assert(paths.sysvol is not None)
1085 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1086 "{" + policyguid + "}")
1087 os.makedirs(policy_path, 0755)
1088 open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
1089 os.makedirs(os.path.join(policy_path, "Machine"), 0755)
1090 os.makedirs(os.path.join(policy_path, "User"), 0755)
1091 if not os.path.isdir(paths.netlogon):
1092 os.makedirs(paths.netlogon, 0755)
1094 if samdb_fill == FILL_FULL:
1095 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1096 root_uid=root_uid, nobody_uid=nobody_uid,
1097 users_gid=users_gid, wheel_gid=wheel_gid)
1099 message("Setting up sam.ldb rootDSE marking as synchronized")
1100 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1102 # Only make a zone file on the first DC, it should be replicated with DNS replication
1103 if serverrole == "domain controller":
1104 secrets_ldb = Ldb(paths.secrets, session_info=session_info,
1105 credentials=credentials, lp=lp)
1106 secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
1107 netbiosname=names.netbiosname, domainsid=domainsid,
1108 keytab_path=paths.keytab, samdb_url=paths.samdb,
1109 dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
1110 machinepass=machinepass, dnsdomain=names.dnsdomain)
1112 samdb = SamDB(paths.samdb, session_info=session_info,
1113 credentials=credentials, lp=lp)
1115 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1116 assert isinstance(domainguid, str)
1117 hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
1118 expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
1119 scope=SCOPE_SUBTREE)
1120 assert isinstance(hostguid, str)
1122 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1123 domaindn=names.domaindn, hostip=hostip,
1124 hostip6=hostip6, hostname=names.hostname,
1125 dnspass=dnspass, realm=names.realm,
1126 domainguid=domainguid, hostguid=hostguid)
1128 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1129 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1131 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1132 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1133 keytab_name=paths.dns_keytab)
1134 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1135 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1137 create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
1138 hostname=names.hostname, realm=names.realm)
1139 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1143 # if backend is openldap, terminate slapd after final provision and check its proper termination
1144 if ldap_backend_type == "openldap":
1146 # We look if our "provision-slapd" is still up and running,
1147 # listening with the stored PID on our ldapi_uri.
1148 # first we check with ldapsearch -> rootDSE via ldapi_uri
1149 # if slapd is running
1150 try:
1151 # in this case we got a running slapd listening on our ldapi_uri
1152 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1153 ldapi_db = Ldb(ldapi_uri)
1154 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1155 expression="(objectClass=OpenLDAProotDSE)");
1156 message("LDAP Debug-Output:" + str(search_ol_rootdse))
1158 # now we check if slapds PID file exists AND is identical to our stored
1159 if os.path.exists(paths.slapdpid):
1160 f = open(paths.slapdpid, "r")
1161 p1 = f.read()
1162 f.close()
1163 message("slapd-PID-File found. PID is :" + str(p1))
1164 # validation against stored PID of "provision-slapd".
1165 # is this the slapd we started from provision-backend?
1166 if os.path.exists(paths.ldapdir + "/slapd_provision_pid"):
1167 f = open(paths.ldapdir + "/slapd_provision_pid", "r")
1168 p2 = f.read()
1169 f.close()
1170 message("File from provision-backend with stored PID found. PID is :" + str(p2))
1171 if int(p1) == int(p2):
1172 message("slapd-Process used for provisioning with PID: " + str(p1) + " will now be shut down.")
1173 os.kill(int(p1),SIGTERM)
1174 else:
1175 message("Warning: PIDs are not identical! Locate the active slapd and shut it down before you continue!")
1176 else:
1177 message("Stored PID File could not be found. Sorry. You have to locate the PID and terminate slapd manually.")
1178 else:
1179 message("slapd-PID File could not be found. Sorry. You have to locate the PID and terminate slapd manually.")
1181 # Now verify proper slapd-termination...
1182 try:
1183 # in this case we got still a running slapd listening on our ldapi_uri: bad
1184 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1185 ldapi_db = Ldb(ldapi_uri)
1186 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1187 expression="(objectClass=OpenLDAProotDSE)");
1188 message("slapd seems still to be running and listening to our "+ ldapi_uri + " -socket. Locate an terminate it manually.")
1189 except LdbError, e:
1190 message("slapd-Process used for final provision was properly shut down.")
1191 # in this case we got no running slapd listening on our ldapi_uri: everythings good - do nothing.
1193 except LdbError, e:
1194 # in this case we got no running slapd
1195 message("LDAP Debug-Output:")
1196 print e
1197 message("No running slapd on: " + ldapi_uri + " detected. Maybe final provision is incomplete.")
1199 # end slapd-termination check
1201 # now display slapd_command_file.txt to show how slapd must be started next time
1202 if os.path.exists(paths.ldapdir +"/slapd_command_file.txt"):
1203 message("Use later the following commandline to start slapd, then Samba:")
1204 f = open(paths.ldapdir +"/slapd_command_file.txt", "r")
1205 x = f.read()
1206 f.close()
1207 message(x)
1208 message("This slapd-Commandline is also stored under: " + str(paths.ldapdir) + "/slapd_command_file.txt")
1209 else:
1210 message("Error. slapd-commandline-File could not be found.")
1213 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1214 ldapi_url)
1216 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1218 message("Once the above files are installed, your Samba4 server will be ready to use")
1219 message("Server Role: %s" % serverrole)
1220 message("Hostname: %s" % names.hostname)
1221 message("NetBIOS Domain: %s" % names.domain)
1222 message("DNS Domain: %s" % names.dnsdomain)
1223 message("DOMAIN SID: %s" % str(domainsid))
1224 if samdb_fill == FILL_FULL:
1225 message("Admin password: %s" % adminpass)
1227 result = ProvisionResult()
1228 result.domaindn = domaindn
1229 result.paths = paths
1230 result.lp = lp
1231 result.samdb = samdb
1232 return result
1236 def provision_become_dc(setup_dir=None,
1237 smbconf=None, targetdir=None, realm=None,
1238 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1239 serverdn=None,
1240 domain=None, hostname=None, domainsid=None,
1241 adminpass=None, krbtgtpass=None, domainguid=None,
1242 policyguid=None, invocationid=None, machinepass=None,
1243 dnspass=None, root=None, nobody=None, nogroup=None, users=None,
1244 wheel=None, backup=None, aci=None, serverrole=None,
1245 ldap_backend=None, ldap_backend_type=None, sitename=None):
1247 def message(text):
1248 """print a message if quiet is not set."""
1249 print text
1251 return provision(setup_dir, message, system_session(), None,
1252 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
1253 rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
1254 domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
1257 def setup_db_config(setup_path, dbdir):
1258 """Setup a Berkeley database.
1260 :param setup_path: Setup path function.
1261 :param dbdir: Database directory."""
1262 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1263 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1264 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1265 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1267 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1268 {"LDAPDBDIR": dbdir})
1272 def provision_backend(setup_dir=None, message=None,
1273 smbconf=None, targetdir=None, realm=None,
1274 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1275 domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
1276 ldap_backend_type=None, ldap_backend_port=None,
1277 ol_mmr_urls=None, ol_olc=None,
1278 ol_slapd=None, nosync=False):
1280 def setup_path(file):
1281 return os.path.join(setup_dir, file)
1283 if hostname is None:
1284 hostname = socket.gethostname().split(".")[0].lower()
1286 if root is None:
1287 root = findnss(pwd.getpwnam, ["root"])[0]
1289 if adminpass is None:
1290 adminpass = glue.generate_random_str(12)
1292 if targetdir is not None:
1293 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1294 os.makedirs(os.path.join(targetdir, "etc"))
1295 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1296 elif smbconf is None:
1297 smbconf = param.default_path()
1298 assert smbconf is not None
1300 # only install a new smb.conf if there isn't one there already
1301 if not os.path.exists(smbconf):
1302 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1303 targetdir)
1305 # if openldap-backend was chosen, check if path to slapd was given and exists
1306 if ldap_backend_type == "openldap" and ol_slapd is None:
1307 sys.exit("Warning: OpenLDAP-Backend must be setup with path to slapd (OpenLDAP-Daemon), e.g. --ol-slapd=\"/usr/local/libexec/slapd\"!")
1308 if ldap_backend_type == "openldap" and ol_slapd is not None:
1309 if not os.path.exists(ol_slapd):
1310 message (ol_slapd)
1311 sys.exit("Warning: Given Path to slapd (OpenLDAP-Daemon) does not exist!")
1313 # openldap-online-configuration: validation of olc and slapd
1314 if ol_olc == "yes" and ol_slapd is None:
1315 sys.exit("Warning: OpenLDAP-Online-Configuration cant be setup without path to slapd!")
1318 lp = param.LoadParm()
1319 lp.load(smbconf)
1321 if serverrole is None:
1322 serverrole = lp.get("server role")
1324 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1325 dnsdomain=realm, serverrole=serverrole,
1326 rootdn=rootdn, domaindn=domaindn, configdn=configdn,
1327 schemadn=schemadn)
1329 paths = provision_paths_from_lp(lp, names.dnsdomain)
1331 if not os.path.isdir(paths.ldapdir):
1332 os.makedirs(paths.ldapdir, 0700)
1333 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1334 try:
1335 os.unlink(schemadb_path)
1336 except OSError:
1337 pass
1339 schemadb = SamDB(schemadb_path, lp=lp)
1340 schemadb.transaction_start()
1341 try:
1343 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1345 setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
1346 {"SCHEMADN": names.schemadn,
1347 "ACI": "#",
1349 setup_modify_ldif(schemadb,
1350 setup_path("provision_schema_basedn_modify.ldif"), \
1351 {"SCHEMADN": names.schemadn,
1352 "NETBIOSNAME": names.netbiosname,
1353 "DEFAULTSITE": DEFAULTSITE,
1354 "CONFIGDN": names.configdn,
1355 "SERVERDN": names.serverdn,
1356 "PREFIXMAP_B64": b64encode(prefixmap)
1359 data = load_schema(setup_path, schemadb, names.schemadn, names.netbiosname,
1360 names.configdn, DEFAULTSITE, names.serverdn)
1361 schemadb.add_ldif(data)
1362 except:
1363 schemadb.transaction_cancel()
1364 raise
1365 schemadb.transaction_commit()
1367 if ldap_backend_type == "fedora-ds":
1368 if ldap_backend_port is not None:
1369 serverport = "ServerPort=%d" % ldap_backend_port
1370 else:
1371 serverport = ""
1373 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1374 {"ROOT": root,
1375 "HOSTNAME": hostname,
1376 "DNSDOMAIN": names.dnsdomain,
1377 "LDAPDIR": paths.ldapdir,
1378 "DOMAINDN": names.domaindn,
1379 "LDAPMANAGERDN": names.ldapmanagerdn,
1380 "LDAPMANAGERPASS": adminpass,
1381 "SERVERPORT": serverport})
1383 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1384 {"CONFIGDN": names.configdn,
1385 "SCHEMADN": names.schemadn,
1388 mapping = "schema-map-fedora-ds-1.0"
1389 backend_schema = "99_ad.ldif"
1391 slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
1393 ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
1395 elif ldap_backend_type == "openldap":
1396 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1397 nosync_config = ""
1398 if nosync:
1399 nosync_config = "dbnosync"
1402 attrs = ["linkID", "lDAPDisplayName"]
1403 res = schemadb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1405 memberof_config = "# Generated from schema in %s\n" % schemadb_path
1406 refint_attributes = ""
1407 for i in range (0, len(res)):
1408 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
1409 target = schemadb.searchone(basedn=names.schemadn,
1410 expression=expression,
1411 attribute="lDAPDisplayName",
1412 scope=SCOPE_SUBTREE)
1413 if target is not None:
1414 refint_attributes = refint_attributes + " " + res[i]["lDAPDisplayName"][0]
1416 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1417 { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
1418 "MEMBEROF_ATTR" : str(target) })
1420 refint_config = read_and_sub_file(setup_path("refint.conf"),
1421 { "LINK_ATTRS" : refint_attributes})
1423 res = schemadb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1424 index_config = ""
1425 for i in range (0, len(res)):
1426 index_attr = res[i]["lDAPDisplayName"][0]
1427 if index_attr == "objectGUID":
1428 index_attr = "entryUUID"
1430 index_config += "index " + index_attr + " eq\n"
1432 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1433 mmr_on_config = ""
1434 mmr_replicator_acl = ""
1435 mmr_serverids_config = ""
1436 mmr_syncrepl_schema_config = ""
1437 mmr_syncrepl_config_config = ""
1438 mmr_syncrepl_user_config = ""
1441 if ol_mmr_urls is not None:
1442 # For now, make these equal
1443 mmr_pass = adminpass
1445 url_list=filter(None,ol_mmr_urls.split(' '))
1446 if (len(url_list) == 1):
1447 url_list=filter(None,ol_mmr_urls.split(','))
1450 mmr_on_config = "MirrorMode On"
1451 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1452 serverid=0
1453 for url in url_list:
1454 serverid=serverid+1
1455 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1456 { "SERVERID" : str(serverid),
1457 "LDAPSERVER" : url })
1458 rid=serverid*10
1459 rid=rid+1
1460 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1461 { "RID" : str(rid),
1462 "MMRDN": names.schemadn,
1463 "LDAPSERVER" : url,
1464 "MMR_PASSWORD": mmr_pass})
1466 rid=rid+1
1467 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1468 { "RID" : str(rid),
1469 "MMRDN": names.configdn,
1470 "LDAPSERVER" : url,
1471 "MMR_PASSWORD": mmr_pass})
1473 rid=rid+1
1474 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1475 { "RID" : str(rid),
1476 "MMRDN": names.domaindn,
1477 "LDAPSERVER" : url,
1478 "MMR_PASSWORD": mmr_pass })
1479 # olc = yes?
1480 olc_config_pass = ""
1481 olc_config_acl = ""
1482 olc_syncrepl_config = ""
1483 olc_mmr_config = ""
1484 if ol_olc == "yes":
1485 olc_config_pass += read_and_sub_file(setup_path("olc_pass.conf"),
1486 { "OLC_PW": adminpass })
1487 olc_config_acl += read_and_sub_file(setup_path("olc_acl.conf"),{})
1489 # if olc = yes + mmr = yes, generate cn=config-replication directives
1490 # and olc_seed.lif for the other mmr-servers
1491 if ol_olc == "yes" and ol_mmr_urls is not None:
1492 serverid=0
1493 olc_serverids_config = ""
1494 olc_syncrepl_config = ""
1495 olc_syncrepl_seed_config = ""
1496 olc_mmr_config = ""
1497 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1498 rid=1000
1499 for url in url_list:
1500 serverid=serverid+1
1501 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1502 { "SERVERID" : str(serverid),
1503 "LDAPSERVER" : url })
1505 rid=rid+1
1506 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1507 { "RID" : str(rid),
1508 "LDAPSERVER" : url,
1509 "MMR_PASSWORD": adminpass})
1511 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1512 { "RID" : str(rid),
1513 "LDAPSERVER" : url})
1515 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1516 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1517 "OLC_PW": adminpass,
1518 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1521 # end olc
1523 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1524 {"DNSDOMAIN": names.dnsdomain,
1525 "LDAPDIR": paths.ldapdir,
1526 "DOMAINDN": names.domaindn,
1527 "CONFIGDN": names.configdn,
1528 "SCHEMADN": names.schemadn,
1529 "MEMBEROF_CONFIG": memberof_config,
1530 "MIRRORMODE": mmr_on_config,
1531 "REPLICATOR_ACL": mmr_replicator_acl,
1532 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1533 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1534 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1535 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1536 "OLC_CONFIG_PASS": olc_config_pass,
1537 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1538 "OLC_CONFIG_ACL": olc_config_acl,
1539 "OLC_MMR_CONFIG": olc_mmr_config,
1540 "REFINT_CONFIG": refint_config,
1541 "INDEX_CONFIG": index_config,
1542 "NOSYNC": nosync_config})
1543 setup_file(setup_path("modules.conf"), paths.modulesconf,
1544 {"REALM": names.realm})
1546 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1547 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1548 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1550 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1551 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1553 setup_file(setup_path("cn=samba.ldif"),
1554 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1555 { "UUID": str(uuid.uuid4()),
1556 "LDAPTIME": timestring(int(time.time()))} )
1557 setup_file(setup_path("cn=samba-admin.ldif"),
1558 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1559 {"LDAPADMINPASS_B64": b64encode(adminpass),
1560 "UUID": str(uuid.uuid4()),
1561 "LDAPTIME": timestring(int(time.time()))} )
1563 if ol_mmr_urls is not None:
1564 setup_file(setup_path("cn=replicator.ldif"),
1565 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1566 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1567 "UUID": str(uuid.uuid4()),
1568 "LDAPTIME": timestring(int(time.time()))} )
1571 mapping = "schema-map-openldap-2.3"
1572 backend_schema = "backend-schema.schema"
1574 # now we generate the needed strings to start slapd automatically,
1575 # first ldapi_uri...
1576 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1577 if ldap_backend_port is not None:
1578 server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
1579 else:
1580 server_port_string = ""
1582 # ..and now the slapd-commandline with (optional) mmr and/or olc
1583 if ol_olc != "yes" and ol_mmr_urls is None:
1584 slapdcommand_prov=ol_slapd + " -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri
1585 slapdcommand=ol_slapd + " -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
1587 if ol_olc == "yes" and ol_mmr_urls is None:
1588 slapdcommand_prov=ol_slapd + " -F " + paths.olcdir + " -h " + ldapi_uri
1589 slapdcommand=ol_slapd + " -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://" + names.hostname + "." + names.dnsdomain +":<Chosen PORT (<> 389!)>\""
1591 if ol_olc != "yes" and ol_mmr_urls is not None:
1592 slapdcommand_prov=ol_slapd + " -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri
1593 slapdcommand=ol_slapd + " -f " + paths.ldapdir + "/slapd.conf -h \"" + ldapi_uri + " ldap://" + names.hostname + "." + names.dnsdomain +":<Chosen PORT (<> 389!)>\""
1595 if ol_olc == "yes" and ol_mmr_urls is not None:
1596 slapdcommand_prov=ol_slapd + " -F " + paths.olcdir + " -h " + ldapi_uri
1597 slapdcommand=ol_slapd + " -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://" + names.hostname + "." + names.dnsdomain +":<Chosen PORT (<> 389!)>\""
1599 # now generate file with complete slapd-commandline.
1600 # it is read out later when final provision's done and ol/s4 are ready to be started manually
1601 f = open(paths.ldapdir +"/slapd_command_file.txt", "w")
1602 f.write(str(slapdcommand + "\n"))
1603 f.close()
1605 ldapuser = "--username=samba-admin"
1608 backend_schema_data = schemadb.convert_schema_to_openldap(ldap_backend_type, open(setup_path(mapping), 'r').read())
1609 assert backend_schema_data is not None
1610 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1612 message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
1613 message("Server Role: %s" % serverrole)
1614 message("Hostname: %s" % names.hostname)
1615 message("DNS Domain: %s" % names.dnsdomain)
1616 message("Base DN: %s" % names.domaindn)
1618 if ldap_backend_type == "openldap":
1619 message("LDAP admin user: samba-admin")
1620 else:
1621 message("LDAP admin DN: %s" % names.ldapmanagerdn)
1623 message("LDAP admin password: %s" % adminpass)
1624 if ol_olc == "yes" or ol_mmr_urls is not None:
1625 message("Attention! You are using olc and/or MMR: The slapd-PORT you choose later must be different than 389!")
1627 # if --ol-olc=yes, generate online-configuration in ../private/ldap/slapd.d
1628 if ol_olc == "yes":
1629 if not os.path.isdir(paths.olcdir):
1630 os.makedirs(paths.olcdir, 0770)
1631 paths.olslapd = str(ol_slapd)
1632 olc_command = paths.olslapd + " -Ttest -f " + paths.slapdconf + " -F " + paths.olcdir + " >/dev/null 2>&1"
1633 os.system(olc_command)
1634 os.remove(paths.slapdconf)
1635 # for debugging purposes, remove: ' + ">/dev/null 2>&1" ' from the line 'olc_command =' above
1639 # start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via ldapi_uri
1640 # if another instance of slapd is already running
1641 try:
1642 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
1643 ldapi_db = Ldb(ldapi_uri)
1644 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1645 expression="(objectClass=OpenLDAProotDSE)");
1646 message("LDAP Debug-Output:" + str(search_ol_rootdse))
1647 if os.path.exists(paths.slapdpid):
1648 f = open(paths.slapdpid, "r")
1649 p = f.read()
1650 f.close()
1651 message("Check for slapd-Process with PID: " + str(p) + " and terminate it manually.")
1652 else:
1653 message("slapd-PID File could not be found. Sorry. You have to locate the PID manually.")
1655 sys.exit("Warning: Another slapd Instance seems already running on this host, listening to " + ldapi_uri + ". Please shut it down before you continue. Exiting now.")
1657 except LdbError, e:
1658 message("LDAP Debug-Output:")
1659 print e
1660 message("Ok. - No other slapd-Instance listening on: " + ldapi_uri + ". Starting slapd now for final provision.")
1662 p = subprocess.Popen(slapdcommand_prov, shell=True)
1664 # after startup: store slapd-provision-pid also in a separate file.
1665 # this is needed as long as provision/provision-backend are not fully merged,
1666 # to compare pids before shutting down
1668 # wait for pidfile to be created
1669 time.sleep(3)
1670 if os.path.exists(paths.slapdpid):
1671 f = open(paths.slapdpid, "r")
1672 p = f.read()
1673 f.close()
1674 f = open(paths.ldapdir +"/slapd_provision_pid", "w")
1675 f.write(str(p) + "\n")
1676 f.close()
1677 message("Started slapd for final provisioning with PID: "+ str(p))
1678 else:
1679 message("slapd-PID File could not be found. Sorry")
1681 # done slapd checking + start
1685 assert isinstance(ldap_backend_type, str)
1686 assert isinstance(ldapuser, str)
1687 assert isinstance(adminpass, str)
1688 assert isinstance(names.dnsdomain, str)
1689 assert isinstance(names.domain, str)
1690 assert isinstance(serverrole, str)
1691 args = ["--ldap-backend=ldapi",
1692 "--ldap-backend-type=" + ldap_backend_type,
1693 "--password=" + adminpass,
1694 ldapuser,
1695 "--realm=" + names.dnsdomain,
1696 "--domain=" + names.domain,
1697 "--server-role='" + serverrole + "'"]
1698 message("Now run final provision with: " + " ".join(args))
1702 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1703 """Create a PHP LDAP admin configuration file.
1705 :param path: Path to write the configuration to.
1706 :param setup_path: Function to generate setup paths.
1708 setup_file(setup_path("phpldapadmin-config.php"), path,
1709 {"S4_LDAPI_URI": ldapi_uri})
1712 def create_zone_file(path, setup_path, dnsdomain, domaindn,
1713 hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
1714 """Write out a DNS zone file, from the info in the current database.
1716 :param path: Path of the new zone file.
1717 :param setup_path: Setup path function.
1718 :param dnsdomain: DNS Domain name
1719 :param domaindn: DN of the Domain
1720 :param hostip: Local IPv4 IP
1721 :param hostip6: Local IPv6 IP
1722 :param hostname: Local hostname
1723 :param dnspass: Password for DNS
1724 :param realm: Realm name
1725 :param domainguid: GUID of the domain.
1726 :param hostguid: GUID of the host.
1728 assert isinstance(domainguid, str)
1730 if hostip6 is not None:
1731 hostip6_base_line = " IN AAAA " + hostip6
1732 hostip6_host_line = hostname + " IN AAAA " + hostip6
1733 else:
1734 hostip6_base_line = ""
1735 hostip6_host_line = ""
1737 if hostip is not None:
1738 hostip_base_line = " IN A " + hostip
1739 hostip_host_line = hostname + " IN A " + hostip
1740 else:
1741 hostip_base_line = ""
1742 hostip_host_line = ""
1744 setup_file(setup_path("provision.zone"), path, {
1745 "DNSPASS_B64": b64encode(dnspass),
1746 "HOSTNAME": hostname,
1747 "DNSDOMAIN": dnsdomain,
1748 "REALM": realm,
1749 "HOSTIP_BASE_LINE": hostip_base_line,
1750 "HOSTIP_HOST_LINE": hostip_host_line,
1751 "DOMAINGUID": domainguid,
1752 "DATESTRING": time.strftime("%Y%m%d%H"),
1753 "DEFAULTSITE": DEFAULTSITE,
1754 "HOSTGUID": hostguid,
1755 "HOSTIP6_BASE_LINE": hostip6_base_line,
1756 "HOSTIP6_HOST_LINE": hostip6_host_line,
1760 def create_named_conf(path, setup_path, realm, dnsdomain,
1761 private_dir):
1762 """Write out a file containing zone statements suitable for inclusion in a
1763 named.conf file (including GSS-TSIG configuration).
1765 :param path: Path of the new named.conf file.
1766 :param setup_path: Setup path function.
1767 :param realm: Realm name
1768 :param dnsdomain: DNS Domain name
1769 :param private_dir: Path to private directory
1770 :param keytab_name: File name of DNS keytab file
1773 setup_file(setup_path("named.conf"), path, {
1774 "DNSDOMAIN": dnsdomain,
1775 "REALM": realm,
1776 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1777 "PRIVATE_DIR": private_dir
1780 def create_named_txt(path, setup_path, realm, dnsdomain,
1781 private_dir, keytab_name):
1782 """Write out a file containing zone statements suitable for inclusion in a
1783 named.conf file (including GSS-TSIG configuration).
1785 :param path: Path of the new named.conf file.
1786 :param setup_path: Setup path function.
1787 :param realm: Realm name
1788 :param dnsdomain: DNS Domain name
1789 :param private_dir: Path to private directory
1790 :param keytab_name: File name of DNS keytab file
1793 setup_file(setup_path("named.txt"), path, {
1794 "DNSDOMAIN": dnsdomain,
1795 "REALM": realm,
1796 "DNS_KEYTAB": keytab_name,
1797 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1798 "PRIVATE_DIR": private_dir
1801 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1802 """Write out a file containing zone statements suitable for inclusion in a
1803 named.conf file (including GSS-TSIG configuration).
1805 :param path: Path of the new named.conf file.
1806 :param setup_path: Setup path function.
1807 :param dnsdomain: DNS Domain name
1808 :param hostname: Local hostname
1809 :param realm: Realm name
1812 setup_file(setup_path("krb5.conf"), path, {
1813 "DNSDOMAIN": dnsdomain,
1814 "HOSTNAME": hostname,
1815 "REALM": realm,
1819 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
1820 serverdn):
1821 """Load schema for the SamDB.
1823 :param samdb: Load a schema into a SamDB.
1824 :param setup_path: Setup path function.
1825 :param schemadn: DN of the schema
1826 :param netbiosname: NetBIOS name of the host.
1827 :param configdn: DN of the configuration
1828 :param serverdn: DN of the server
1830 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
1832 schema_data = get_schema_data(setup_path, {"SCHEMADN": schemadn})
1833 schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
1834 schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
1835 check_all_substituted(schema_data)
1836 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
1837 prefixmap = b64encode(prefixmap)
1839 head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
1840 head_data = substitute_var(head_data, {
1841 "SCHEMADN": schemadn,
1842 "NETBIOSNAME": netbiosname,
1843 "CONFIGDN": configdn,
1844 "DEFAULTSITE": sitename,
1845 "PREFIXMAP_B64": prefixmap,
1846 "SERVERDN": serverdn,
1848 check_all_substituted(head_data)
1849 samdb.attach_schema_from_ldif(head_data, schema_data)
1850 return schema_data;
1852 def get_schema_data(setup_path, subst_vars = None):
1853 """Get schema data from the AD schema files instead of schema.ldif.
1855 :param setup_path: Setup path function.
1856 :param subst_vars: Optional variables to substitute in the file.
1858 Returns the schema data after substitution
1859 """
1861 # this data used to be read from schema.ldif
1863 data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
1864 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
1866 if subst_vars is not None:
1867 data = substitute_var(data, subst_vars)
1868 check_all_substituted(data)
1869 return data