2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
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
42 from samba.auth import system_session, admin_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name
45 from samba import check_all_substituted, read_and_sub_file, setup_file
46 from samba.dsdb import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008_R2, ENC_ALL_TYPES
47 from samba.dcerpc import security
48 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
49 from samba.idmap import IDmapDB
50 from samba.ms_display_specifiers import read_ms_ldif
51 from samba.ntacls import setntacl, dsacl2fsacl
52 from samba.ndr import ndr_pack,ndr_unpack
53 from samba.provisionbackend import (
61 from samba.schema import Schema
62 from samba.samdb import SamDB
64 __docformat__ = "restructuredText"
65 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
66 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
69 """Find the setup directory used by provision."""
71 for prefix in [sys.prefix,
72 os.path.join(os.path.dirname(__file__), "../../../..")]:
73 for suffix in ["share/setup", "share/samba/setup", "setup"]:
74 ret = os.path.join(prefix, suffix)
75 if os.path.isdir(ret):
78 dirname = os.path.dirname(__file__)
79 ret = os.path.join(dirname, "../../../setup")
80 if os.path.isdir(ret):
82 raise Exception("Unable to find setup directory.")
84 # descriptors of the naming contexts
85 # hard coded at this point, but will probably be changed when
86 # we enable different fsmo roles
89 def get_config_descriptor(domain_sid):
90 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
91 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
92 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
93 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
94 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
95 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
96 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
97 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
98 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
99 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
100 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
101 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
102 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
103 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
104 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
105 sec = security.descriptor.from_sddl(sddl, domain_sid)
108 def get_domain_descriptor(domain_sid):
109 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
110 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
111 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
112 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
113 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
114 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
115 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
116 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
117 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
118 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
119 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
120 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
121 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
122 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
123 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
124 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
125 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
126 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
127 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
128 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
129 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
130 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
131 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
132 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
133 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
134 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
135 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
136 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
137 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
138 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
139 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
140 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
141 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
142 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
143 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
144 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
145 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
146 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
147 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
150 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
152 "(A;;RPLCLORC;;;ED)" \
153 "(A;;RPLCLORC;;;AU)" \
154 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
155 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
156 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
157 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
158 sec = security.descriptor.from_sddl(sddl, domain_sid)
161 DEFAULTSITE = "Default-First-Site-Name"
162 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
164 class ProvisionPaths(object):
167 self.shareconf = None
178 self.dns_keytab = None
181 self.private_dir = None
184 class ProvisionNames(object):
191 self.ldapmanagerdn = None
192 self.dnsdomain = None
194 self.netbiosname = None
201 def update_provision_usn(samdb, low, high, replace=False):
202 """Update the field provisionUSN in sam.ldb
204 This field is used to track range of USN modified by provision and
206 This value is used afterward by next provision to figure out if
207 the field have been modified since last provision.
209 :param samdb: An LDB object connect to sam.ldb
210 :param low: The lowest USN modified by this upgrade
211 :param high: The highest USN modified by this upgrade
212 :param replace: A boolean indicating if the range should replace any
213 existing one or appended (default)
218 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" % \
219 LAST_PROVISION_USN_ATTRIBUTE, base="",
220 scope=ldb.SCOPE_SUBTREE,
221 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
222 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
225 tab.append("%s-%s" % (low, high))
226 delta = ldb.Message()
227 delta.dn = ldb.Dn(samdb, "@PROVISION")
228 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
229 ldb.FLAG_MOD_REPLACE,
230 LAST_PROVISION_USN_ATTRIBUTE)
234 def set_provision_usn(samdb, low, high):
235 """Set the field provisionUSN in sam.ldb
236 This field is used to track range of USN modified by provision and
238 This value is used afterward by next provision to figure out if
239 the field have been modified since last provision.
241 :param samdb: An LDB object connect to sam.ldb
242 :param low: The lowest USN modified by this upgrade
243 :param high: The highest USN modified by this upgrade"""
245 tab.append("%s-%s" % (low, high))
246 delta = ldb.Message()
247 delta.dn = ldb.Dn(samdb, "@PROVISION")
248 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
250 LAST_PROVISION_USN_ATTRIBUTE)
254 def get_max_usn(samdb,basedn):
255 """ This function return the biggest USN present in the provision
257 :param samdb: A LDB object pointing to the sam.ldb
258 :param basedn: A string containing the base DN of the provision
260 :return: The biggest USN in the provision"""
262 res = samdb.search(expression="objectClass=*",base=basedn,
263 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
264 controls=["search_options:1:2",
265 "server_sort:1:1:uSNChanged",
266 "paged_results:1:1"])
267 return res[0]["uSNChanged"]
269 def get_last_provision_usn(sam):
270 """Get the lastest USN modified by a provision or an upgradeprovision
272 :param sam: An LDB object pointing to the sam.ldb
273 :return an integer corresponding to the highest USN modified by
274 (upgrade)provision, 0 is this value is unknown"""
276 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" % \
277 LAST_PROVISION_USN_ATTRIBUTE,
278 base="", scope=ldb.SCOPE_SUBTREE,
279 attrs=[LAST_PROVISION_USN_ATTRIBUTE])
284 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
285 tab = p.split(str(r))
293 class ProvisionResult(object):
302 def check_install(lp, session_info, credentials):
303 """Check whether the current install seems ok.
305 :param lp: Loadparm context
306 :param session_info: Session information
307 :param credentials: Credentials
309 if lp.get("realm") == "":
310 raise Exception("Realm empty")
311 samdb = Ldb(lp.get("sam database"), session_info=session_info,
312 credentials=credentials, lp=lp)
313 if len(samdb.search("(cn=Administrator)")) != 1:
314 raise ProvisioningError("No administrator account found")
317 def findnss(nssfn, names):
318 """Find a user or group from a list of possibilities.
320 :param nssfn: NSS Function to try (should raise KeyError if not found)
321 :param names: Names to check.
322 :return: Value return by first names list.
329 raise KeyError("Unable to find user/group in %r" % names)
332 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
333 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
336 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
337 """Setup a ldb in the private dir.
339 :param ldb: LDB file to import data into
340 :param ldif_path: Path of the LDIF file to load
341 :param subst_vars: Optional variables to subsitute in LDIF.
342 :param nocontrols: Optional list of controls, can be None for no controls
344 assert isinstance(ldif_path, str)
345 data = read_and_sub_file(ldif_path, subst_vars)
346 ldb.add_ldif(data, controls)
349 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
350 """Modify a ldb in the private dir.
352 :param ldb: LDB object.
353 :param ldif_path: LDIF file path.
354 :param subst_vars: Optional dictionary with substitution variables.
356 data = read_and_sub_file(ldif_path, subst_vars)
357 ldb.modify_ldif(data, controls)
360 def setup_ldb(ldb, ldif_path, subst_vars):
361 """Import a LDIF a file into a LDB handle, optionally substituting variables.
363 :note: Either all LDIF data will be added or none (using transactions).
365 :param ldb: LDB file to import into.
366 :param ldif_path: Path to the LDIF file.
367 :param subst_vars: Dictionary with substitution variables.
369 assert ldb is not None
370 ldb.transaction_start()
372 setup_add_ldif(ldb, ldif_path, subst_vars)
374 ldb.transaction_cancel()
377 ldb.transaction_commit()
380 def provision_paths_from_lp(lp, dnsdomain):
381 """Set the default paths for provisioning.
383 :param lp: Loadparm context.
384 :param dnsdomain: DNS Domain name
386 paths = ProvisionPaths()
387 paths.private_dir = lp.get("private dir")
389 # This is stored without path prefix for the "privateKeytab" attribute in
390 # "secrets_dns.ldif".
391 paths.dns_keytab = "dns.keytab"
393 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
394 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
395 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
396 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
397 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
398 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
399 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
400 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
401 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
402 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
403 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
404 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
405 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
406 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
407 paths.phpldapadminconfig = os.path.join(paths.private_dir,
408 "phpldapadmin-config.php")
409 paths.hklm = "hklm.ldb"
410 paths.hkcr = "hkcr.ldb"
411 paths.hkcu = "hkcu.ldb"
412 paths.hku = "hku.ldb"
413 paths.hkpd = "hkpd.ldb"
414 paths.hkpt = "hkpt.ldb"
415 paths.sysvol = lp.get("path", "sysvol")
416 paths.netlogon = lp.get("path", "netlogon")
417 paths.smbconf = lp.configfile
421 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
422 serverrole=None, rootdn=None, domaindn=None, configdn=None,
423 schemadn=None, serverdn=None, sitename=None):
424 """Guess configuration settings to use."""
427 hostname = socket.gethostname().split(".")[0]
429 netbiosname = lp.get("netbios name")
430 if netbiosname is None:
431 netbiosname = hostname
432 assert netbiosname is not None
433 netbiosname = netbiosname.upper()
434 if not valid_netbios_name(netbiosname):
435 raise InvalidNetbiosName(netbiosname)
437 if dnsdomain is None:
438 dnsdomain = lp.get("realm")
439 if dnsdomain is None or dnsdomain == "":
440 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
442 dnsdomain = dnsdomain.lower()
444 if serverrole is None:
445 serverrole = lp.get("server role")
446 if serverrole is None:
447 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
449 serverrole = serverrole.lower()
451 realm = dnsdomain.upper()
453 if lp.get("realm") == "":
454 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
456 if lp.get("realm").upper() != realm:
457 raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
459 if lp.get("server role").lower() != serverrole:
460 raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role").upper(), serverrole, lp.configfile))
462 if serverrole == "domain controller":
464 # This will, for better or worse, default to 'WORKGROUP'
465 domain = lp.get("workgroup")
466 domain = domain.upper()
468 if lp.get("workgroup").upper() != domain:
469 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
472 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
476 domaindn = "DC=" + netbiosname
478 if not valid_netbios_name(domain):
479 raise InvalidNetbiosName(domain)
481 if hostname.upper() == realm:
482 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
483 if netbiosname == realm:
484 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
486 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
492 configdn = "CN=Configuration," + rootdn
494 schemadn = "CN=Schema," + configdn
499 names = ProvisionNames()
500 names.rootdn = rootdn
501 names.domaindn = domaindn
502 names.configdn = configdn
503 names.schemadn = schemadn
504 names.ldapmanagerdn = "CN=Manager," + rootdn
505 names.dnsdomain = dnsdomain
506 names.domain = domain
508 names.netbiosname = netbiosname
509 names.hostname = hostname
510 names.sitename = sitename
511 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
516 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
517 targetdir, sid_generator="internal", eadb=False):
518 """Create a new smb.conf file based on a couple of basic settings.
520 assert smbconf is not None
522 hostname = socket.gethostname().split(".")[0]
523 netbiosname = hostname.upper()
525 if serverrole is None:
526 serverrole = "standalone"
528 assert serverrole in ("domain controller", "member server", "standalone")
529 if serverrole == "domain controller":
531 elif serverrole == "member server":
532 smbconfsuffix = "member"
533 elif serverrole == "standalone":
534 smbconfsuffix = "standalone"
536 if sid_generator is None:
537 sid_generator = "internal"
539 assert domain is not None
540 domain = domain.upper()
542 assert realm is not None
543 realm = realm.upper()
545 default_lp = samba.param.LoadParm()
546 #Load non-existant file
547 if os.path.exists(smbconf):
548 default_lp.load(smbconf)
550 if targetdir is not None:
551 privdir = os.path.join(targetdir, "private")
553 privdir = default_lp.get("private dir")
554 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir, "eadb.tdb"))
558 if targetdir is not None:
559 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
560 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
562 default_lp.set("lock dir", os.path.abspath(targetdir))
567 if sid_generator == "internal":
568 sid_generator_line = ""
570 sid_generator_line = "sid generator = " + sid_generator
572 used_setup_dir = setup_path("")
573 default_setup_dir = default_lp.get("setup directory")
575 if used_setup_dir != default_setup_dir:
576 setupdir_line = "setup directory = %s" % used_setup_dir
577 default_lp.set("setup directory", used_setup_dir)
579 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
580 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
582 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
584 "NETBIOS_NAME": netbiosname,
587 "SERVERROLE": serverrole,
588 "NETLOGONPATH": netlogon,
589 "SYSVOLPATH": sysvol,
590 "SETUPDIRECTORY_LINE": setupdir_line,
591 "SIDGENERATOR_LINE": sid_generator_line,
592 "PRIVATEDIR_LINE": privatedir_line,
593 "LOCKDIR_LINE": lockdir_line,
594 "POSIXEADB_LINE": posixeadb_line
598 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
599 users_gid, wheel_gid):
600 """setup reasonable name mappings for sam names to unix names.
602 :param samdb: SamDB object.
603 :param idmap: IDmap db object.
604 :param sid: The domain sid.
605 :param domaindn: The domain DN.
606 :param root_uid: uid of the UNIX root user.
607 :param nobody_uid: uid of the UNIX nobody user.
608 :param users_gid: gid of the UNIX users group.
609 :param wheel_gid: gid of the UNIX wheel group."""
610 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
611 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
613 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
614 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
617 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
618 provision_backend, names, schema, serverrole,
620 """Setup the partitions for the SAM database.
622 Alternatively, provision() may call this, and then populate the database.
624 :note: This will wipe the Sam Database!
626 :note: This function always removes the local SAM LDB file. The erase
627 parameter controls whether to erase the existing data, which
628 may not be stored locally but in LDAP.
631 assert session_info is not None
633 # We use options=["modules:"] to stop the modules loading - we
634 # just want to wipe and re-initialise the database, not start it up
637 os.unlink(samdb_path)
641 samdb = Ldb(url=samdb_path, session_info=session_info,
642 lp=lp, options=["modules:"])
644 ldap_backend_line = "# No LDAP backend"
645 if provision_backend.type is not "ldb":
646 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
648 samdb.transaction_start()
650 logger.info("Setting up sam.ldb partitions and settings")
651 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
652 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
653 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
654 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
655 "LDAP_BACKEND_LINE": ldap_backend_line,
659 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
660 "BACKEND_TYPE": provision_backend.type,
661 "SERVER_ROLE": serverrole
664 logger.info("Setting up sam.ldb rootDSE")
665 setup_samdb_rootdse(samdb, setup_path, names)
667 samdb.transaction_cancel()
670 samdb.transaction_commit()
673 def secretsdb_self_join(secretsdb, domain,
674 netbiosname, machinepass, domainsid=None,
675 realm=None, dnsdomain=None,
677 key_version_number=1,
678 secure_channel_type=SEC_CHAN_WKSTA):
679 """Add domain join-specific bits to a secrets database.
681 :param secretsdb: Ldb Handle to the secrets database
682 :param machinepass: Machine password
684 attrs=["whenChanged",
691 #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
692 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
693 msg["secureChannelType"] = [str(secure_channel_type)]
694 msg["objectClass"] = ["top", "primaryDomain"]
695 if realm is not None:
696 if dnsdomain is None:
697 dnsdomain = realm.lower()
698 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
699 msg["realm"] = [realm]
700 msg["saltPrincipal"] = ["host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())]
701 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
702 msg["privateKeytab"] = ["secrets.keytab"]
705 msg["secret"] = [machinepass]
706 msg["samAccountName"] = ["%s$" % netbiosname]
707 msg["secureChannelType"] = [str(secure_channel_type)]
708 if domainsid is not None:
709 msg["objectSid"] = [ndr_pack(domainsid)]
711 # This complex expression tries to ensure that we don't have more
712 # than one record for this SID, realm or netbios domain at a time,
713 # but we don't delete the old record that we are about to modify,
714 # because that would delete the keytab and previous password.
715 res = secretsdb.search(base="cn=Primary Domains",
717 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
718 scope=ldb.SCOPE_ONELEVEL)
721 secretsdb.delete(del_msg.dn)
723 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
726 msg["priorSecret"] = [res[0]["secret"][0]]
727 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
730 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
735 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
741 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
742 secretsdb.modify(msg)
743 secretsdb.rename(res[0].dn, msg.dn)
748 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
750 dns_keytab_path, dnspass):
751 """Add DNS specific bits to a secrets database.
753 :param secretsdb: Ldb Handle to the secrets database
754 :param setup_path: Setup path function
755 :param machinepass: Machine password
758 os.unlink(os.path.join(private_dir, dns_keytab_path))
762 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
764 "DNSDOMAIN": dnsdomain,
765 "DNS_KEYTAB": dns_keytab_path,
766 "DNSPASS_B64": b64encode(dnspass),
770 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
771 """Setup the secrets database.
773 :note: This function does not handle exceptions and transaction on purpose,
774 it's up to the caller to do this job.
776 :param path: Path to the secrets database.
777 :param setup_path: Get the path to a setup file.
778 :param session_info: Session info.
779 :param credentials: Credentials
780 :param lp: Loadparm context
781 :return: LDB handle for the created secrets database
783 if os.path.exists(path):
785 secrets_ldb = Ldb(path, session_info=session_info,
788 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
789 secrets_ldb = Ldb(path, session_info=session_info,
791 secrets_ldb.transaction_start()
793 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
795 if backend_credentials is not None and backend_credentials.authentication_requested():
796 if backend_credentials.get_bind_dn() is not None:
797 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
798 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
799 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
802 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
803 "LDAPADMINUSER": backend_credentials.get_username(),
804 "LDAPADMINREALM": backend_credentials.get_realm(),
805 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
810 secrets_ldb.transaction_cancel()
813 def setup_privileges(path, setup_path, session_info, lp):
814 """Setup the privileges database.
816 :param path: Path to the privileges database.
817 :param setup_path: Get the path to a setup file.
818 :param session_info: Session info.
819 :param credentials: Credentials
820 :param lp: Loadparm context
821 :return: LDB handle for the created secrets database
823 if os.path.exists(path):
825 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
826 privilege_ldb.erase()
827 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
830 def setup_registry(path, setup_path, session_info, lp):
831 """Setup the registry.
833 :param path: Path to the registry database
834 :param setup_path: Function that returns the path to a setup.
835 :param session_info: Session information
836 :param credentials: Credentials
837 :param lp: Loadparm context
839 reg = samba.registry.Registry()
840 hive = samba.registry.open_ldb(path, session_info=session_info,
842 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
843 provision_reg = setup_path("provision.reg")
844 assert os.path.exists(provision_reg)
845 reg.diff_apply(provision_reg)
848 def setup_idmapdb(path, setup_path, session_info, lp):
849 """Setup the idmap database.
851 :param path: path to the idmap database
852 :param setup_path: Function that returns a path to a setup file
853 :param session_info: Session information
854 :param credentials: Credentials
855 :param lp: Loadparm context
857 if os.path.exists(path):
860 idmap_ldb = IDmapDB(path, session_info=session_info,
864 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
868 def setup_samdb_rootdse(samdb, setup_path, names):
869 """Setup the SamDB rootdse.
871 :param samdb: Sam Database handle
872 :param setup_path: Obtain setup path
874 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
875 "SCHEMADN": names.schemadn,
876 "NETBIOSNAME": names.netbiosname,
877 "DNSDOMAIN": names.dnsdomain,
878 "REALM": names.realm,
879 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
880 "DOMAINDN": names.domaindn,
881 "ROOTDN": names.rootdn,
882 "CONFIGDN": names.configdn,
883 "SERVERDN": names.serverdn,
887 def setup_self_join(samdb, names,
888 machinepass, dnspass,
889 domainsid, next_rid, invocationid, setup_path,
890 policyguid, policyguid_dc, domainControllerFunctionality,
892 """Join a host to its own domain."""
893 assert isinstance(invocationid, str)
894 if ntdsguid is not None:
895 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
898 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
899 "CONFIGDN": names.configdn,
900 "SCHEMADN": names.schemadn,
901 "DOMAINDN": names.domaindn,
902 "SERVERDN": names.serverdn,
903 "INVOCATIONID": invocationid,
904 "NETBIOSNAME": names.netbiosname,
905 "DEFAULTSITE": names.sitename,
906 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
907 "MACHINEPASS_B64": b64encode(machinepass),
908 "REALM": names.realm,
909 "DOMAIN": names.domain,
910 "DOMAINSID": str(domainsid),
911 "DCRID": str(next_rid),
912 "DNSDOMAIN": names.dnsdomain,
913 "SAMBA_VERSION_STRING": version,
914 "NTDSGUID": ntdsguid_line,
915 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
917 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
918 "POLICYGUID": policyguid,
919 "POLICYGUID_DC": policyguid_dc,
920 "DNSDOMAIN": names.dnsdomain,
921 "DOMAINSID": str(domainsid),
922 "DOMAINDN": names.domaindn})
924 # add the NTDSGUID based SPNs
925 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
926 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
927 expression="", scope=ldb.SCOPE_BASE)
928 assert isinstance(names.ntdsguid, str)
930 # Setup fSMORoleOwner entries to point at the newly created DC entry
931 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
932 "DOMAIN": names.domain,
933 "DNSDOMAIN": names.dnsdomain,
934 "DOMAINDN": names.domaindn,
935 "CONFIGDN": names.configdn,
936 "SCHEMADN": names.schemadn,
937 "DEFAULTSITE": names.sitename,
938 "SERVERDN": names.serverdn,
939 "NETBIOSNAME": names.netbiosname,
940 "NTDSGUID": names.ntdsguid,
941 "RIDALLOCATIONSTART": str(next_rid + 100),
942 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
945 # This is partially Samba4 specific and should be replaced by the correct
947 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
948 "DNSDOMAIN": names.dnsdomain,
949 "DOMAINDN": names.domaindn,
950 "DNSPASS_B64": b64encode(dnspass),
953 def getpolicypath(sysvolpath, dnsdomain, guid):
954 """Return the physical path of policy given its guid.
956 :param sysvolpath: Path to the sysvol folder
957 :param dnsdomain: DNS name of the AD domain
958 :param guid: The GUID of the policy
959 :return: A string with the complete path to the policy folder
964 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
967 def create_gpo_struct(policy_path):
968 if not os.path.exists(policy_path):
969 os.makedirs(policy_path, 0755)
970 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
971 "[General]\r\nVersion=65543")
972 p = os.path.join(policy_path, "MACHINE")
973 if not os.path.exists(p):
975 p = os.path.join(policy_path, "USER")
976 if not os.path.exists(p):
980 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
981 """Create the default GPO for a domain
983 :param sysvolpath: Physical path for the sysvol folder
984 :param dnsdomain: DNS domain name of the AD domain
985 :param policyguid: GUID of the default domain policy
986 :param policyguid_dc: GUID of the default domain controler policy
989 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
990 create_gpo_struct(policy_path)
992 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
993 create_gpo_struct(policy_path)
996 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
997 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
998 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
999 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1001 """Setup a complete SAM Database.
1003 :note: This will wipe the main SAM database file!
1007 # Provision does not make much sense values larger than 1000000000
1008 # as the upper range of the rIDAvailablePool is 1073741823 and
1009 # we don't want to create a domain that cannot allocate rids.
1010 if next_rid < 1000 or next_rid > 1000000000:
1011 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1012 error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
1013 raise ProvisioningError(error)
1015 # ATTENTION: Do NOT change these default values without discussion with the
1016 # team and/or release manager. They have a big impact on the whole program!
1017 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1019 if dom_for_fun_level is None:
1020 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1022 if dom_for_fun_level > domainControllerFunctionality:
1023 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008_R2). This won't work!")
1025 domainFunctionality = dom_for_fun_level
1026 forestFunctionality = dom_for_fun_level
1028 # Also wipes the database
1029 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1030 provision_backend=provision_backend, session_info=session_info,
1031 names=names, serverrole=serverrole, schema=schema)
1034 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1036 # Load the database, but don's load the global schema and don't connect quite yet
1037 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1038 credentials=provision_backend.credentials, lp=lp, global_schema=False,
1041 logger.info("Pre-loading the Samba 4 and AD schema")
1043 # Load the schema from the one we computed earlier
1044 samdb.set_schema(schema)
1046 # And now we can connect to the DB - the schema won't be loaded from the DB
1049 if fill == FILL_DRS:
1052 samdb.transaction_start()
1054 # Set the domain functionality levels onto the database.
1055 # Various module (the password_hash module in particular) need
1056 # to know what level of AD we are emulating.
1058 # These will be fixed into the database via the database
1059 # modifictions below, but we need them set from the start.
1060 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1061 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1062 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1064 samdb.set_domain_sid(str(domainsid))
1065 samdb.set_invocation_id(invocationid)
1066 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1068 logger.info("Adding DomainDN: %s" % names.domaindn)
1070 #impersonate domain admin
1071 admin_session_info = admin_session(lp, str(domainsid))
1072 samdb.set_session_info(admin_session_info)
1073 if domainguid is not None:
1074 domainguid_line = "objectGUID: %s\n-" % domainguid
1076 domainguid_line = ""
1078 descr = b64encode(get_domain_descriptor(domainsid))
1079 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1080 "DOMAINDN": names.domaindn,
1081 "DOMAINGUID": domainguid_line,
1086 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1087 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1088 "DOMAINSID": str(domainsid),
1089 "NEXTRID": str(next_rid),
1090 "SCHEMADN": names.schemadn,
1091 "NETBIOSNAME": names.netbiosname,
1092 "DEFAULTSITE": names.sitename,
1093 "CONFIGDN": names.configdn,
1094 "SERVERDN": names.serverdn,
1095 "POLICYGUID": policyguid,
1096 "DOMAINDN": names.domaindn,
1097 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1098 "SAMBA_VERSION_STRING": version
1101 logger.info("Adding configuration container")
1102 descr = b64encode(get_config_descriptor(domainsid))
1103 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1104 "CONFIGDN": names.configdn,
1105 "DESCRIPTOR": descr,
1108 # The LDIF here was created when the Schema object was constructed
1109 logger.info("Setting up sam.ldb schema")
1110 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1111 samdb.modify_ldif(schema.schema_dn_modify)
1112 samdb.write_prefixes_from_schema()
1113 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1114 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1115 {"SCHEMADN": names.schemadn})
1117 logger.info("Reopening sam.ldb with new schema")
1119 samdb.transaction_cancel()
1122 samdb.transaction_commit()
1124 samdb = SamDB(session_info=admin_session_info,
1125 credentials=provision_backend.credentials, lp=lp,
1126 global_schema=False, am_rodc=am_rodc)
1128 samdb.transaction_start()
1130 samdb.invocation_id = invocationid
1132 logger.info("Setting up sam.ldb configuration data")
1133 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1134 "CONFIGDN": names.configdn,
1135 "NETBIOSNAME": names.netbiosname,
1136 "DEFAULTSITE": names.sitename,
1137 "DNSDOMAIN": names.dnsdomain,
1138 "DOMAIN": names.domain,
1139 "SCHEMADN": names.schemadn,
1140 "DOMAINDN": names.domaindn,
1141 "SERVERDN": names.serverdn,
1142 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1143 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
1146 logger.info("Setting up display specifiers")
1147 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1148 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1149 check_all_substituted(display_specifiers_ldif)
1150 samdb.add_ldif(display_specifiers_ldif)
1152 logger.info("Adding users container")
1153 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1154 "DOMAINDN": names.domaindn})
1155 logger.info("Modifying users container")
1156 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1157 "DOMAINDN": names.domaindn})
1158 logger.info("Adding computers container")
1159 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1160 "DOMAINDN": names.domaindn})
1161 logger.info("Modifying computers container")
1162 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1163 "DOMAINDN": names.domaindn})
1164 logger.info("Setting up sam.ldb data")
1165 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1166 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1167 "DOMAINDN": names.domaindn,
1168 "NETBIOSNAME": names.netbiosname,
1169 "DEFAULTSITE": names.sitename,
1170 "CONFIGDN": names.configdn,
1171 "SERVERDN": names.serverdn,
1172 "RIDAVAILABLESTART": str(next_rid + 600),
1173 "POLICYGUID_DC": policyguid_dc
1176 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1177 "DOMAINDN": names.domaindn})
1179 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1180 "CONFIGDN": names.configdn,
1181 "SCHEMADN": names.schemadn})
1182 if fill == FILL_FULL:
1183 logger.info("Setting up sam.ldb users and groups")
1184 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1185 "DOMAINDN": names.domaindn,
1186 "DOMAINSID": str(domainsid),
1187 "CONFIGDN": names.configdn,
1188 "ADMINPASS_B64": b64encode(adminpass),
1189 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1192 logger.info("Setting up self join")
1193 setup_self_join(samdb, names=names, invocationid=invocationid,
1195 machinepass=machinepass,
1196 domainsid=domainsid,
1198 policyguid=policyguid,
1199 policyguid_dc=policyguid_dc,
1200 setup_path=setup_path,
1201 domainControllerFunctionality=domainControllerFunctionality,
1204 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1205 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1206 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1207 assert isinstance(names.ntdsguid, str)
1209 samdb.transaction_cancel()
1212 samdb.transaction_commit()
1217 FILL_NT4SYNC = "NT4SYNC"
1219 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1220 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1222 def set_dir_acl(path, acl, lp, domsid):
1223 setntacl(lp, path, acl, domsid)
1224 for root, dirs, files in os.walk(path, topdown=False):
1226 setntacl(lp, os.path.join(root, name), acl, domsid)
1228 setntacl(lp, os.path.join(root, name), acl, domsid)
1231 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1232 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1235 :param sysvol: Physical path for the sysvol folder
1236 :param dnsdomain: The DNS name of the domain
1237 :param domainsid: The SID of the domain
1238 :param domaindn: The DN of the domain (ie. DC=...)
1239 :param samdb: An LDB object on the SAM db
1240 :param lp: an LP object
1243 # Set ACL for GPO root folder
1244 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1245 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1247 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1248 attrs=["cn", "nTSecurityDescriptor"],
1249 expression="", scope=ldb.SCOPE_ONELEVEL)
1252 acl = ndr_unpack(security.descriptor,
1253 str(policy["nTSecurityDescriptor"])).as_sddl()
1254 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1255 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1258 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1260 """Set the ACL for the sysvol share and the subfolders
1262 :param samdb: An LDB object on the SAM db
1263 :param netlogon: Physical path for the netlogon folder
1264 :param sysvol: Physical path for the sysvol folder
1265 :param gid: The GID of the "Domain adminstrators" group
1266 :param domainsid: The SID of the domain
1267 :param dnsdomain: The DNS name of the domain
1268 :param domaindn: The DN of the domain (ie. DC=...)
1272 os.chown(sysvol,-1,gid)
1278 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1279 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1280 for root, dirs, files in os.walk(sysvol, topdown=False):
1283 os.chown(os.path.join(root, name), -1, gid)
1284 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1287 os.chown(os.path.join(root, name), -1, gid)
1288 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1290 # Set acls on Policy folder and policies folders
1291 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1294 def provision(setup_dir, logger, session_info,
1295 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1297 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1299 domain=None, hostname=None, hostip=None, hostip6=None,
1300 domainsid=None, next_rid=1000,
1301 adminpass=None, ldapadminpass=None,
1302 krbtgtpass=None, domainguid=None,
1303 policyguid=None, policyguid_dc=None, invocationid=None,
1304 machinepass=None, ntdsguid=None,
1305 dnspass=None, root=None, nobody=None, users=None,
1306 wheel=None, backup=None, aci=None, serverrole=None,
1307 dom_for_fun_level=None,
1308 ldap_backend_extra_port=None, backend_type=None,
1310 ol_mmr_urls=None, ol_olc=None,
1311 setup_ds_path=None, slapd_path=None, nosync=False,
1312 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1315 :note: caution, this wipes all existing data!
1318 def setup_path(file):
1319 return os.path.join(setup_dir, file)
1321 if domainsid is None:
1322 domainsid = security.random_sid()
1324 domainsid = security.dom_sid(domainsid)
1326 # create/adapt the group policy GUIDs
1327 # Default GUID for default policy are described at
1328 # "How Core Group Policy Works"
1329 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1330 if policyguid is None:
1331 policyguid = DEFAULT_POLICY_GUID
1332 policyguid = policyguid.upper()
1333 if policyguid_dc is None:
1334 policyguid_dc = DEFAULT_DC_POLICY_GUID
1335 policyguid_dc = policyguid_dc.upper()
1337 if adminpass is None:
1338 adminpass = samba.generate_random_password(12, 32)
1339 if krbtgtpass is None:
1340 krbtgtpass = samba.generate_random_password(128, 255)
1341 if machinepass is None:
1342 machinepass = samba.generate_random_password(128, 255)
1344 dnspass = samba.generate_random_password(128, 255)
1345 if ldapadminpass is None:
1346 #Make a new, random password between Samba and it's LDAP server
1347 ldapadminpass=samba.generate_random_password(128, 255)
1349 if backend_type is None:
1350 backend_type = "ldb"
1352 sid_generator = "internal"
1353 if backend_type == "fedora-ds":
1354 sid_generator = "backend"
1356 root_uid = findnss_uid([root or "root"])
1357 nobody_uid = findnss_uid([nobody or "nobody"])
1358 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1360 wheel_gid = findnss_gid(["wheel", "adm"])
1362 wheel_gid = findnss_gid([wheel])
1364 bind_gid = findnss_gid(["bind", "named"])
1368 if targetdir is not None:
1369 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1370 elif smbconf is None:
1371 smbconf = samba.param.default_path()
1372 if not os.path.exists(os.path.dirname(smbconf)):
1373 os.makedirs(os.path.dirname(smbconf))
1375 # only install a new smb.conf if there isn't one there already
1376 if os.path.exists(smbconf):
1377 # if Samba Team members can't figure out the weird errors
1378 # loading an empty smb.conf gives, then we need to be smarter.
1379 # Pretend it just didn't exist --abartlet
1380 data = open(smbconf, 'r').read()
1381 data = data.lstrip()
1382 if data is None or data == "":
1383 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1384 serverrole, targetdir, sid_generator, useeadb)
1386 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1387 targetdir, sid_generator, useeadb)
1389 lp = samba.param.LoadParm()
1392 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1393 dnsdomain=realm, serverrole=serverrole,
1394 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1395 serverdn=serverdn, sitename=sitename)
1397 paths = provision_paths_from_lp(lp, names.dnsdomain)
1399 paths.bind_gid = bind_gid
1402 hostips = samba.interface_ips(lp, False)
1403 if len(hostips) == 0:
1404 logger.warning("No external IPv4 address has been found. Using loopback.")
1405 hostip = '127.0.0.1'
1408 if len(hostips) > 1:
1409 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1413 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1416 if hostip6 == '::1' and ip[-1][0] != '::1':
1418 except socket.gaierror, (socket.EAI_NODATA, msg):
1421 if serverrole is None:
1422 serverrole = lp.get("server role")
1424 assert serverrole in ("domain controller", "member server", "standalone")
1425 if invocationid is None:
1426 invocationid = str(uuid.uuid4())
1428 if not os.path.exists(paths.private_dir):
1429 os.mkdir(paths.private_dir)
1430 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1431 os.mkdir(os.path.join(paths.private_dir, "tls"))
1433 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1435 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1436 serverdn=names.serverdn)
1438 if backend_type == "ldb":
1439 provision_backend = LDBBackend(backend_type,
1440 paths=paths, setup_path=setup_path,
1441 lp=lp, credentials=credentials,
1444 elif backend_type == "existing":
1445 provision_backend = ExistingBackend(backend_type,
1446 paths=paths, setup_path=setup_path,
1447 lp=lp, credentials=credentials,
1450 ldapi_url=ldapi_url)
1451 elif backend_type == "fedora-ds":
1452 provision_backend = FDSBackend(backend_type,
1453 paths=paths, setup_path=setup_path,
1454 lp=lp, credentials=credentials,
1457 domainsid=domainsid,
1460 ldapadminpass=ldapadminpass,
1461 slapd_path=slapd_path,
1462 ldap_backend_extra_port=ldap_backend_extra_port,
1463 ldap_dryrun_mode=ldap_dryrun_mode,
1465 setup_ds_path=setup_ds_path)
1466 elif backend_type == "openldap":
1467 provision_backend = OpenLDAPBackend(backend_type,
1468 paths=paths, setup_path=setup_path,
1469 lp=lp, credentials=credentials,
1472 domainsid=domainsid,
1475 ldapadminpass=ldapadminpass,
1476 slapd_path=slapd_path,
1477 ldap_backend_extra_port=ldap_backend_extra_port,
1478 ldap_dryrun_mode=ldap_dryrun_mode,
1479 ol_mmr_urls=ol_mmr_urls,
1482 raise ValueError("Unknown LDAP backend type selected")
1484 provision_backend.init()
1485 provision_backend.start()
1487 # only install a new shares config db if there is none
1488 if not os.path.exists(paths.shareconf):
1489 logger.info("Setting up share.ldb")
1490 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1492 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1494 logger.info("Setting up secrets.ldb")
1495 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1496 session_info=session_info,
1497 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1500 logger.info("Setting up the registry")
1501 setup_registry(paths.hklm, setup_path, session_info,
1504 logger.info("Setting up the privileges database")
1505 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1507 logger.info("Setting up idmap db")
1508 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1511 logger.info("Setting up SAM db")
1512 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1513 provision_backend, lp, names,
1515 domainsid=domainsid,
1516 schema=schema, domainguid=domainguid,
1517 policyguid=policyguid, policyguid_dc=policyguid_dc,
1519 adminpass=adminpass, krbtgtpass=krbtgtpass,
1520 invocationid=invocationid,
1521 machinepass=machinepass, dnspass=dnspass,
1522 ntdsguid=ntdsguid, serverrole=serverrole,
1523 dom_for_fun_level=dom_for_fun_level,
1524 am_rodc=am_rodc, next_rid=next_rid)
1526 if serverrole == "domain controller":
1527 if paths.netlogon is None:
1528 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1529 logger.info("Please either remove %s or see the template at %s" %
1530 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1531 assert paths.netlogon is not None
1533 if paths.sysvol is None:
1534 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1535 " are configuring a DC.")
1536 logger.info("Please either remove %s or see the template at %s" %
1537 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1538 assert paths.sysvol is not None
1540 if not os.path.isdir(paths.netlogon):
1541 os.makedirs(paths.netlogon, 0755)
1543 if samdb_fill == FILL_FULL:
1544 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1545 root_uid=root_uid, nobody_uid=nobody_uid,
1546 users_gid=users_gid, wheel_gid=wheel_gid)
1548 if serverrole == "domain controller":
1549 # Set up group policies (domain policy and domain controller policy)
1550 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1551 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1552 domainsid, names.dnsdomain, names.domaindn, lp)
1554 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1555 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1557 secretsdb_self_join(secrets_ldb, domain=names.domain,
1559 dnsdomain=names.dnsdomain,
1560 netbiosname=names.netbiosname,
1561 domainsid=domainsid,
1562 machinepass=machinepass,
1563 secure_channel_type=SEC_CHAN_BDC)
1565 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1566 # In future, this might be determined from some configuration
1567 kerberos_enctypes = str(ENC_ALL_TYPES)
1570 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1571 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
1572 flags=ldb.FLAG_MOD_REPLACE,
1573 name="msDS-SupportedEncryptionTypes")
1575 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1576 # It might be that this attribute does not exist in this schema
1580 if serverrole == "domain controller":
1581 secretsdb_setup_dns(secrets_ldb, setup_path,
1583 realm=names.realm, dnsdomain=names.dnsdomain,
1584 dns_keytab_path=paths.dns_keytab,
1587 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1588 assert isinstance(domainguid, str)
1590 # Only make a zone file on the first DC, it should be replicated
1591 # with DNS replication
1592 create_zone_file(lp, logger, paths, targetdir, setup_path,
1593 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1594 hostname=names.hostname, realm=names.realm,
1595 domainguid=domainguid, ntdsguid=names.ntdsguid)
1597 create_named_conf(paths, setup_path, realm=names.realm,
1598 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1600 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1601 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1602 keytab_name=paths.dns_keytab)
1603 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1604 logger.info("and %s for further documentation required for secure DNS "
1605 "updates", paths.namedtxt)
1607 create_krb5_conf(paths.krb5conf, setup_path,
1608 dnsdomain=names.dnsdomain, hostname=names.hostname,
1610 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1611 "generated at %s", paths.krb5conf)
1613 lastProvisionUSNs = get_last_provision_usn(samdb)
1614 maxUSN = get_max_usn(samdb, str(names.rootdn))
1615 if lastProvisionUSNs is not None:
1616 update_provision_usn(samdb, 0, maxUSN, 1)
1618 set_provision_usn(samdb, 0, maxUSN)
1620 if serverrole == "domain controller":
1621 create_dns_update_list(lp, logger, paths, setup_path)
1623 provision_backend.post_setup()
1624 provision_backend.shutdown()
1626 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1629 secrets_ldb.transaction_cancel()
1632 #Now commit the secrets.ldb to disk
1633 secrets_ldb.transaction_commit()
1635 # the commit creates the dns.keytab, now chown it
1636 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1637 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1639 os.chmod(dns_keytab_path, 0640)
1640 os.chown(dns_keytab_path, -1, paths.bind_gid)
1642 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1646 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1647 paths.phpldapadminconfig)
1649 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1650 logger.info("Server Role: %s" % serverrole)
1651 logger.info("Hostname: %s" % names.hostname)
1652 logger.info("NetBIOS Domain: %s" % names.domain)
1653 logger.info("DNS Domain: %s" % names.dnsdomain)
1654 logger.info("DOMAIN SID: %s" % str(domainsid))
1655 if samdb_fill == FILL_FULL:
1656 logger.info("Admin password: %s" % adminpass)
1657 if provision_backend.type is not "ldb":
1658 if provision_backend.credentials.get_bind_dn() is not None:
1659 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1661 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1663 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1665 if provision_backend.slapd_command_escaped is not None:
1666 # now display slapd_command_file.txt to show how slapd must be started next time
1667 logger.info("Use later the following commandline to start slapd, then Samba:")
1668 logger.info(provision_backend.slapd_command_escaped)
1669 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1670 provision_backend.ldapdir)
1672 result = ProvisionResult()
1673 result.domaindn = domaindn
1674 result.paths = paths
1676 result.samdb = samdb
1680 def provision_become_dc(setup_dir=None,
1681 smbconf=None, targetdir=None, realm=None,
1682 rootdn=None, domaindn=None, schemadn=None,
1683 configdn=None, serverdn=None,
1684 domain=None, hostname=None, domainsid=None,
1685 adminpass=None, krbtgtpass=None, domainguid=None,
1686 policyguid=None, policyguid_dc=None, invocationid=None,
1688 dnspass=None, root=None, nobody=None, users=None,
1689 wheel=None, backup=None, serverrole=None,
1690 ldap_backend=None, ldap_backend_type=None,
1691 sitename=None, debuglevel=1):
1693 logger = logging.getLogger("provision")
1694 samba.set_debug_level(debuglevel)
1696 return provision(setup_dir, logger, system_session(), None,
1697 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1698 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1699 configdn=configdn, serverdn=serverdn, domain=domain,
1700 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1701 machinepass=machinepass, serverrole="domain controller",
1705 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1706 """Create a PHP LDAP admin configuration file.
1708 :param path: Path to write the configuration to.
1709 :param setup_path: Function to generate setup paths.
1711 setup_file(setup_path("phpldapadmin-config.php"), path,
1712 {"S4_LDAPI_URI": ldapi_uri})
1715 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1716 hostip, hostip6, hostname, realm, domainguid,
1718 """Write out a DNS zone file, from the info in the current database.
1720 :param paths: paths object
1721 :param setup_path: Setup path function.
1722 :param dnsdomain: DNS Domain name
1723 :param domaindn: DN of the Domain
1724 :param hostip: Local IPv4 IP
1725 :param hostip6: Local IPv6 IP
1726 :param hostname: Local hostname
1727 :param realm: Realm name
1728 :param domainguid: GUID of the domain.
1729 :param ntdsguid: GUID of the hosts nTDSDSA record.
1731 assert isinstance(domainguid, str)
1733 if hostip6 is not None:
1734 hostip6_base_line = " IN AAAA " + hostip6
1735 hostip6_host_line = hostname + " IN AAAA " + hostip6
1736 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1738 hostip6_base_line = ""
1739 hostip6_host_line = ""
1740 gc_msdcs_ip6_line = ""
1742 if hostip is not None:
1743 hostip_base_line = " IN A " + hostip
1744 hostip_host_line = hostname + " IN A " + hostip
1745 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1747 hostip_base_line = ""
1748 hostip_host_line = ""
1749 gc_msdcs_ip_line = ""
1751 dns_dir = os.path.dirname(paths.dns)
1754 shutil.rmtree(dns_dir, True)
1758 os.mkdir(dns_dir, 0775)
1760 # we need to freeze the zone while we update the contents
1761 if targetdir is None:
1762 rndc = ' '.join(lp.get("rndc command"))
1763 os.system(rndc + " freeze " + lp.get("realm"))
1765 setup_file(setup_path("provision.zone"), paths.dns, {
1766 "HOSTNAME": hostname,
1767 "DNSDOMAIN": dnsdomain,
1769 "HOSTIP_BASE_LINE": hostip_base_line,
1770 "HOSTIP_HOST_LINE": hostip_host_line,
1771 "DOMAINGUID": domainguid,
1772 "DATESTRING": time.strftime("%Y%m%d%H"),
1773 "DEFAULTSITE": DEFAULTSITE,
1774 "NTDSGUID": ntdsguid,
1775 "HOSTIP6_BASE_LINE": hostip6_base_line,
1776 "HOSTIP6_HOST_LINE": hostip6_host_line,
1777 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1778 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1781 # note that we use no variable substitution on this file
1782 # the substitution is done at runtime by samba_dnsupdate
1783 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1785 # and the SPN update list
1786 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1788 if paths.bind_gid is not None:
1790 os.chown(dns_dir, -1, paths.bind_gid)
1791 os.chown(paths.dns, -1, paths.bind_gid)
1792 # chmod needed to cope with umask
1793 os.chmod(dns_dir, 0775)
1794 os.chmod(paths.dns, 0664)
1796 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1798 if targetdir is None:
1799 os.system(rndc + " unfreeze " + lp.get("realm"))
1802 def create_dns_update_list(lp, logger, paths, setup_path):
1803 """Write out a dns_update_list file"""
1804 # note that we use no variable substitution on this file
1805 # the substitution is done at runtime by samba_dnsupdate
1806 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1807 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1810 def create_named_conf(paths, setup_path, realm, dnsdomain,
1812 """Write out a file containing zone statements suitable for inclusion in a
1813 named.conf file (including GSS-TSIG configuration).
1815 :param paths: all paths
1816 :param setup_path: Setup path function.
1817 :param realm: Realm name
1818 :param dnsdomain: DNS Domain name
1819 :param private_dir: Path to private directory
1820 :param keytab_name: File name of DNS keytab file
1823 setup_file(setup_path("named.conf"), paths.namedconf, {
1824 "DNSDOMAIN": dnsdomain,
1826 "ZONE_FILE": paths.dns,
1827 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1828 "NAMED_CONF": paths.namedconf,
1829 "NAMED_CONF_UPDATE": paths.namedconf_update
1832 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1835 def create_named_txt(path, setup_path, realm, dnsdomain,
1836 private_dir, keytab_name):
1837 """Write out a file containing zone statements suitable for inclusion in a
1838 named.conf file (including GSS-TSIG configuration).
1840 :param path: Path of the new named.conf file.
1841 :param setup_path: Setup path function.
1842 :param realm: Realm name
1843 :param dnsdomain: DNS Domain name
1844 :param private_dir: Path to private directory
1845 :param keytab_name: File name of DNS keytab file
1848 setup_file(setup_path("named.txt"), path, {
1849 "DNSDOMAIN": dnsdomain,
1851 "DNS_KEYTAB": keytab_name,
1852 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1853 "PRIVATE_DIR": private_dir
1857 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1858 """Write out a file containing zone statements suitable for inclusion in a
1859 named.conf file (including GSS-TSIG configuration).
1861 :param path: Path of the new named.conf file.
1862 :param setup_path: Setup path function.
1863 :param dnsdomain: DNS Domain name
1864 :param hostname: Local hostname
1865 :param realm: Realm name
1867 setup_file(setup_path("krb5.conf"), path, {
1868 "DNSDOMAIN": dnsdomain,
1869 "HOSTNAME": hostname,
1874 class ProvisioningError(Exception):
1875 """A generic provision error."""
1877 def __init__(self, value):
1881 return "ProvisioningError: " + self.value
1884 class InvalidNetbiosName(Exception):
1885 """A specified name was not a valid NetBIOS name."""
1886 def __init__(self, name):
1887 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)