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"
392 paths.keytab = "secrets.keytab"
394 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
395 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
396 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
397 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
398 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
399 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
400 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
401 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
402 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
403 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
404 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
405 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
406 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
407 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
408 paths.phpldapadminconfig = os.path.join(paths.private_dir,
409 "phpldapadmin-config.php")
410 paths.hklm = "hklm.ldb"
411 paths.hkcr = "hkcr.ldb"
412 paths.hkcu = "hkcu.ldb"
413 paths.hku = "hku.ldb"
414 paths.hkpd = "hkpd.ldb"
415 paths.hkpt = "hkpt.ldb"
416 paths.sysvol = lp.get("path", "sysvol")
417 paths.netlogon = lp.get("path", "netlogon")
418 paths.smbconf = lp.configfile
422 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
423 serverrole=None, rootdn=None, domaindn=None, configdn=None,
424 schemadn=None, serverdn=None, sitename=None):
425 """Guess configuration settings to use."""
428 hostname = socket.gethostname().split(".")[0]
430 netbiosname = lp.get("netbios name")
431 if netbiosname is None:
432 netbiosname = hostname
433 assert netbiosname is not None
434 netbiosname = netbiosname.upper()
435 if not valid_netbios_name(netbiosname):
436 raise InvalidNetbiosName(netbiosname)
438 if dnsdomain is None:
439 dnsdomain = lp.get("realm")
440 if dnsdomain is None or dnsdomain == "":
441 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
443 dnsdomain = dnsdomain.lower()
445 if serverrole is None:
446 serverrole = lp.get("server role")
447 if serverrole is None:
448 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
450 serverrole = serverrole.lower()
452 realm = dnsdomain.upper()
454 if lp.get("realm") == "":
455 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
457 if lp.get("realm").upper() != realm:
458 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))
460 if lp.get("server role").lower() != serverrole:
461 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))
463 if serverrole == "domain controller":
465 # This will, for better or worse, default to 'WORKGROUP'
466 domain = lp.get("workgroup")
467 domain = domain.upper()
469 if lp.get("workgroup").upper() != domain:
470 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))
473 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
477 domaindn = "DC=" + netbiosname
479 if not valid_netbios_name(domain):
480 raise InvalidNetbiosName(domain)
482 if hostname.upper() == realm:
483 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
484 if netbiosname == realm:
485 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
487 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
493 configdn = "CN=Configuration," + rootdn
495 schemadn = "CN=Schema," + configdn
500 names = ProvisionNames()
501 names.rootdn = rootdn
502 names.domaindn = domaindn
503 names.configdn = configdn
504 names.schemadn = schemadn
505 names.ldapmanagerdn = "CN=Manager," + rootdn
506 names.dnsdomain = dnsdomain
507 names.domain = domain
509 names.netbiosname = netbiosname
510 names.hostname = hostname
511 names.sitename = sitename
512 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
517 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
518 targetdir, sid_generator="internal", eadb=False):
519 """Create a new smb.conf file based on a couple of basic settings.
521 assert smbconf is not None
523 hostname = socket.gethostname().split(".")[0]
524 netbiosname = hostname.upper()
526 if serverrole is None:
527 serverrole = "standalone"
529 assert serverrole in ("domain controller", "member server", "standalone")
530 if serverrole == "domain controller":
532 elif serverrole == "member server":
533 smbconfsuffix = "member"
534 elif serverrole == "standalone":
535 smbconfsuffix = "standalone"
537 if sid_generator is None:
538 sid_generator = "internal"
540 assert domain is not None
541 domain = domain.upper()
543 assert realm is not None
544 realm = realm.upper()
546 default_lp = samba.param.LoadParm()
547 #Load non-existant file
548 if os.path.exists(smbconf):
549 default_lp.load(smbconf)
551 if targetdir is not None:
552 privdir = os.path.join(targetdir, "private")
554 privdir = default_lp.get("private dir")
555 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir, "eadb.tdb"))
559 if targetdir is not None:
560 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
561 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
563 default_lp.set("lock dir", os.path.abspath(targetdir))
568 if sid_generator == "internal":
569 sid_generator_line = ""
571 sid_generator_line = "sid generator = " + sid_generator
573 used_setup_dir = setup_path("")
574 default_setup_dir = default_lp.get("setup directory")
576 if used_setup_dir != default_setup_dir:
577 setupdir_line = "setup directory = %s" % used_setup_dir
578 default_lp.set("setup directory", used_setup_dir)
580 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
581 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
583 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
585 "NETBIOS_NAME": netbiosname,
588 "SERVERROLE": serverrole,
589 "NETLOGONPATH": netlogon,
590 "SYSVOLPATH": sysvol,
591 "SETUPDIRECTORY_LINE": setupdir_line,
592 "SIDGENERATOR_LINE": sid_generator_line,
593 "PRIVATEDIR_LINE": privatedir_line,
594 "LOCKDIR_LINE": lockdir_line,
595 "POSIXEADB_LINE": posixeadb_line
599 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
600 users_gid, wheel_gid):
601 """setup reasonable name mappings for sam names to unix names.
603 :param samdb: SamDB object.
604 :param idmap: IDmap db object.
605 :param sid: The domain sid.
606 :param domaindn: The domain DN.
607 :param root_uid: uid of the UNIX root user.
608 :param nobody_uid: uid of the UNIX nobody user.
609 :param users_gid: gid of the UNIX users group.
610 :param wheel_gid: gid of the UNIX wheel group."""
611 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
612 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
614 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
615 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
618 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
619 provision_backend, names, schema, serverrole,
621 """Setup the partitions for the SAM database.
623 Alternatively, provision() may call this, and then populate the database.
625 :note: This will wipe the Sam Database!
627 :note: This function always removes the local SAM LDB file. The erase
628 parameter controls whether to erase the existing data, which
629 may not be stored locally but in LDAP.
632 assert session_info is not None
634 # We use options=["modules:"] to stop the modules loading - we
635 # just want to wipe and re-initialise the database, not start it up
638 os.unlink(samdb_path)
642 samdb = Ldb(url=samdb_path, session_info=session_info,
643 lp=lp, options=["modules:"])
645 ldap_backend_line = "# No LDAP backend"
646 if provision_backend.type is not "ldb":
647 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
649 samdb.transaction_start()
651 logger.info("Setting up sam.ldb partitions and settings")
652 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
653 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
654 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
655 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
656 "LDAP_BACKEND_LINE": ldap_backend_line,
660 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
661 "BACKEND_TYPE": provision_backend.type,
662 "SERVER_ROLE": serverrole
665 logger.info("Setting up sam.ldb rootDSE")
666 setup_samdb_rootdse(samdb, setup_path, names)
668 samdb.transaction_cancel()
671 samdb.transaction_commit()
674 def secretsdb_self_join(secretsdb, domain,
675 netbiosname, machinepass, domainsid=None,
676 realm=None, dnsdomain=None,
678 key_version_number=1,
679 secure_channel_type=SEC_CHAN_WKSTA):
680 """Add domain join-specific bits to a secrets database.
682 :param secretsdb: Ldb Handle to the secrets database
683 :param machinepass: Machine password
685 attrs=["whenChanged",
692 if realm is not None:
693 if dnsdomain is None:
694 dnsdomain = realm.lower()
695 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
698 shortname = netbiosname.lower()
700 #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
701 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
702 msg["secureChannelType"] = [str(secure_channel_type)]
703 msg["objectClass"] = ["top", "primaryDomain"]
704 if dnsname is not None:
705 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
706 msg["realm"] = [realm]
707 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
708 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
709 msg["privateKeytab"] = ["secrets.keytab"]
711 msg["secret"] = [machinepass]
712 msg["samAccountName"] = ["%s$" % netbiosname]
713 msg["secureChannelType"] = [str(secure_channel_type)]
714 if domainsid is not None:
715 msg["objectSid"] = [ndr_pack(domainsid)]
717 # This complex expression tries to ensure that we don't have more
718 # than one record for this SID, realm or netbios domain at a time,
719 # but we don't delete the old record that we are about to modify,
720 # because that would delete the keytab and previous password.
721 res = secretsdb.search(base="cn=Primary Domains",
723 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
724 scope=ldb.SCOPE_ONELEVEL)
727 secretsdb.delete(del_msg.dn)
729 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
732 msg["priorSecret"] = [res[0]["secret"][0]]
733 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
736 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
741 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
747 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
748 secretsdb.modify(msg)
749 secretsdb.rename(res[0].dn, msg.dn)
751 spn = [ 'HOST/%s' % shortname ]
752 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
753 # we are a domain controller then we add servicePrincipalName entries
754 # for the keytab code to update
755 spn.extend([ 'HOST/%s' % dnsname ])
756 msg["servicePrincipalName"] = spn
761 def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir,
763 dns_keytab_path, dnspass):
764 """Add DNS specific bits to a secrets database.
766 :param secretsdb: Ldb Handle to the secrets database
767 :param setup_path: Setup path function
768 :param machinepass: Machine password
771 os.unlink(os.path.join(private_dir, dns_keytab_path))
775 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
777 "DNSDOMAIN": dnsdomain,
778 "DNS_KEYTAB": dns_keytab_path,
779 "DNSPASS_B64": b64encode(dnspass),
780 "HOSTNAME": names.hostname,
781 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
785 def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
786 """Setup the secrets database.
788 :note: This function does not handle exceptions and transaction on purpose,
789 it's up to the caller to do this job.
791 :param path: Path to the secrets database.
792 :param setup_path: Get the path to a setup file.
793 :param session_info: Session info.
794 :param credentials: Credentials
795 :param lp: Loadparm context
796 :return: LDB handle for the created secrets database
798 if os.path.exists(paths.secrets):
799 os.unlink(paths.secrets)
801 keytab_path = os.path.join(paths.private_dir, paths.keytab)
802 if os.path.exists(keytab_path):
803 os.unlink(keytab_path)
805 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
806 if os.path.exists(dns_keytab_path):
807 os.unlink(dns_keytab_path)
811 secrets_ldb = Ldb(path, session_info=session_info,
814 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
815 secrets_ldb = Ldb(path, session_info=session_info,
817 secrets_ldb.transaction_start()
819 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
821 if backend_credentials is not None and backend_credentials.authentication_requested():
822 if backend_credentials.get_bind_dn() is not None:
823 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
824 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
825 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
828 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
829 "LDAPADMINUSER": backend_credentials.get_username(),
830 "LDAPADMINREALM": backend_credentials.get_realm(),
831 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
836 secrets_ldb.transaction_cancel()
839 def setup_privileges(path, setup_path, session_info, lp):
840 """Setup the privileges database.
842 :param path: Path to the privileges database.
843 :param setup_path: Get the path to a setup file.
844 :param session_info: Session info.
845 :param credentials: Credentials
846 :param lp: Loadparm context
847 :return: LDB handle for the created secrets database
849 if os.path.exists(path):
851 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
852 privilege_ldb.erase()
853 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
856 def setup_registry(path, setup_path, session_info, lp):
857 """Setup the registry.
859 :param path: Path to the registry database
860 :param setup_path: Function that returns the path to a setup.
861 :param session_info: Session information
862 :param credentials: Credentials
863 :param lp: Loadparm context
865 reg = samba.registry.Registry()
866 hive = samba.registry.open_ldb(path, session_info=session_info,
868 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
869 provision_reg = setup_path("provision.reg")
870 assert os.path.exists(provision_reg)
871 reg.diff_apply(provision_reg)
874 def setup_idmapdb(path, setup_path, session_info, lp):
875 """Setup the idmap database.
877 :param path: path to the idmap database
878 :param setup_path: Function that returns a path to a setup file
879 :param session_info: Session information
880 :param credentials: Credentials
881 :param lp: Loadparm context
883 if os.path.exists(path):
886 idmap_ldb = IDmapDB(path, session_info=session_info,
890 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
894 def setup_samdb_rootdse(samdb, setup_path, names):
895 """Setup the SamDB rootdse.
897 :param samdb: Sam Database handle
898 :param setup_path: Obtain setup path
900 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
901 "SCHEMADN": names.schemadn,
902 "DOMAINDN": names.domaindn,
903 "ROOTDN": names.rootdn,
904 "CONFIGDN": names.configdn,
905 "SERVERDN": names.serverdn,
909 def setup_self_join(samdb, names,
910 machinepass, dnspass,
911 domainsid, next_rid, invocationid, setup_path,
912 policyguid, policyguid_dc, domainControllerFunctionality,
914 """Join a host to its own domain."""
915 assert isinstance(invocationid, str)
916 if ntdsguid is not None:
917 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
920 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
921 "CONFIGDN": names.configdn,
922 "SCHEMADN": names.schemadn,
923 "DOMAINDN": names.domaindn,
924 "SERVERDN": names.serverdn,
925 "INVOCATIONID": invocationid,
926 "NETBIOSNAME": names.netbiosname,
927 "DEFAULTSITE": names.sitename,
928 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
929 "MACHINEPASS_B64": b64encode(machinepass),
930 "REALM": names.realm,
931 "DOMAIN": names.domain,
932 "DOMAINSID": str(domainsid),
933 "DCRID": str(next_rid),
934 "DNSDOMAIN": names.dnsdomain,
935 "SAMBA_VERSION_STRING": version,
936 "NTDSGUID": ntdsguid_line,
937 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
939 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
940 "POLICYGUID": policyguid,
941 "POLICYGUID_DC": policyguid_dc,
942 "DNSDOMAIN": names.dnsdomain,
943 "DOMAINSID": str(domainsid),
944 "DOMAINDN": names.domaindn})
946 # add the NTDSGUID based SPNs
947 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
948 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
949 expression="", scope=ldb.SCOPE_BASE)
950 assert isinstance(names.ntdsguid, str)
952 # Setup fSMORoleOwner entries to point at the newly created DC entry
953 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
954 "DOMAIN": names.domain,
955 "DNSDOMAIN": names.dnsdomain,
956 "DOMAINDN": names.domaindn,
957 "CONFIGDN": names.configdn,
958 "SCHEMADN": names.schemadn,
959 "DEFAULTSITE": names.sitename,
960 "SERVERDN": names.serverdn,
961 "NETBIOSNAME": names.netbiosname,
962 "NTDSGUID": names.ntdsguid,
963 "RIDALLOCATIONSTART": str(next_rid + 100),
964 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
967 # This is partially Samba4 specific and should be replaced by the correct
969 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
970 "DNSDOMAIN": names.dnsdomain,
971 "DOMAINDN": names.domaindn,
972 "DNSPASS_B64": b64encode(dnspass),
973 "HOSTNAME" : names.hostname,
974 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
977 def getpolicypath(sysvolpath, dnsdomain, guid):
978 """Return the physical path of policy given its guid.
980 :param sysvolpath: Path to the sysvol folder
981 :param dnsdomain: DNS name of the AD domain
982 :param guid: The GUID of the policy
983 :return: A string with the complete path to the policy folder
988 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
991 def create_gpo_struct(policy_path):
992 if not os.path.exists(policy_path):
993 os.makedirs(policy_path, 0775)
994 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
995 "[General]\r\nVersion=0")
996 p = os.path.join(policy_path, "MACHINE")
997 if not os.path.exists(p):
999 p = os.path.join(policy_path, "USER")
1000 if not os.path.exists(p):
1001 os.makedirs(p, 0775)
1004 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1005 """Create the default GPO for a domain
1007 :param sysvolpath: Physical path for the sysvol folder
1008 :param dnsdomain: DNS domain name of the AD domain
1009 :param policyguid: GUID of the default domain policy
1010 :param policyguid_dc: GUID of the default domain controler policy
1013 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1014 create_gpo_struct(policy_path)
1016 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1017 create_gpo_struct(policy_path)
1020 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1021 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1022 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1023 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1025 """Setup a complete SAM Database.
1027 :note: This will wipe the main SAM database file!
1031 # Provision does not make much sense values larger than 1000000000
1032 # as the upper range of the rIDAvailablePool is 1073741823 and
1033 # we don't want to create a domain that cannot allocate rids.
1034 if next_rid < 1000 or next_rid > 1000000000:
1035 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1036 error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
1037 raise ProvisioningError(error)
1039 # ATTENTION: Do NOT change these default values without discussion with the
1040 # team and/or release manager. They have a big impact on the whole program!
1041 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1043 if dom_for_fun_level is None:
1044 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1046 if dom_for_fun_level > domainControllerFunctionality:
1047 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!")
1049 domainFunctionality = dom_for_fun_level
1050 forestFunctionality = dom_for_fun_level
1052 # Also wipes the database
1053 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1054 provision_backend=provision_backend, session_info=session_info,
1055 names=names, serverrole=serverrole, schema=schema)
1058 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1060 # Load the database, but don's load the global schema and don't connect quite yet
1061 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1062 credentials=provision_backend.credentials, lp=lp, global_schema=False,
1065 logger.info("Pre-loading the Samba 4 and AD schema")
1067 # Load the schema from the one we computed earlier
1068 samdb.set_schema(schema)
1070 # Set the NTDS settings DN manually - in order to have it already around
1071 # before the provisioned tree exists and we connect
1072 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1074 # And now we can connect to the DB - the schema won't be loaded from the DB
1077 if fill == FILL_DRS:
1080 samdb.transaction_start()
1082 # Set the domain functionality levels onto the database.
1083 # Various module (the password_hash module in particular) need
1084 # to know what level of AD we are emulating.
1086 # These will be fixed into the database via the database
1087 # modifictions below, but we need them set from the start.
1088 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1089 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1090 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1092 samdb.set_domain_sid(str(domainsid))
1093 samdb.set_invocation_id(invocationid)
1095 logger.info("Adding DomainDN: %s" % names.domaindn)
1097 #impersonate domain admin
1098 admin_session_info = admin_session(lp, str(domainsid))
1099 samdb.set_session_info(admin_session_info)
1100 if domainguid is not None:
1101 domainguid_line = "objectGUID: %s\n-" % domainguid
1103 domainguid_line = ""
1105 descr = b64encode(get_domain_descriptor(domainsid))
1106 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1107 "DOMAINDN": names.domaindn,
1108 "DOMAINGUID": domainguid_line,
1113 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1114 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1115 "DOMAINSID": str(domainsid),
1116 "NEXTRID": str(next_rid),
1117 "SCHEMADN": names.schemadn,
1118 "NETBIOSNAME": names.netbiosname,
1119 "DEFAULTSITE": names.sitename,
1120 "CONFIGDN": names.configdn,
1121 "SERVERDN": names.serverdn,
1122 "POLICYGUID": policyguid,
1123 "DOMAINDN": names.domaindn,
1124 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1125 "SAMBA_VERSION_STRING": version
1128 logger.info("Adding configuration container")
1129 descr = b64encode(get_config_descriptor(domainsid))
1130 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1131 "CONFIGDN": names.configdn,
1132 "DESCRIPTOR": descr,
1135 # The LDIF here was created when the Schema object was constructed
1136 logger.info("Setting up sam.ldb schema")
1137 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1138 samdb.modify_ldif(schema.schema_dn_modify)
1139 samdb.write_prefixes_from_schema()
1140 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1141 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1142 {"SCHEMADN": names.schemadn})
1144 logger.info("Reopening sam.ldb with new schema")
1146 samdb.transaction_cancel()
1149 samdb.transaction_commit()
1151 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1152 credentials=provision_backend.credentials, lp=lp,
1153 global_schema=False, am_rodc=am_rodc)
1155 # Set the NTDS settings DN manually - in order to have it already around
1156 # before the provisioned tree exists and we connect
1157 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1161 samdb.transaction_start()
1163 samdb.invocation_id = invocationid
1165 logger.info("Setting up sam.ldb configuration data")
1166 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1167 "CONFIGDN": names.configdn,
1168 "NETBIOSNAME": names.netbiosname,
1169 "DEFAULTSITE": names.sitename,
1170 "DNSDOMAIN": names.dnsdomain,
1171 "DOMAIN": names.domain,
1172 "SCHEMADN": names.schemadn,
1173 "DOMAINDN": names.domaindn,
1174 "SERVERDN": names.serverdn,
1175 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1176 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
1179 logger.info("Setting up display specifiers")
1180 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1181 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1182 check_all_substituted(display_specifiers_ldif)
1183 samdb.add_ldif(display_specifiers_ldif)
1185 logger.info("Adding users container")
1186 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1187 "DOMAINDN": names.domaindn})
1188 logger.info("Modifying users container")
1189 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1190 "DOMAINDN": names.domaindn})
1191 logger.info("Adding computers container")
1192 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1193 "DOMAINDN": names.domaindn})
1194 logger.info("Modifying computers container")
1195 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1196 "DOMAINDN": names.domaindn})
1197 logger.info("Setting up sam.ldb data")
1198 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1199 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1200 "DOMAINDN": names.domaindn,
1201 "NETBIOSNAME": names.netbiosname,
1202 "DEFAULTSITE": names.sitename,
1203 "CONFIGDN": names.configdn,
1204 "SERVERDN": names.serverdn,
1205 "RIDAVAILABLESTART": str(next_rid + 600),
1206 "POLICYGUID_DC": policyguid_dc
1209 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1210 "DOMAINDN": names.domaindn})
1212 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1213 "CONFIGDN": names.configdn,
1214 "SCHEMADN": names.schemadn})
1215 if fill == FILL_FULL:
1216 logger.info("Setting up sam.ldb users and groups")
1217 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1218 "DOMAINDN": names.domaindn,
1219 "DOMAINSID": str(domainsid),
1220 "CONFIGDN": names.configdn,
1221 "ADMINPASS_B64": b64encode(adminpass),
1222 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1225 logger.info("Setting up self join")
1226 setup_self_join(samdb, names=names, invocationid=invocationid,
1228 machinepass=machinepass,
1229 domainsid=domainsid,
1231 policyguid=policyguid,
1232 policyguid_dc=policyguid_dc,
1233 setup_path=setup_path,
1234 domainControllerFunctionality=domainControllerFunctionality,
1237 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1238 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1239 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1240 assert isinstance(names.ntdsguid, str)
1242 samdb.transaction_cancel()
1245 samdb.transaction_commit()
1250 FILL_NT4SYNC = "NT4SYNC"
1252 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1253 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)"
1255 def set_dir_acl(path, acl, lp, domsid):
1256 setntacl(lp, path, acl, domsid)
1257 for root, dirs, files in os.walk(path, topdown=False):
1259 setntacl(lp, os.path.join(root, name), acl, domsid)
1261 setntacl(lp, os.path.join(root, name), acl, domsid)
1264 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1265 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1268 :param sysvol: Physical path for the sysvol folder
1269 :param dnsdomain: The DNS name of the domain
1270 :param domainsid: The SID of the domain
1271 :param domaindn: The DN of the domain (ie. DC=...)
1272 :param samdb: An LDB object on the SAM db
1273 :param lp: an LP object
1276 # Set ACL for GPO root folder
1277 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1278 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1280 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1281 attrs=["cn", "nTSecurityDescriptor"],
1282 expression="", scope=ldb.SCOPE_ONELEVEL)
1285 acl = ndr_unpack(security.descriptor,
1286 str(policy["nTSecurityDescriptor"])).as_sddl()
1287 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1288 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1291 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1293 """Set the ACL for the sysvol share and the subfolders
1295 :param samdb: An LDB object on the SAM db
1296 :param netlogon: Physical path for the netlogon folder
1297 :param sysvol: Physical path for the sysvol folder
1298 :param gid: The GID of the "Domain adminstrators" group
1299 :param domainsid: The SID of the domain
1300 :param dnsdomain: The DNS name of the domain
1301 :param domaindn: The DN of the domain (ie. DC=...)
1305 os.chown(sysvol,-1,gid)
1311 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1312 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1313 for root, dirs, files in os.walk(sysvol, topdown=False):
1316 os.chown(os.path.join(root, name), -1, gid)
1317 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1320 os.chown(os.path.join(root, name), -1, gid)
1321 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1323 # Set acls on Policy folder and policies folders
1324 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1327 def provision(setup_dir, logger, session_info,
1328 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1330 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1332 domain=None, hostname=None, hostip=None, hostip6=None,
1333 domainsid=None, next_rid=1000,
1334 adminpass=None, ldapadminpass=None,
1335 krbtgtpass=None, domainguid=None,
1336 policyguid=None, policyguid_dc=None, invocationid=None,
1337 machinepass=None, ntdsguid=None,
1338 dnspass=None, root=None, nobody=None, users=None,
1339 wheel=None, backup=None, aci=None, serverrole=None,
1340 dom_for_fun_level=None,
1341 ldap_backend_extra_port=None, backend_type=None,
1343 ol_mmr_urls=None, ol_olc=None,
1344 setup_ds_path=None, slapd_path=None, nosync=False,
1345 ldap_dryrun_mode=False, useeadb=False, am_rodc=False):
1348 :note: caution, this wipes all existing data!
1351 def setup_path(file):
1352 return os.path.join(setup_dir, file)
1354 if domainsid is None:
1355 domainsid = security.random_sid()
1357 domainsid = security.dom_sid(domainsid)
1359 # create/adapt the group policy GUIDs
1360 # Default GUID for default policy are described at
1361 # "How Core Group Policy Works"
1362 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1363 if policyguid is None:
1364 policyguid = DEFAULT_POLICY_GUID
1365 policyguid = policyguid.upper()
1366 if policyguid_dc is None:
1367 policyguid_dc = DEFAULT_DC_POLICY_GUID
1368 policyguid_dc = policyguid_dc.upper()
1370 if adminpass is None:
1371 adminpass = samba.generate_random_password(12, 32)
1372 if krbtgtpass is None:
1373 krbtgtpass = samba.generate_random_password(128, 255)
1374 if machinepass is None:
1375 machinepass = samba.generate_random_password(128, 255)
1377 dnspass = samba.generate_random_password(128, 255)
1378 if ldapadminpass is None:
1379 #Make a new, random password between Samba and it's LDAP server
1380 ldapadminpass=samba.generate_random_password(128, 255)
1382 if backend_type is None:
1383 backend_type = "ldb"
1385 sid_generator = "internal"
1386 if backend_type == "fedora-ds":
1387 sid_generator = "backend"
1389 root_uid = findnss_uid([root or "root"])
1390 nobody_uid = findnss_uid([nobody or "nobody"])
1391 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1393 wheel_gid = findnss_gid(["wheel", "adm"])
1395 wheel_gid = findnss_gid([wheel])
1397 bind_gid = findnss_gid(["bind", "named"])
1401 if targetdir is not None:
1402 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1403 elif smbconf is None:
1404 smbconf = samba.param.default_path()
1405 if not os.path.exists(os.path.dirname(smbconf)):
1406 os.makedirs(os.path.dirname(smbconf))
1408 # only install a new smb.conf if there isn't one there already
1409 if os.path.exists(smbconf):
1410 # if Samba Team members can't figure out the weird errors
1411 # loading an empty smb.conf gives, then we need to be smarter.
1412 # Pretend it just didn't exist --abartlet
1413 data = open(smbconf, 'r').read()
1414 data = data.lstrip()
1415 if data is None or data == "":
1416 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1417 serverrole, targetdir, sid_generator, useeadb)
1419 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1420 targetdir, sid_generator, useeadb)
1422 lp = samba.param.LoadParm()
1425 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1426 dnsdomain=realm, serverrole=serverrole,
1427 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1428 serverdn=serverdn, sitename=sitename)
1430 paths = provision_paths_from_lp(lp, names.dnsdomain)
1432 paths.bind_gid = bind_gid
1435 hostips = samba.interface_ips(lp, False)
1436 if len(hostips) == 0:
1437 logger.warning("No external IPv4 address has been found. Using loopback.")
1438 hostip = '127.0.0.1'
1441 if len(hostips) > 1:
1442 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1446 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1449 if hostip6 == '::1' and ip[-1][0] != '::1':
1451 except socket.gaierror, (socket.EAI_NODATA, msg):
1454 if serverrole is None:
1455 serverrole = lp.get("server role")
1457 assert serverrole in ("domain controller", "member server", "standalone")
1458 if invocationid is None:
1459 invocationid = str(uuid.uuid4())
1461 if not os.path.exists(paths.private_dir):
1462 os.mkdir(paths.private_dir)
1463 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1464 os.mkdir(os.path.join(paths.private_dir, "tls"))
1466 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1468 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1469 serverdn=names.serverdn)
1471 if backend_type == "ldb":
1472 provision_backend = LDBBackend(backend_type,
1473 paths=paths, setup_path=setup_path,
1474 lp=lp, credentials=credentials,
1477 elif backend_type == "existing":
1478 provision_backend = ExistingBackend(backend_type,
1479 paths=paths, setup_path=setup_path,
1480 lp=lp, credentials=credentials,
1483 ldapi_url=ldapi_url)
1484 elif backend_type == "fedora-ds":
1485 provision_backend = FDSBackend(backend_type,
1486 paths=paths, setup_path=setup_path,
1487 lp=lp, credentials=credentials,
1490 domainsid=domainsid,
1493 ldapadminpass=ldapadminpass,
1494 slapd_path=slapd_path,
1495 ldap_backend_extra_port=ldap_backend_extra_port,
1496 ldap_dryrun_mode=ldap_dryrun_mode,
1498 setup_ds_path=setup_ds_path)
1499 elif backend_type == "openldap":
1500 provision_backend = OpenLDAPBackend(backend_type,
1501 paths=paths, setup_path=setup_path,
1502 lp=lp, credentials=credentials,
1505 domainsid=domainsid,
1508 ldapadminpass=ldapadminpass,
1509 slapd_path=slapd_path,
1510 ldap_backend_extra_port=ldap_backend_extra_port,
1511 ldap_dryrun_mode=ldap_dryrun_mode,
1512 ol_mmr_urls=ol_mmr_urls,
1515 raise ValueError("Unknown LDAP backend type selected")
1517 provision_backend.init()
1518 provision_backend.start()
1520 # only install a new shares config db if there is none
1521 if not os.path.exists(paths.shareconf):
1522 logger.info("Setting up share.ldb")
1523 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1525 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1527 logger.info("Setting up secrets.ldb")
1528 secrets_ldb = setup_secretsdb(paths, setup_path,
1529 session_info=session_info,
1530 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1533 logger.info("Setting up the registry")
1534 setup_registry(paths.hklm, setup_path, session_info,
1537 logger.info("Setting up the privileges database")
1538 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1540 logger.info("Setting up idmap db")
1541 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1544 logger.info("Setting up SAM db")
1545 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1546 provision_backend, lp, names,
1548 domainsid=domainsid,
1549 schema=schema, domainguid=domainguid,
1550 policyguid=policyguid, policyguid_dc=policyguid_dc,
1552 adminpass=adminpass, krbtgtpass=krbtgtpass,
1553 invocationid=invocationid,
1554 machinepass=machinepass, dnspass=dnspass,
1555 ntdsguid=ntdsguid, serverrole=serverrole,
1556 dom_for_fun_level=dom_for_fun_level,
1557 am_rodc=am_rodc, next_rid=next_rid)
1559 if serverrole == "domain controller":
1560 if paths.netlogon is None:
1561 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1562 logger.info("Please either remove %s or see the template at %s" %
1563 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1564 assert paths.netlogon is not None
1566 if paths.sysvol is None:
1567 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1568 " are configuring a DC.")
1569 logger.info("Please either remove %s or see the template at %s" %
1570 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1571 assert paths.sysvol is not None
1573 if not os.path.isdir(paths.netlogon):
1574 os.makedirs(paths.netlogon, 0755)
1576 if samdb_fill == FILL_FULL:
1577 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1578 root_uid=root_uid, nobody_uid=nobody_uid,
1579 users_gid=users_gid, wheel_gid=wheel_gid)
1581 if serverrole == "domain controller":
1582 # Set up group policies (domain policy and domain controller policy)
1583 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1584 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1585 domainsid, names.dnsdomain, names.domaindn, lp)
1587 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1588 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1590 secretsdb_self_join(secrets_ldb, domain=names.domain,
1592 dnsdomain=names.dnsdomain,
1593 netbiosname=names.netbiosname,
1594 domainsid=domainsid,
1595 machinepass=machinepass,
1596 secure_channel_type=SEC_CHAN_BDC)
1598 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1599 # In future, this might be determined from some configuration
1600 kerberos_enctypes = str(ENC_ALL_TYPES)
1603 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1604 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
1605 flags=ldb.FLAG_MOD_REPLACE,
1606 name="msDS-SupportedEncryptionTypes")
1608 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1609 # It might be that this attribute does not exist in this schema
1613 if serverrole == "domain controller":
1614 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1616 realm=names.realm, dnsdomain=names.dnsdomain,
1617 dns_keytab_path=paths.dns_keytab,
1620 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1621 assert isinstance(domainguid, str)
1623 # Only make a zone file on the first DC, it should be replicated
1624 # with DNS replication
1625 create_zone_file(lp, logger, paths, targetdir, setup_path,
1626 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1627 hostname=names.hostname, realm=names.realm,
1628 domainguid=domainguid, ntdsguid=names.ntdsguid)
1630 create_named_conf(paths, setup_path, realm=names.realm,
1631 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1633 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1634 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1635 keytab_name=paths.dns_keytab)
1636 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1637 logger.info("and %s for further documentation required for secure DNS "
1638 "updates", paths.namedtxt)
1640 lastProvisionUSNs = get_last_provision_usn(samdb)
1641 maxUSN = get_max_usn(samdb, str(names.rootdn))
1642 if lastProvisionUSNs is not None:
1643 update_provision_usn(samdb, 0, maxUSN, 1)
1645 set_provision_usn(samdb, 0, maxUSN)
1647 create_krb5_conf(paths.krb5conf, setup_path,
1648 dnsdomain=names.dnsdomain, hostname=names.hostname,
1650 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1651 "generated at %s", paths.krb5conf)
1653 if serverrole == "domain controller":
1654 create_dns_update_list(lp, logger, paths, setup_path)
1656 provision_backend.post_setup()
1657 provision_backend.shutdown()
1659 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1662 secrets_ldb.transaction_cancel()
1665 #Now commit the secrets.ldb to disk
1666 secrets_ldb.transaction_commit()
1668 # the commit creates the dns.keytab, now chown it
1669 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1670 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1672 os.chmod(dns_keytab_path, 0640)
1673 os.chown(dns_keytab_path, -1, paths.bind_gid)
1675 if not os.environ.has_key('SAMBA_SELFTEST'):
1676 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1680 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1681 paths.phpldapadminconfig)
1683 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1684 logger.info("Server Role: %s" % serverrole)
1685 logger.info("Hostname: %s" % names.hostname)
1686 logger.info("NetBIOS Domain: %s" % names.domain)
1687 logger.info("DNS Domain: %s" % names.dnsdomain)
1688 logger.info("DOMAIN SID: %s" % str(domainsid))
1689 if samdb_fill == FILL_FULL:
1690 logger.info("Admin password: %s" % adminpass)
1691 if provision_backend.type is not "ldb":
1692 if provision_backend.credentials.get_bind_dn() is not None:
1693 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1695 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1697 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1699 if provision_backend.slapd_command_escaped is not None:
1700 # now display slapd_command_file.txt to show how slapd must be started next time
1701 logger.info("Use later the following commandline to start slapd, then Samba:")
1702 logger.info(provision_backend.slapd_command_escaped)
1703 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1704 provision_backend.ldapdir)
1706 result = ProvisionResult()
1707 result.domaindn = domaindn
1708 result.paths = paths
1710 result.samdb = samdb
1714 def provision_become_dc(setup_dir=None,
1715 smbconf=None, targetdir=None, realm=None,
1716 rootdn=None, domaindn=None, schemadn=None,
1717 configdn=None, serverdn=None,
1718 domain=None, hostname=None, domainsid=None,
1719 adminpass=None, krbtgtpass=None, domainguid=None,
1720 policyguid=None, policyguid_dc=None, invocationid=None,
1722 dnspass=None, root=None, nobody=None, users=None,
1723 wheel=None, backup=None, serverrole=None,
1724 ldap_backend=None, ldap_backend_type=None,
1725 sitename=None, debuglevel=1):
1727 logger = logging.getLogger("provision")
1728 samba.set_debug_level(debuglevel)
1730 res = provision(setup_dir, logger, system_session(), None,
1731 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1732 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1733 configdn=configdn, serverdn=serverdn, domain=domain,
1734 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1735 machinepass=machinepass, serverrole="domain controller",
1737 res.lp.set("debuglevel", str(debuglevel))
1741 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1742 """Create a PHP LDAP admin configuration file.
1744 :param path: Path to write the configuration to.
1745 :param setup_path: Function to generate setup paths.
1747 setup_file(setup_path("phpldapadmin-config.php"), path,
1748 {"S4_LDAPI_URI": ldapi_uri})
1751 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1752 hostip, hostip6, hostname, realm, domainguid,
1754 """Write out a DNS zone file, from the info in the current database.
1756 :param paths: paths object
1757 :param setup_path: Setup path function.
1758 :param dnsdomain: DNS Domain name
1759 :param domaindn: DN of the Domain
1760 :param hostip: Local IPv4 IP
1761 :param hostip6: Local IPv6 IP
1762 :param hostname: Local hostname
1763 :param realm: Realm name
1764 :param domainguid: GUID of the domain.
1765 :param ntdsguid: GUID of the hosts nTDSDSA record.
1767 assert isinstance(domainguid, str)
1769 if hostip6 is not None:
1770 hostip6_base_line = " IN AAAA " + hostip6
1771 hostip6_host_line = hostname + " IN AAAA " + hostip6
1772 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1774 hostip6_base_line = ""
1775 hostip6_host_line = ""
1776 gc_msdcs_ip6_line = ""
1778 if hostip is not None:
1779 hostip_base_line = " IN A " + hostip
1780 hostip_host_line = hostname + " IN A " + hostip
1781 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1783 hostip_base_line = ""
1784 hostip_host_line = ""
1785 gc_msdcs_ip_line = ""
1787 dns_dir = os.path.dirname(paths.dns)
1790 shutil.rmtree(dns_dir, True)
1794 os.mkdir(dns_dir, 0775)
1796 # we need to freeze the zone while we update the contents
1797 if targetdir is None:
1798 rndc = ' '.join(lp.get("rndc command"))
1799 os.system(rndc + " freeze " + lp.get("realm"))
1801 setup_file(setup_path("provision.zone"), paths.dns, {
1802 "HOSTNAME": hostname,
1803 "DNSDOMAIN": dnsdomain,
1805 "HOSTIP_BASE_LINE": hostip_base_line,
1806 "HOSTIP_HOST_LINE": hostip_host_line,
1807 "DOMAINGUID": domainguid,
1808 "DATESTRING": time.strftime("%Y%m%d%H"),
1809 "DEFAULTSITE": DEFAULTSITE,
1810 "NTDSGUID": ntdsguid,
1811 "HOSTIP6_BASE_LINE": hostip6_base_line,
1812 "HOSTIP6_HOST_LINE": hostip6_host_line,
1813 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1814 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1817 # note that we use no variable substitution on this file
1818 # the substitution is done at runtime by samba_dnsupdate
1819 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1821 # and the SPN update list
1822 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1824 if paths.bind_gid is not None:
1826 os.chown(dns_dir, -1, paths.bind_gid)
1827 os.chown(paths.dns, -1, paths.bind_gid)
1828 # chmod needed to cope with umask
1829 os.chmod(dns_dir, 0775)
1830 os.chmod(paths.dns, 0664)
1832 if not os.environ.has_key('SAMBA_SELFTEST'):
1833 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1835 if targetdir is None:
1836 os.system(rndc + " unfreeze " + lp.get("realm"))
1839 def create_dns_update_list(lp, logger, paths, setup_path):
1840 """Write out a dns_update_list file"""
1841 # note that we use no variable substitution on this file
1842 # the substitution is done at runtime by samba_dnsupdate
1843 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1844 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1847 def create_named_conf(paths, setup_path, realm, dnsdomain,
1849 """Write out a file containing zone statements suitable for inclusion in a
1850 named.conf file (including GSS-TSIG configuration).
1852 :param paths: all paths
1853 :param setup_path: Setup path function.
1854 :param realm: Realm name
1855 :param dnsdomain: DNS Domain name
1856 :param private_dir: Path to private directory
1857 :param keytab_name: File name of DNS keytab file
1860 setup_file(setup_path("named.conf"), paths.namedconf, {
1861 "DNSDOMAIN": dnsdomain,
1863 "ZONE_FILE": paths.dns,
1864 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1865 "NAMED_CONF": paths.namedconf,
1866 "NAMED_CONF_UPDATE": paths.namedconf_update
1869 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1872 def create_named_txt(path, setup_path, realm, dnsdomain,
1873 private_dir, keytab_name):
1874 """Write out a file containing zone statements suitable for inclusion in a
1875 named.conf file (including GSS-TSIG configuration).
1877 :param path: Path of the new named.conf file.
1878 :param setup_path: Setup path function.
1879 :param realm: Realm name
1880 :param dnsdomain: DNS Domain name
1881 :param private_dir: Path to private directory
1882 :param keytab_name: File name of DNS keytab file
1885 setup_file(setup_path("named.txt"), path, {
1886 "DNSDOMAIN": dnsdomain,
1888 "DNS_KEYTAB": keytab_name,
1889 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1890 "PRIVATE_DIR": private_dir
1894 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1895 """Write out a file containing zone statements suitable for inclusion in a
1896 named.conf file (including GSS-TSIG configuration).
1898 :param path: Path of the new named.conf file.
1899 :param setup_path: Setup path function.
1900 :param dnsdomain: DNS Domain name
1901 :param hostname: Local hostname
1902 :param realm: Realm name
1904 setup_file(setup_path("krb5.conf"), path, {
1905 "DNSDOMAIN": dnsdomain,
1906 "HOSTNAME": hostname,
1911 class ProvisioningError(Exception):
1912 """A generic provision error."""
1914 def __init__(self, value):
1918 return "ProvisioningError: " + self.value
1921 class InvalidNetbiosName(Exception):
1922 """A specified name was not a valid NetBIOS name."""
1923 def __init__(self, name):
1924 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)