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 if realm is not None:
692 if dnsdomain is None:
693 dnsdomain = realm.lower()
694 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
697 shortname = netbiosname.lower()
699 #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
700 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
701 msg["secureChannelType"] = [str(secure_channel_type)]
702 msg["objectClass"] = ["top", "primaryDomain"]
703 if dnsname is not None:
704 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
705 msg["realm"] = [realm]
706 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
707 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
708 msg["privateKeytab"] = ["secrets.keytab"]
710 msg["secret"] = [machinepass]
711 msg["samAccountName"] = ["%s$" % netbiosname]
712 msg["secureChannelType"] = [str(secure_channel_type)]
713 if domainsid is not None:
714 msg["objectSid"] = [ndr_pack(domainsid)]
716 # This complex expression tries to ensure that we don't have more
717 # than one record for this SID, realm or netbios domain at a time,
718 # but we don't delete the old record that we are about to modify,
719 # because that would delete the keytab and previous password.
720 res = secretsdb.search(base="cn=Primary Domains",
722 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
723 scope=ldb.SCOPE_ONELEVEL)
726 secretsdb.delete(del_msg.dn)
728 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
731 msg["priorSecret"] = [res[0]["secret"][0]]
732 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
735 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
740 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
746 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
747 secretsdb.modify(msg)
748 secretsdb.rename(res[0].dn, msg.dn)
750 spn = [ 'HOST/%s' % shortname ]
751 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
752 # we are a domain controller then we add servicePrincipalName entries
753 # for the keytab code to update
754 spn.extend([ 'HOST/%s' % dnsname ])
755 msg["servicePrincipalName"] = spn
760 def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir,
762 dns_keytab_path, dnspass):
763 """Add DNS specific bits to a secrets database.
765 :param secretsdb: Ldb Handle to the secrets database
766 :param setup_path: Setup path function
767 :param machinepass: Machine password
770 os.unlink(os.path.join(private_dir, dns_keytab_path))
774 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
776 "DNSDOMAIN": dnsdomain,
777 "DNS_KEYTAB": dns_keytab_path,
778 "DNSPASS_B64": b64encode(dnspass),
779 "HOSTNAME": names.hostname,
780 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
784 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
785 """Setup the secrets database.
787 :note: This function does not handle exceptions and transaction on purpose,
788 it's up to the caller to do this job.
790 :param path: Path to the secrets database.
791 :param setup_path: Get the path to a setup file.
792 :param session_info: Session info.
793 :param credentials: Credentials
794 :param lp: Loadparm context
795 :return: LDB handle for the created secrets database
797 if os.path.exists(path):
799 secrets_ldb = Ldb(path, session_info=session_info,
802 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
803 secrets_ldb = Ldb(path, session_info=session_info,
805 secrets_ldb.transaction_start()
807 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
809 if backend_credentials is not None and backend_credentials.authentication_requested():
810 if backend_credentials.get_bind_dn() is not None:
811 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
812 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
813 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
816 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
817 "LDAPADMINUSER": backend_credentials.get_username(),
818 "LDAPADMINREALM": backend_credentials.get_realm(),
819 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
824 secrets_ldb.transaction_cancel()
827 def setup_privileges(path, setup_path, session_info, lp):
828 """Setup the privileges database.
830 :param path: Path to the privileges database.
831 :param setup_path: Get the path to a setup file.
832 :param session_info: Session info.
833 :param credentials: Credentials
834 :param lp: Loadparm context
835 :return: LDB handle for the created secrets database
837 if os.path.exists(path):
839 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
840 privilege_ldb.erase()
841 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
844 def setup_registry(path, setup_path, session_info, lp):
845 """Setup the registry.
847 :param path: Path to the registry database
848 :param setup_path: Function that returns the path to a setup.
849 :param session_info: Session information
850 :param credentials: Credentials
851 :param lp: Loadparm context
853 reg = samba.registry.Registry()
854 hive = samba.registry.open_ldb(path, session_info=session_info,
856 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
857 provision_reg = setup_path("provision.reg")
858 assert os.path.exists(provision_reg)
859 reg.diff_apply(provision_reg)
862 def setup_idmapdb(path, setup_path, session_info, lp):
863 """Setup the idmap database.
865 :param path: path to the idmap database
866 :param setup_path: Function that returns a path to a setup file
867 :param session_info: Session information
868 :param credentials: Credentials
869 :param lp: Loadparm context
871 if os.path.exists(path):
874 idmap_ldb = IDmapDB(path, session_info=session_info,
878 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
882 def setup_samdb_rootdse(samdb, setup_path, names):
883 """Setup the SamDB rootdse.
885 :param samdb: Sam Database handle
886 :param setup_path: Obtain setup path
888 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
889 "SCHEMADN": names.schemadn,
890 "DOMAINDN": names.domaindn,
891 "ROOTDN": names.rootdn,
892 "CONFIGDN": names.configdn,
893 "SERVERDN": names.serverdn,
897 def setup_self_join(samdb, names,
898 machinepass, dnspass,
899 domainsid, next_rid, invocationid, setup_path,
900 policyguid, policyguid_dc, domainControllerFunctionality,
902 """Join a host to its own domain."""
903 assert isinstance(invocationid, str)
904 if ntdsguid is not None:
905 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
908 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
909 "CONFIGDN": names.configdn,
910 "SCHEMADN": names.schemadn,
911 "DOMAINDN": names.domaindn,
912 "SERVERDN": names.serverdn,
913 "INVOCATIONID": invocationid,
914 "NETBIOSNAME": names.netbiosname,
915 "DEFAULTSITE": names.sitename,
916 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
917 "MACHINEPASS_B64": b64encode(machinepass),
918 "REALM": names.realm,
919 "DOMAIN": names.domain,
920 "DOMAINSID": str(domainsid),
921 "DCRID": str(next_rid),
922 "DNSDOMAIN": names.dnsdomain,
923 "SAMBA_VERSION_STRING": version,
924 "NTDSGUID": ntdsguid_line,
925 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
927 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
928 "POLICYGUID": policyguid,
929 "POLICYGUID_DC": policyguid_dc,
930 "DNSDOMAIN": names.dnsdomain,
931 "DOMAINSID": str(domainsid),
932 "DOMAINDN": names.domaindn})
934 # add the NTDSGUID based SPNs
935 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
936 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
937 expression="", scope=ldb.SCOPE_BASE)
938 assert isinstance(names.ntdsguid, str)
940 # Setup fSMORoleOwner entries to point at the newly created DC entry
941 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
942 "DOMAIN": names.domain,
943 "DNSDOMAIN": names.dnsdomain,
944 "DOMAINDN": names.domaindn,
945 "CONFIGDN": names.configdn,
946 "SCHEMADN": names.schemadn,
947 "DEFAULTSITE": names.sitename,
948 "SERVERDN": names.serverdn,
949 "NETBIOSNAME": names.netbiosname,
950 "NTDSGUID": names.ntdsguid,
951 "RIDALLOCATIONSTART": str(next_rid + 100),
952 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
955 # This is partially Samba4 specific and should be replaced by the correct
957 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
958 "DNSDOMAIN": names.dnsdomain,
959 "DOMAINDN": names.domaindn,
960 "DNSPASS_B64": b64encode(dnspass),
961 "HOSTNAME" : names.hostname,
962 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
965 def getpolicypath(sysvolpath, dnsdomain, guid):
966 """Return the physical path of policy given its guid.
968 :param sysvolpath: Path to the sysvol folder
969 :param dnsdomain: DNS name of the AD domain
970 :param guid: The GUID of the policy
971 :return: A string with the complete path to the policy folder
976 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
979 def create_gpo_struct(policy_path):
980 if not os.path.exists(policy_path):
981 os.makedirs(policy_path, 0775)
982 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
983 "[General]\r\nVersion=65543")
984 p = os.path.join(policy_path, "MACHINE")
985 if not os.path.exists(p):
987 p = os.path.join(policy_path, "USER")
988 if not os.path.exists(p):
992 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
993 """Create the default GPO for a domain
995 :param sysvolpath: Physical path for the sysvol folder
996 :param dnsdomain: DNS domain name of the AD domain
997 :param policyguid: GUID of the default domain policy
998 :param policyguid_dc: GUID of the default domain controler policy
1001 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1002 create_gpo_struct(policy_path)
1004 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1005 create_gpo_struct(policy_path)
1008 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1009 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1010 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1011 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1013 """Setup a complete SAM Database.
1015 :note: This will wipe the main SAM database file!
1019 # Provision does not make much sense values larger than 1000000000
1020 # as the upper range of the rIDAvailablePool is 1073741823 and
1021 # we don't want to create a domain that cannot allocate rids.
1022 if next_rid < 1000 or next_rid > 1000000000:
1023 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1024 error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
1025 raise ProvisioningError(error)
1027 # ATTENTION: Do NOT change these default values without discussion with the
1028 # team and/or release manager. They have a big impact on the whole program!
1029 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1031 if dom_for_fun_level is None:
1032 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1034 if dom_for_fun_level > domainControllerFunctionality:
1035 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!")
1037 domainFunctionality = dom_for_fun_level
1038 forestFunctionality = dom_for_fun_level
1040 # Also wipes the database
1041 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1042 provision_backend=provision_backend, session_info=session_info,
1043 names=names, serverrole=serverrole, schema=schema)
1046 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1048 # Load the database, but don's load the global schema and don't connect quite yet
1049 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1050 credentials=provision_backend.credentials, lp=lp, global_schema=False,
1053 logger.info("Pre-loading the Samba 4 and AD schema")
1055 # Load the schema from the one we computed earlier
1056 samdb.set_schema(schema)
1058 # Set the NTDS settings DN manually - in order to have it already around
1059 # before the provisioned tree exists and we connect
1060 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1062 # And now we can connect to the DB - the schema won't be loaded from the DB
1065 if fill == FILL_DRS:
1068 samdb.transaction_start()
1070 # Set the domain functionality levels onto the database.
1071 # Various module (the password_hash module in particular) need
1072 # to know what level of AD we are emulating.
1074 # These will be fixed into the database via the database
1075 # modifictions below, but we need them set from the start.
1076 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1077 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1078 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1080 samdb.set_domain_sid(str(domainsid))
1081 samdb.set_invocation_id(invocationid)
1083 logger.info("Adding DomainDN: %s" % names.domaindn)
1085 #impersonate domain admin
1086 admin_session_info = admin_session(lp, str(domainsid))
1087 samdb.set_session_info(admin_session_info)
1088 if domainguid is not None:
1089 domainguid_line = "objectGUID: %s\n-" % domainguid
1091 domainguid_line = ""
1093 descr = b64encode(get_domain_descriptor(domainsid))
1094 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1095 "DOMAINDN": names.domaindn,
1096 "DOMAINGUID": domainguid_line,
1101 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1102 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1103 "DOMAINSID": str(domainsid),
1104 "NEXTRID": str(next_rid),
1105 "SCHEMADN": names.schemadn,
1106 "NETBIOSNAME": names.netbiosname,
1107 "DEFAULTSITE": names.sitename,
1108 "CONFIGDN": names.configdn,
1109 "SERVERDN": names.serverdn,
1110 "POLICYGUID": policyguid,
1111 "DOMAINDN": names.domaindn,
1112 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1113 "SAMBA_VERSION_STRING": version
1116 logger.info("Adding configuration container")
1117 descr = b64encode(get_config_descriptor(domainsid))
1118 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1119 "CONFIGDN": names.configdn,
1120 "DESCRIPTOR": descr,
1123 # The LDIF here was created when the Schema object was constructed
1124 logger.info("Setting up sam.ldb schema")
1125 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1126 samdb.modify_ldif(schema.schema_dn_modify)
1127 samdb.write_prefixes_from_schema()
1128 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1129 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1130 {"SCHEMADN": names.schemadn})
1132 logger.info("Reopening sam.ldb with new schema")
1134 samdb.transaction_cancel()
1137 samdb.transaction_commit()
1139 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1140 credentials=provision_backend.credentials, lp=lp,
1141 global_schema=False, am_rodc=am_rodc)
1143 # Set the NTDS settings DN manually - in order to have it already around
1144 # before the provisioned tree exists and we connect
1145 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1149 samdb.transaction_start()
1151 samdb.invocation_id = invocationid
1153 logger.info("Setting up sam.ldb configuration data")
1154 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1155 "CONFIGDN": names.configdn,
1156 "NETBIOSNAME": names.netbiosname,
1157 "DEFAULTSITE": names.sitename,
1158 "DNSDOMAIN": names.dnsdomain,
1159 "DOMAIN": names.domain,
1160 "SCHEMADN": names.schemadn,
1161 "DOMAINDN": names.domaindn,
1162 "SERVERDN": names.serverdn,
1163 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1164 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
1167 logger.info("Setting up display specifiers")
1168 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1169 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1170 check_all_substituted(display_specifiers_ldif)
1171 samdb.add_ldif(display_specifiers_ldif)
1173 logger.info("Adding users container")
1174 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1175 "DOMAINDN": names.domaindn})
1176 logger.info("Modifying users container")
1177 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1178 "DOMAINDN": names.domaindn})
1179 logger.info("Adding computers container")
1180 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1181 "DOMAINDN": names.domaindn})
1182 logger.info("Modifying computers container")
1183 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1184 "DOMAINDN": names.domaindn})
1185 logger.info("Setting up sam.ldb data")
1186 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1187 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1188 "DOMAINDN": names.domaindn,
1189 "NETBIOSNAME": names.netbiosname,
1190 "DEFAULTSITE": names.sitename,
1191 "CONFIGDN": names.configdn,
1192 "SERVERDN": names.serverdn,
1193 "RIDAVAILABLESTART": str(next_rid + 600),
1194 "POLICYGUID_DC": policyguid_dc
1197 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1198 "DOMAINDN": names.domaindn})
1200 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1201 "CONFIGDN": names.configdn,
1202 "SCHEMADN": names.schemadn})
1203 if fill == FILL_FULL:
1204 logger.info("Setting up sam.ldb users and groups")
1205 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1206 "DOMAINDN": names.domaindn,
1207 "DOMAINSID": str(domainsid),
1208 "CONFIGDN": names.configdn,
1209 "ADMINPASS_B64": b64encode(adminpass),
1210 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1213 logger.info("Setting up self join")
1214 setup_self_join(samdb, names=names, invocationid=invocationid,
1216 machinepass=machinepass,
1217 domainsid=domainsid,
1219 policyguid=policyguid,
1220 policyguid_dc=policyguid_dc,
1221 setup_path=setup_path,
1222 domainControllerFunctionality=domainControllerFunctionality,
1225 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1226 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1227 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1228 assert isinstance(names.ntdsguid, str)
1230 samdb.transaction_cancel()
1233 samdb.transaction_commit()
1238 FILL_NT4SYNC = "NT4SYNC"
1240 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1241 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)"
1243 def set_dir_acl(path, acl, lp, domsid):
1244 setntacl(lp, path, acl, domsid)
1245 for root, dirs, files in os.walk(path, topdown=False):
1247 setntacl(lp, os.path.join(root, name), acl, domsid)
1249 setntacl(lp, os.path.join(root, name), acl, domsid)
1252 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1253 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1256 :param sysvol: Physical path for the sysvol folder
1257 :param dnsdomain: The DNS name of the domain
1258 :param domainsid: The SID of the domain
1259 :param domaindn: The DN of the domain (ie. DC=...)
1260 :param samdb: An LDB object on the SAM db
1261 :param lp: an LP object
1264 # Set ACL for GPO root folder
1265 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1266 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1268 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1269 attrs=["cn", "nTSecurityDescriptor"],
1270 expression="", scope=ldb.SCOPE_ONELEVEL)
1273 acl = ndr_unpack(security.descriptor,
1274 str(policy["nTSecurityDescriptor"])).as_sddl()
1275 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1276 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1279 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1281 """Set the ACL for the sysvol share and the subfolders
1283 :param samdb: An LDB object on the SAM db
1284 :param netlogon: Physical path for the netlogon folder
1285 :param sysvol: Physical path for the sysvol folder
1286 :param gid: The GID of the "Domain adminstrators" group
1287 :param domainsid: The SID of the domain
1288 :param dnsdomain: The DNS name of the domain
1289 :param domaindn: The DN of the domain (ie. DC=...)
1293 os.chown(sysvol,-1,gid)
1299 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1300 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1301 for root, dirs, files in os.walk(sysvol, topdown=False):
1304 os.chown(os.path.join(root, name), -1, gid)
1305 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1308 os.chown(os.path.join(root, name), -1, gid)
1309 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1311 # Set acls on Policy folder and policies folders
1312 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1315 def provision(setup_dir, logger, session_info,
1316 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1318 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1320 domain=None, hostname=None, hostip=None, hostip6=None,
1321 domainsid=None, next_rid=1000,
1322 adminpass=None, ldapadminpass=None,
1323 krbtgtpass=None, domainguid=None,
1324 policyguid=None, policyguid_dc=None, invocationid=None,
1325 machinepass=None, ntdsguid=None,
1326 dnspass=None, root=None, nobody=None, users=None,
1327 wheel=None, backup=None, aci=None, serverrole=None,
1328 dom_for_fun_level=None,
1329 ldap_backend_extra_port=None, backend_type=None,
1331 ol_mmr_urls=None, ol_olc=None,
1332 setup_ds_path=None, slapd_path=None, nosync=False,
1333 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1336 :note: caution, this wipes all existing data!
1339 def setup_path(file):
1340 return os.path.join(setup_dir, file)
1342 if domainsid is None:
1343 domainsid = security.random_sid()
1345 domainsid = security.dom_sid(domainsid)
1347 # create/adapt the group policy GUIDs
1348 # Default GUID for default policy are described at
1349 # "How Core Group Policy Works"
1350 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1351 if policyguid is None:
1352 policyguid = DEFAULT_POLICY_GUID
1353 policyguid = policyguid.upper()
1354 if policyguid_dc is None:
1355 policyguid_dc = DEFAULT_DC_POLICY_GUID
1356 policyguid_dc = policyguid_dc.upper()
1358 if adminpass is None:
1359 adminpass = samba.generate_random_password(12, 32)
1360 if krbtgtpass is None:
1361 krbtgtpass = samba.generate_random_password(128, 255)
1362 if machinepass is None:
1363 machinepass = samba.generate_random_password(128, 255)
1365 dnspass = samba.generate_random_password(128, 255)
1366 if ldapadminpass is None:
1367 #Make a new, random password between Samba and it's LDAP server
1368 ldapadminpass=samba.generate_random_password(128, 255)
1370 if backend_type is None:
1371 backend_type = "ldb"
1373 sid_generator = "internal"
1374 if backend_type == "fedora-ds":
1375 sid_generator = "backend"
1377 root_uid = findnss_uid([root or "root"])
1378 nobody_uid = findnss_uid([nobody or "nobody"])
1379 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1381 wheel_gid = findnss_gid(["wheel", "adm"])
1383 wheel_gid = findnss_gid([wheel])
1385 bind_gid = findnss_gid(["bind", "named"])
1389 if targetdir is not None:
1390 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1391 elif smbconf is None:
1392 smbconf = samba.param.default_path()
1393 if not os.path.exists(os.path.dirname(smbconf)):
1394 os.makedirs(os.path.dirname(smbconf))
1396 # only install a new smb.conf if there isn't one there already
1397 if os.path.exists(smbconf):
1398 # if Samba Team members can't figure out the weird errors
1399 # loading an empty smb.conf gives, then we need to be smarter.
1400 # Pretend it just didn't exist --abartlet
1401 data = open(smbconf, 'r').read()
1402 data = data.lstrip()
1403 if data is None or data == "":
1404 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1405 serverrole, targetdir, sid_generator, useeadb)
1407 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1408 targetdir, sid_generator, useeadb)
1410 lp = samba.param.LoadParm()
1413 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1414 dnsdomain=realm, serverrole=serverrole,
1415 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1416 serverdn=serverdn, sitename=sitename)
1418 paths = provision_paths_from_lp(lp, names.dnsdomain)
1420 paths.bind_gid = bind_gid
1423 hostips = samba.interface_ips(lp, False)
1424 if len(hostips) == 0:
1425 logger.warning("No external IPv4 address has been found. Using loopback.")
1426 hostip = '127.0.0.1'
1429 if len(hostips) > 1:
1430 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1434 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1437 if hostip6 == '::1' and ip[-1][0] != '::1':
1439 except socket.gaierror, (socket.EAI_NODATA, msg):
1442 if serverrole is None:
1443 serverrole = lp.get("server role")
1445 assert serverrole in ("domain controller", "member server", "standalone")
1446 if invocationid is None:
1447 invocationid = str(uuid.uuid4())
1449 if not os.path.exists(paths.private_dir):
1450 os.mkdir(paths.private_dir)
1451 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1452 os.mkdir(os.path.join(paths.private_dir, "tls"))
1454 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1456 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1457 serverdn=names.serverdn)
1459 if backend_type == "ldb":
1460 provision_backend = LDBBackend(backend_type,
1461 paths=paths, setup_path=setup_path,
1462 lp=lp, credentials=credentials,
1465 elif backend_type == "existing":
1466 provision_backend = ExistingBackend(backend_type,
1467 paths=paths, setup_path=setup_path,
1468 lp=lp, credentials=credentials,
1471 ldapi_url=ldapi_url)
1472 elif backend_type == "fedora-ds":
1473 provision_backend = FDSBackend(backend_type,
1474 paths=paths, setup_path=setup_path,
1475 lp=lp, credentials=credentials,
1478 domainsid=domainsid,
1481 ldapadminpass=ldapadminpass,
1482 slapd_path=slapd_path,
1483 ldap_backend_extra_port=ldap_backend_extra_port,
1484 ldap_dryrun_mode=ldap_dryrun_mode,
1486 setup_ds_path=setup_ds_path)
1487 elif backend_type == "openldap":
1488 provision_backend = OpenLDAPBackend(backend_type,
1489 paths=paths, setup_path=setup_path,
1490 lp=lp, credentials=credentials,
1493 domainsid=domainsid,
1496 ldapadminpass=ldapadminpass,
1497 slapd_path=slapd_path,
1498 ldap_backend_extra_port=ldap_backend_extra_port,
1499 ldap_dryrun_mode=ldap_dryrun_mode,
1500 ol_mmr_urls=ol_mmr_urls,
1503 raise ValueError("Unknown LDAP backend type selected")
1505 provision_backend.init()
1506 provision_backend.start()
1508 # only install a new shares config db if there is none
1509 if not os.path.exists(paths.shareconf):
1510 logger.info("Setting up share.ldb")
1511 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1513 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1515 logger.info("Setting up secrets.ldb")
1516 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1517 session_info=session_info,
1518 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1521 logger.info("Setting up the registry")
1522 setup_registry(paths.hklm, setup_path, session_info,
1525 logger.info("Setting up the privileges database")
1526 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1528 logger.info("Setting up idmap db")
1529 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1532 logger.info("Setting up SAM db")
1533 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1534 provision_backend, lp, names,
1536 domainsid=domainsid,
1537 schema=schema, domainguid=domainguid,
1538 policyguid=policyguid, policyguid_dc=policyguid_dc,
1540 adminpass=adminpass, krbtgtpass=krbtgtpass,
1541 invocationid=invocationid,
1542 machinepass=machinepass, dnspass=dnspass,
1543 ntdsguid=ntdsguid, serverrole=serverrole,
1544 dom_for_fun_level=dom_for_fun_level,
1545 am_rodc=am_rodc, next_rid=next_rid)
1547 if serverrole == "domain controller":
1548 if paths.netlogon is None:
1549 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1550 logger.info("Please either remove %s or see the template at %s" %
1551 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1552 assert paths.netlogon is not None
1554 if paths.sysvol is None:
1555 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1556 " are configuring a DC.")
1557 logger.info("Please either remove %s or see the template at %s" %
1558 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1559 assert paths.sysvol is not None
1561 if not os.path.isdir(paths.netlogon):
1562 os.makedirs(paths.netlogon, 0755)
1564 if samdb_fill == FILL_FULL:
1565 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1566 root_uid=root_uid, nobody_uid=nobody_uid,
1567 users_gid=users_gid, wheel_gid=wheel_gid)
1569 if serverrole == "domain controller":
1570 # Set up group policies (domain policy and domain controller policy)
1571 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1572 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1573 domainsid, names.dnsdomain, names.domaindn, lp)
1575 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1576 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1578 secretsdb_self_join(secrets_ldb, domain=names.domain,
1580 dnsdomain=names.dnsdomain,
1581 netbiosname=names.netbiosname,
1582 domainsid=domainsid,
1583 machinepass=machinepass,
1584 secure_channel_type=SEC_CHAN_BDC)
1586 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1587 # In future, this might be determined from some configuration
1588 kerberos_enctypes = str(ENC_ALL_TYPES)
1591 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1592 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
1593 flags=ldb.FLAG_MOD_REPLACE,
1594 name="msDS-SupportedEncryptionTypes")
1596 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1597 # It might be that this attribute does not exist in this schema
1601 if serverrole == "domain controller":
1602 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1604 realm=names.realm, dnsdomain=names.dnsdomain,
1605 dns_keytab_path=paths.dns_keytab,
1608 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1609 assert isinstance(domainguid, str)
1611 # Only make a zone file on the first DC, it should be replicated
1612 # with DNS replication
1613 create_zone_file(lp, logger, paths, targetdir, setup_path,
1614 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1615 hostname=names.hostname, realm=names.realm,
1616 domainguid=domainguid, ntdsguid=names.ntdsguid)
1618 create_named_conf(paths, setup_path, realm=names.realm,
1619 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1621 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1622 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1623 keytab_name=paths.dns_keytab)
1624 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1625 logger.info("and %s for further documentation required for secure DNS "
1626 "updates", paths.namedtxt)
1628 create_krb5_conf(paths.krb5conf, setup_path,
1629 dnsdomain=names.dnsdomain, hostname=names.hostname,
1631 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1632 "generated at %s", paths.krb5conf)
1634 lastProvisionUSNs = get_last_provision_usn(samdb)
1635 maxUSN = get_max_usn(samdb, str(names.rootdn))
1636 if lastProvisionUSNs is not None:
1637 update_provision_usn(samdb, 0, maxUSN, 1)
1639 set_provision_usn(samdb, 0, maxUSN)
1641 if serverrole == "domain controller":
1642 create_dns_update_list(lp, logger, paths, setup_path)
1644 provision_backend.post_setup()
1645 provision_backend.shutdown()
1647 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1650 secrets_ldb.transaction_cancel()
1653 #Now commit the secrets.ldb to disk
1654 secrets_ldb.transaction_commit()
1656 # the commit creates the dns.keytab, now chown it
1657 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1658 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1660 os.chmod(dns_keytab_path, 0640)
1661 os.chown(dns_keytab_path, -1, paths.bind_gid)
1663 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1667 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1668 paths.phpldapadminconfig)
1670 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1671 logger.info("Server Role: %s" % serverrole)
1672 logger.info("Hostname: %s" % names.hostname)
1673 logger.info("NetBIOS Domain: %s" % names.domain)
1674 logger.info("DNS Domain: %s" % names.dnsdomain)
1675 logger.info("DOMAIN SID: %s" % str(domainsid))
1676 if samdb_fill == FILL_FULL:
1677 logger.info("Admin password: %s" % adminpass)
1678 if provision_backend.type is not "ldb":
1679 if provision_backend.credentials.get_bind_dn() is not None:
1680 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1682 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1684 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1686 if provision_backend.slapd_command_escaped is not None:
1687 # now display slapd_command_file.txt to show how slapd must be started next time
1688 logger.info("Use later the following commandline to start slapd, then Samba:")
1689 logger.info(provision_backend.slapd_command_escaped)
1690 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1691 provision_backend.ldapdir)
1693 result = ProvisionResult()
1694 result.domaindn = domaindn
1695 result.paths = paths
1697 result.samdb = samdb
1701 def provision_become_dc(setup_dir=None,
1702 smbconf=None, targetdir=None, realm=None,
1703 rootdn=None, domaindn=None, schemadn=None,
1704 configdn=None, serverdn=None,
1705 domain=None, hostname=None, domainsid=None,
1706 adminpass=None, krbtgtpass=None, domainguid=None,
1707 policyguid=None, policyguid_dc=None, invocationid=None,
1709 dnspass=None, root=None, nobody=None, users=None,
1710 wheel=None, backup=None, serverrole=None,
1711 ldap_backend=None, ldap_backend_type=None,
1712 sitename=None, debuglevel=1):
1714 logger = logging.getLogger("provision")
1715 samba.set_debug_level(debuglevel)
1717 return provision(setup_dir, logger, system_session(), None,
1718 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1719 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1720 configdn=configdn, serverdn=serverdn, domain=domain,
1721 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1722 machinepass=machinepass, serverrole="domain controller",
1726 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1727 """Create a PHP LDAP admin configuration file.
1729 :param path: Path to write the configuration to.
1730 :param setup_path: Function to generate setup paths.
1732 setup_file(setup_path("phpldapadmin-config.php"), path,
1733 {"S4_LDAPI_URI": ldapi_uri})
1736 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1737 hostip, hostip6, hostname, realm, domainguid,
1739 """Write out a DNS zone file, from the info in the current database.
1741 :param paths: paths object
1742 :param setup_path: Setup path function.
1743 :param dnsdomain: DNS Domain name
1744 :param domaindn: DN of the Domain
1745 :param hostip: Local IPv4 IP
1746 :param hostip6: Local IPv6 IP
1747 :param hostname: Local hostname
1748 :param realm: Realm name
1749 :param domainguid: GUID of the domain.
1750 :param ntdsguid: GUID of the hosts nTDSDSA record.
1752 assert isinstance(domainguid, str)
1754 if hostip6 is not None:
1755 hostip6_base_line = " IN AAAA " + hostip6
1756 hostip6_host_line = hostname + " IN AAAA " + hostip6
1757 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1759 hostip6_base_line = ""
1760 hostip6_host_line = ""
1761 gc_msdcs_ip6_line = ""
1763 if hostip is not None:
1764 hostip_base_line = " IN A " + hostip
1765 hostip_host_line = hostname + " IN A " + hostip
1766 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1768 hostip_base_line = ""
1769 hostip_host_line = ""
1770 gc_msdcs_ip_line = ""
1772 dns_dir = os.path.dirname(paths.dns)
1775 shutil.rmtree(dns_dir, True)
1779 os.mkdir(dns_dir, 0775)
1781 # we need to freeze the zone while we update the contents
1782 if targetdir is None:
1783 rndc = ' '.join(lp.get("rndc command"))
1784 os.system(rndc + " freeze " + lp.get("realm"))
1786 setup_file(setup_path("provision.zone"), paths.dns, {
1787 "HOSTNAME": hostname,
1788 "DNSDOMAIN": dnsdomain,
1790 "HOSTIP_BASE_LINE": hostip_base_line,
1791 "HOSTIP_HOST_LINE": hostip_host_line,
1792 "DOMAINGUID": domainguid,
1793 "DATESTRING": time.strftime("%Y%m%d%H"),
1794 "DEFAULTSITE": DEFAULTSITE,
1795 "NTDSGUID": ntdsguid,
1796 "HOSTIP6_BASE_LINE": hostip6_base_line,
1797 "HOSTIP6_HOST_LINE": hostip6_host_line,
1798 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1799 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1802 # note that we use no variable substitution on this file
1803 # the substitution is done at runtime by samba_dnsupdate
1804 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1806 # and the SPN update list
1807 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1809 if paths.bind_gid is not None:
1811 os.chown(dns_dir, -1, paths.bind_gid)
1812 os.chown(paths.dns, -1, paths.bind_gid)
1813 # chmod needed to cope with umask
1814 os.chmod(dns_dir, 0775)
1815 os.chmod(paths.dns, 0664)
1817 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1819 if targetdir is None:
1820 os.system(rndc + " unfreeze " + lp.get("realm"))
1823 def create_dns_update_list(lp, logger, paths, setup_path):
1824 """Write out a dns_update_list file"""
1825 # note that we use no variable substitution on this file
1826 # the substitution is done at runtime by samba_dnsupdate
1827 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1828 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1831 def create_named_conf(paths, setup_path, realm, dnsdomain,
1833 """Write out a file containing zone statements suitable for inclusion in a
1834 named.conf file (including GSS-TSIG configuration).
1836 :param paths: all paths
1837 :param setup_path: Setup path function.
1838 :param realm: Realm name
1839 :param dnsdomain: DNS Domain name
1840 :param private_dir: Path to private directory
1841 :param keytab_name: File name of DNS keytab file
1844 setup_file(setup_path("named.conf"), paths.namedconf, {
1845 "DNSDOMAIN": dnsdomain,
1847 "ZONE_FILE": paths.dns,
1848 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1849 "NAMED_CONF": paths.namedconf,
1850 "NAMED_CONF_UPDATE": paths.namedconf_update
1853 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1856 def create_named_txt(path, setup_path, realm, dnsdomain,
1857 private_dir, keytab_name):
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 realm: Realm name
1864 :param dnsdomain: DNS Domain name
1865 :param private_dir: Path to private directory
1866 :param keytab_name: File name of DNS keytab file
1869 setup_file(setup_path("named.txt"), path, {
1870 "DNSDOMAIN": dnsdomain,
1872 "DNS_KEYTAB": keytab_name,
1873 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1874 "PRIVATE_DIR": private_dir
1878 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1879 """Write out a file containing zone statements suitable for inclusion in a
1880 named.conf file (including GSS-TSIG configuration).
1882 :param path: Path of the new named.conf file.
1883 :param setup_path: Setup path function.
1884 :param dnsdomain: DNS Domain name
1885 :param hostname: Local hostname
1886 :param realm: Realm name
1888 setup_file(setup_path("krb5.conf"), path, {
1889 "DNSDOMAIN": dnsdomain,
1890 "HOSTNAME": hostname,
1895 class ProvisioningError(Exception):
1896 """A generic provision error."""
1898 def __init__(self, value):
1902 return "ProvisioningError: " + self.value
1905 class InvalidNetbiosName(Exception):
1906 """A specified name was not a valid NetBIOS name."""
1907 def __init__(self, name):
1908 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)