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
46 check_all_substituted,
54 from samba.dsdb import (
55 DS_DOMAIN_FUNCTION_2003,
56 DS_DOMAIN_FUNCTION_2008_R2,
59 from samba.dcerpc import security
60 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
61 from samba.idmap import IDmapDB
62 from samba.ms_display_specifiers import read_ms_ldif
63 from samba.ntacls import setntacl, dsacl2fsacl
64 from samba.ndr import ndr_pack,ndr_unpack
65 from samba.provisionbackend import (
73 from samba.schema import Schema
74 from samba.samdb import SamDB
76 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
77 __docformat__ = "restructuredText"
78 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
79 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
82 """Find the setup directory used by provision."""
85 dirname = os.path.dirname(__file__)
86 return os.path.normpath(os.path.join(dirname, "../../../setup"))
89 for prefix in [sys.prefix,
90 os.path.join(os.path.dirname(__file__), "../../../..")]:
91 for suffix in ["share/setup", "share/samba/setup", "setup"]:
92 ret = os.path.normpath(os.path.join(prefix, suffix))
93 if os.path.isdir(ret):
95 raise Exception("Unable to find setup directory.")
97 # Descriptors of naming contexts and other important objects
99 # "get_schema_descriptor" is located in "schema.py"
101 def get_sites_descriptor(domain_sid):
102 sddl = "O:EAG:EAD:AI(A;;RPLCLORC;;;AU)" \
103 "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \
104 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
105 "(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
106 "(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
107 "S:AI(AU;CISA;CCDCSDDT;;;WD)" \
108 "(OU;CIIOSA;CR;;f0f8ffab-1191-11d0-a060-00aa006c33ed;WD)" \
109 "(OU;CIIOSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
110 "(OU;CIIOSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
111 "(OU;CIIOSA;WP;3e10944c-c354-11d0-aff8-0000f80367c1;b7b13124-b82e-11d0-afee-0000f80367c1;WD)"
112 sec = security.descriptor.from_sddl(sddl, domain_sid)
115 def get_config_descriptor(domain_sid):
116 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
117 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
118 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
119 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
120 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
121 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
122 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
123 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
124 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
125 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
126 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
127 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
128 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
129 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
130 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
131 sec = security.descriptor.from_sddl(sddl, domain_sid)
134 def get_domain_descriptor(domain_sid):
135 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
136 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
137 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
138 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
139 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
140 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
141 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
142 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
143 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
144 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
145 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
146 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
147 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
148 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
149 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
150 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
151 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
152 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
153 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
154 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
155 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
156 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
157 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
158 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
159 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
160 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
161 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
162 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
163 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
164 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
165 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
166 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
167 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
168 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
169 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
170 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
171 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
172 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
173 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
176 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
178 "(A;;RPLCLORC;;;ED)" \
179 "(A;;RPLCLORC;;;AU)" \
180 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
181 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
182 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
183 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
184 sec = security.descriptor.from_sddl(sddl, domain_sid)
187 DEFAULTSITE = "Default-First-Site-Name"
188 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
190 class ProvisionPaths(object):
193 self.shareconf = None
204 self.dns_keytab = None
207 self.private_dir = None
210 class ProvisionNames(object):
217 self.ldapmanagerdn = None
218 self.dnsdomain = None
220 self.netbiosname = None
227 def update_provision_usn(samdb, low, high, replace=False):
228 """Update the field provisionUSN in sam.ldb
230 This field is used to track range of USN modified by provision and
232 This value is used afterward by next provision to figure out if
233 the field have been modified since last provision.
235 :param samdb: An LDB object connect to sam.ldb
236 :param low: The lowest USN modified by this upgrade
237 :param high: The highest USN modified by this upgrade
238 :param replace: A boolean indicating if the range should replace any
239 existing one or appended (default)
244 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" % \
245 LAST_PROVISION_USN_ATTRIBUTE, base="",
246 scope=ldb.SCOPE_SUBTREE,
247 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
248 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
251 tab.append("%s-%s" % (low, high))
252 delta = ldb.Message()
253 delta.dn = ldb.Dn(samdb, "@PROVISION")
254 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
255 ldb.FLAG_MOD_REPLACE,
256 LAST_PROVISION_USN_ATTRIBUTE)
260 def set_provision_usn(samdb, low, high):
261 """Set the field provisionUSN in sam.ldb
262 This field is used to track range of USN modified by provision and
264 This value is used afterward by next provision to figure out if
265 the field have been modified since last provision.
267 :param samdb: An LDB object connect to sam.ldb
268 :param low: The lowest USN modified by this upgrade
269 :param high: The highest USN modified by this upgrade"""
271 tab.append("%s-%s" % (low, high))
272 delta = ldb.Message()
273 delta.dn = ldb.Dn(samdb, "@PROVISION")
274 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
276 LAST_PROVISION_USN_ATTRIBUTE)
280 def get_max_usn(samdb,basedn):
281 """ This function return the biggest USN present in the provision
283 :param samdb: A LDB object pointing to the sam.ldb
284 :param basedn: A string containing the base DN of the provision
286 :return: The biggest USN in the provision"""
288 res = samdb.search(expression="objectClass=*",base=basedn,
289 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
290 controls=["search_options:1:2",
291 "server_sort:1:1:uSNChanged",
292 "paged_results:1:1"])
293 return res[0]["uSNChanged"]
295 def get_last_provision_usn(sam):
296 """Get the lastest USN modified by a provision or an upgradeprovision
298 :param sam: An LDB object pointing to the sam.ldb
299 :return an integer corresponding to the highest USN modified by
300 (upgrade)provision, 0 is this value is unknown"""
302 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" % \
303 LAST_PROVISION_USN_ATTRIBUTE,
304 base="", scope=ldb.SCOPE_SUBTREE,
305 attrs=[LAST_PROVISION_USN_ATTRIBUTE])
310 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
311 tab = p.split(str(r))
319 class ProvisionResult(object):
328 def check_install(lp, session_info, credentials):
329 """Check whether the current install seems ok.
331 :param lp: Loadparm context
332 :param session_info: Session information
333 :param credentials: Credentials
335 if lp.get("realm") == "":
336 raise Exception("Realm empty")
337 samdb = Ldb(lp.get("sam database"), session_info=session_info,
338 credentials=credentials, lp=lp)
339 if len(samdb.search("(cn=Administrator)")) != 1:
340 raise ProvisioningError("No administrator account found")
343 def findnss(nssfn, names):
344 """Find a user or group from a list of possibilities.
346 :param nssfn: NSS Function to try (should raise KeyError if not found)
347 :param names: Names to check.
348 :return: Value return by first names list.
355 raise KeyError("Unable to find user/group in %r" % names)
358 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
359 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
362 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
363 """Setup a ldb in the private dir.
365 :param ldb: LDB file to import data into
366 :param ldif_path: Path of the LDIF file to load
367 :param subst_vars: Optional variables to subsitute in LDIF.
368 :param nocontrols: Optional list of controls, can be None for no controls
370 assert isinstance(ldif_path, str)
371 data = read_and_sub_file(ldif_path, subst_vars)
372 ldb.add_ldif(data, controls)
375 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
376 """Modify a ldb in the private dir.
378 :param ldb: LDB object.
379 :param ldif_path: LDIF file path.
380 :param subst_vars: Optional dictionary with substitution variables.
382 data = read_and_sub_file(ldif_path, subst_vars)
383 ldb.modify_ldif(data, controls)
386 def setup_ldb(ldb, ldif_path, subst_vars):
387 """Import a LDIF a file into a LDB handle, optionally substituting variables.
389 :note: Either all LDIF data will be added or none (using transactions).
391 :param ldb: LDB file to import into.
392 :param ldif_path: Path to the LDIF file.
393 :param subst_vars: Dictionary with substitution variables.
395 assert ldb is not None
396 ldb.transaction_start()
398 setup_add_ldif(ldb, ldif_path, subst_vars)
400 ldb.transaction_cancel()
403 ldb.transaction_commit()
406 def provision_paths_from_lp(lp, dnsdomain):
407 """Set the default paths for provisioning.
409 :param lp: Loadparm context.
410 :param dnsdomain: DNS Domain name
412 paths = ProvisionPaths()
413 paths.private_dir = lp.get("private dir")
415 # This is stored without path prefix for the "privateKeytab" attribute in
416 # "secrets_dns.ldif".
417 paths.dns_keytab = "dns.keytab"
418 paths.keytab = "secrets.keytab"
420 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
421 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
422 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
423 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
424 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
425 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
426 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
427 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
428 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
429 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
430 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
431 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
432 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
433 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
434 paths.phpldapadminconfig = os.path.join(paths.private_dir,
435 "phpldapadmin-config.php")
436 paths.hklm = "hklm.ldb"
437 paths.hkcr = "hkcr.ldb"
438 paths.hkcu = "hkcu.ldb"
439 paths.hku = "hku.ldb"
440 paths.hkpd = "hkpd.ldb"
441 paths.hkpt = "hkpt.ldb"
442 paths.sysvol = lp.get("path", "sysvol")
443 paths.netlogon = lp.get("path", "netlogon")
444 paths.smbconf = lp.configfile
448 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
449 serverrole=None, rootdn=None, domaindn=None, configdn=None,
450 schemadn=None, serverdn=None, sitename=None):
451 """Guess configuration settings to use."""
454 hostname = socket.gethostname().split(".")[0]
456 netbiosname = lp.get("netbios name")
457 if netbiosname is None:
458 netbiosname = hostname
459 # remove forbidden chars
461 for x in netbiosname:
462 if x.isalnum() or x in VALID_NETBIOS_CHARS:
463 newnbname = "%s%c" % (newnbname, x)
464 #force the length to be <16
465 netbiosname = newnbname[0:15]
466 assert netbiosname is not None
467 netbiosname = netbiosname.upper()
468 if not valid_netbios_name(netbiosname):
469 raise InvalidNetbiosName(netbiosname)
471 if dnsdomain is None:
472 dnsdomain = lp.get("realm")
473 if dnsdomain is None or dnsdomain == "":
474 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
476 dnsdomain = dnsdomain.lower()
478 if serverrole is None:
479 serverrole = lp.get("server role")
480 if serverrole is None:
481 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
483 serverrole = serverrole.lower()
485 realm = dnsdomain.upper()
487 if lp.get("realm") == "":
488 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
490 if lp.get("realm").upper() != realm:
491 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))
493 if lp.get("server role").lower() != serverrole:
494 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))
496 if serverrole == "domain controller":
498 # This will, for better or worse, default to 'WORKGROUP'
499 domain = lp.get("workgroup")
500 domain = domain.upper()
502 if lp.get("workgroup").upper() != domain:
503 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))
506 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
510 domaindn = "DC=" + netbiosname
512 if not valid_netbios_name(domain):
513 raise InvalidNetbiosName(domain)
515 if hostname.upper() == realm:
516 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
517 if netbiosname == realm:
518 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
520 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
526 configdn = "CN=Configuration," + rootdn
528 schemadn = "CN=Schema," + configdn
533 names = ProvisionNames()
534 names.rootdn = rootdn
535 names.domaindn = domaindn
536 names.configdn = configdn
537 names.schemadn = schemadn
538 names.ldapmanagerdn = "CN=Manager," + rootdn
539 names.dnsdomain = dnsdomain
540 names.domain = domain
542 names.netbiosname = netbiosname
543 names.hostname = hostname
544 names.sitename = sitename
545 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
550 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
551 targetdir, sid_generator="internal", eadb=False, lp=None):
552 """Create a new smb.conf file based on a couple of basic settings.
554 assert smbconf is not None
556 hostname = socket.gethostname().split(".")[0]
557 netbiosname = hostname.upper()
558 # remove forbidden chars
560 for x in netbiosname:
561 if x.isalnum() or x in VALID_NETBIOS_CHARS:
562 newnbname = "%s%c" % (newnbname, x)
563 #force the length to be <16
564 netbiosname = newnbname[0:15]
566 netbiosname = hostname.upper()
568 if serverrole is None:
569 serverrole = "standalone"
571 assert serverrole in ("domain controller", "member server", "standalone")
572 if serverrole == "domain controller":
574 elif serverrole == "member server":
575 smbconfsuffix = "member"
576 elif serverrole == "standalone":
577 smbconfsuffix = "standalone"
579 if sid_generator is None:
580 sid_generator = "internal"
582 assert domain is not None
583 domain = domain.upper()
585 assert realm is not None
586 realm = realm.upper()
589 lp = samba.param.LoadParm()
590 #Load non-existant file
591 if os.path.exists(smbconf):
594 if targetdir is not None:
595 privdir = os.path.join(targetdir, "private")
597 privdir = lp.get("private dir")
598 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
600 if targetdir is not None:
601 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
602 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
604 lp.set("lock dir", os.path.abspath(targetdir))
609 if sid_generator == "internal":
610 sid_generator_line = ""
612 sid_generator_line = "sid generator = " + sid_generator
614 used_setup_dir = setup_path("")
615 default_setup_dir = lp.get("setup directory")
617 if used_setup_dir != default_setup_dir:
618 setupdir_line = "setup directory = %s" % used_setup_dir
619 lp.set("setup directory", used_setup_dir)
621 sysvol = os.path.join(lp.get("lock dir"), "sysvol")
622 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
624 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
626 "NETBIOS_NAME": netbiosname,
629 "SERVERROLE": serverrole,
630 "NETLOGONPATH": netlogon,
631 "SYSVOLPATH": sysvol,
632 "SETUPDIRECTORY_LINE": setupdir_line,
633 "SIDGENERATOR_LINE": sid_generator_line,
634 "PRIVATEDIR_LINE": privatedir_line,
635 "LOCKDIR_LINE": lockdir_line
638 # reload the smb.conf
641 # and dump it without any values that are the default
642 # this ensures that any smb.conf parameters that were set
643 # on the provision/join command line are set in the resulting smb.conf
644 f = open(smbconf, mode='w')
650 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
651 users_gid, wheel_gid):
652 """setup reasonable name mappings for sam names to unix names.
654 :param samdb: SamDB object.
655 :param idmap: IDmap db object.
656 :param sid: The domain sid.
657 :param domaindn: The domain DN.
658 :param root_uid: uid of the UNIX root user.
659 :param nobody_uid: uid of the UNIX nobody user.
660 :param users_gid: gid of the UNIX users group.
661 :param wheel_gid: gid of the UNIX wheel group."""
662 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
663 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
665 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
666 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
669 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
670 provision_backend, names, schema, serverrole,
672 """Setup the partitions for the SAM database.
674 Alternatively, provision() may call this, and then populate the database.
676 :note: This will wipe the Sam Database!
678 :note: This function always removes the local SAM LDB file. The erase
679 parameter controls whether to erase the existing data, which
680 may not be stored locally but in LDAP.
683 assert session_info is not None
685 # We use options=["modules:"] to stop the modules loading - we
686 # just want to wipe and re-initialise the database, not start it up
689 os.unlink(samdb_path)
693 samdb = Ldb(url=samdb_path, session_info=session_info,
694 lp=lp, options=["modules:"])
696 ldap_backend_line = "# No LDAP backend"
697 if provision_backend.type is not "ldb":
698 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
700 samdb.transaction_start()
702 logger.info("Setting up sam.ldb partitions and settings")
703 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
704 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
705 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
706 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
707 "LDAP_BACKEND_LINE": ldap_backend_line,
711 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
712 "BACKEND_TYPE": provision_backend.type,
713 "SERVER_ROLE": serverrole
716 logger.info("Setting up sam.ldb rootDSE")
717 setup_samdb_rootdse(samdb, setup_path, names)
719 samdb.transaction_cancel()
722 samdb.transaction_commit()
725 def secretsdb_self_join(secretsdb, domain,
726 netbiosname, machinepass, domainsid=None,
727 realm=None, dnsdomain=None,
729 key_version_number=1,
730 secure_channel_type=SEC_CHAN_WKSTA):
731 """Add domain join-specific bits to a secrets database.
733 :param secretsdb: Ldb Handle to the secrets database
734 :param machinepass: Machine password
736 attrs=["whenChanged",
743 if realm is not None:
744 if dnsdomain is None:
745 dnsdomain = realm.lower()
746 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
749 shortname = netbiosname.lower()
751 #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
752 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
753 msg["secureChannelType"] = [str(secure_channel_type)]
754 msg["objectClass"] = ["top", "primaryDomain"]
755 if dnsname is not None:
756 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
757 msg["realm"] = [realm]
758 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
759 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
760 msg["privateKeytab"] = ["secrets.keytab"]
762 msg["secret"] = [machinepass]
763 msg["samAccountName"] = ["%s$" % netbiosname]
764 msg["secureChannelType"] = [str(secure_channel_type)]
765 if domainsid is not None:
766 msg["objectSid"] = [ndr_pack(domainsid)]
768 # This complex expression tries to ensure that we don't have more
769 # than one record for this SID, realm or netbios domain at a time,
770 # but we don't delete the old record that we are about to modify,
771 # because that would delete the keytab and previous password.
772 res = secretsdb.search(base="cn=Primary Domains",
774 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
775 scope=ldb.SCOPE_ONELEVEL)
778 secretsdb.delete(del_msg.dn)
780 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
783 msg["priorSecret"] = [res[0]["secret"][0]]
784 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
787 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
792 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
798 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
799 secretsdb.modify(msg)
800 secretsdb.rename(res[0].dn, msg.dn)
802 spn = [ 'HOST/%s' % shortname ]
803 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
804 # we are a domain controller then we add servicePrincipalName entries
805 # for the keytab code to update
806 spn.extend([ 'HOST/%s' % dnsname ])
807 msg["servicePrincipalName"] = spn
812 def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir,
814 dns_keytab_path, dnspass):
815 """Add DNS specific bits to a secrets database.
817 :param secretsdb: Ldb Handle to the secrets database
818 :param setup_path: Setup path function
819 :param machinepass: Machine password
822 os.unlink(os.path.join(private_dir, dns_keytab_path))
826 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
828 "DNSDOMAIN": dnsdomain,
829 "DNS_KEYTAB": dns_keytab_path,
830 "DNSPASS_B64": b64encode(dnspass),
831 "HOSTNAME": names.hostname,
832 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
836 def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
837 """Setup the secrets database.
839 :note: This function does not handle exceptions and transaction on purpose,
840 it's up to the caller to do this job.
842 :param path: Path to the secrets 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(paths.secrets):
850 os.unlink(paths.secrets)
852 keytab_path = os.path.join(paths.private_dir, paths.keytab)
853 if os.path.exists(keytab_path):
854 os.unlink(keytab_path)
856 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
857 if os.path.exists(dns_keytab_path):
858 os.unlink(dns_keytab_path)
862 secrets_ldb = Ldb(path, session_info=session_info,
865 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
866 secrets_ldb = Ldb(path, session_info=session_info,
868 secrets_ldb.transaction_start()
870 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
872 if backend_credentials is not None and backend_credentials.authentication_requested():
873 if backend_credentials.get_bind_dn() is not None:
874 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
875 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
876 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
879 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
880 "LDAPADMINUSER": backend_credentials.get_username(),
881 "LDAPADMINREALM": backend_credentials.get_realm(),
882 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
887 secrets_ldb.transaction_cancel()
890 def setup_privileges(path, setup_path, session_info, lp):
891 """Setup the privileges database.
893 :param path: Path to the privileges database.
894 :param setup_path: Get the path to a setup file.
895 :param session_info: Session info.
896 :param credentials: Credentials
897 :param lp: Loadparm context
898 :return: LDB handle for the created secrets database
900 if os.path.exists(path):
902 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
903 privilege_ldb.erase()
904 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
907 def setup_registry(path, setup_path, session_info, lp):
908 """Setup the registry.
910 :param path: Path to the registry database
911 :param setup_path: Function that returns the path to a setup.
912 :param session_info: Session information
913 :param credentials: Credentials
914 :param lp: Loadparm context
916 reg = samba.registry.Registry()
917 hive = samba.registry.open_ldb(path, session_info=session_info,
919 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
920 provision_reg = setup_path("provision.reg")
921 assert os.path.exists(provision_reg)
922 reg.diff_apply(provision_reg)
925 def setup_idmapdb(path, setup_path, session_info, lp):
926 """Setup the idmap database.
928 :param path: path to the idmap database
929 :param setup_path: Function that returns a path to a setup file
930 :param session_info: Session information
931 :param credentials: Credentials
932 :param lp: Loadparm context
934 if os.path.exists(path):
937 idmap_ldb = IDmapDB(path, session_info=session_info,
941 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
945 def setup_samdb_rootdse(samdb, setup_path, names):
946 """Setup the SamDB rootdse.
948 :param samdb: Sam Database handle
949 :param setup_path: Obtain setup path
951 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
952 "SCHEMADN": names.schemadn,
953 "DOMAINDN": names.domaindn,
954 "ROOTDN": names.rootdn,
955 "CONFIGDN": names.configdn,
956 "SERVERDN": names.serverdn,
960 def setup_self_join(samdb, names,
961 machinepass, dnspass,
962 domainsid, next_rid, invocationid, setup_path,
963 policyguid, policyguid_dc, domainControllerFunctionality,
965 """Join a host to its own domain."""
966 assert isinstance(invocationid, str)
967 if ntdsguid is not None:
968 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
971 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
972 "CONFIGDN": names.configdn,
973 "SCHEMADN": names.schemadn,
974 "DOMAINDN": names.domaindn,
975 "SERVERDN": names.serverdn,
976 "INVOCATIONID": invocationid,
977 "NETBIOSNAME": names.netbiosname,
978 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
979 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
980 "DOMAINSID": str(domainsid),
981 "DCRID": str(next_rid),
982 "SAMBA_VERSION_STRING": version,
983 "NTDSGUID": ntdsguid_line,
984 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
986 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
987 "POLICYGUID": policyguid,
988 "POLICYGUID_DC": policyguid_dc,
989 "DNSDOMAIN": names.dnsdomain,
990 "DOMAINDN": names.domaindn})
992 # add the NTDSGUID based SPNs
993 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
994 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
995 expression="", scope=ldb.SCOPE_BASE)
996 assert isinstance(names.ntdsguid, str)
998 # Setup fSMORoleOwner entries to point at the newly created DC entry
999 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1000 "DOMAINDN": names.domaindn,
1001 "CONFIGDN": names.configdn,
1002 "SCHEMADN": names.schemadn,
1003 "DEFAULTSITE": names.sitename,
1004 "SERVERDN": names.serverdn,
1005 "NETBIOSNAME": names.netbiosname,
1006 "RIDALLOCATIONSTART": str(next_rid + 100),
1007 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1010 # This is partially Samba4 specific and should be replaced by the correct
1011 # DNS AD-style setup
1012 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1013 "DNSDOMAIN": names.dnsdomain,
1014 "DOMAINDN": names.domaindn,
1015 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1016 "HOSTNAME" : names.hostname,
1017 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
1020 def getpolicypath(sysvolpath, dnsdomain, guid):
1021 """Return the physical path of policy given its guid.
1023 :param sysvolpath: Path to the sysvol folder
1024 :param dnsdomain: DNS name of the AD domain
1025 :param guid: The GUID of the policy
1026 :return: A string with the complete path to the policy folder
1030 guid = "{%s}" % guid
1031 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1034 def create_gpo_struct(policy_path):
1035 if not os.path.exists(policy_path):
1036 os.makedirs(policy_path, 0775)
1037 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1038 "[General]\r\nVersion=0")
1039 p = os.path.join(policy_path, "MACHINE")
1040 if not os.path.exists(p):
1041 os.makedirs(p, 0775)
1042 p = os.path.join(policy_path, "USER")
1043 if not os.path.exists(p):
1044 os.makedirs(p, 0775)
1047 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1048 """Create the default GPO for a domain
1050 :param sysvolpath: Physical path for the sysvol folder
1051 :param dnsdomain: DNS domain name of the AD domain
1052 :param policyguid: GUID of the default domain policy
1053 :param policyguid_dc: GUID of the default domain controler policy
1056 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1057 create_gpo_struct(policy_path)
1059 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1060 create_gpo_struct(policy_path)
1063 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1064 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1065 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1066 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1068 """Setup a complete SAM Database.
1070 :note: This will wipe the main SAM database file!
1074 # Provision does not make much sense values larger than 1000000000
1075 # as the upper range of the rIDAvailablePool is 1073741823 and
1076 # we don't want to create a domain that cannot allocate rids.
1077 if next_rid < 1000 or next_rid > 1000000000:
1078 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1079 error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
1080 raise ProvisioningError(error)
1082 # ATTENTION: Do NOT change these default values without discussion with the
1083 # team and/or release manager. They have a big impact on the whole program!
1084 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1086 if dom_for_fun_level is None:
1087 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1089 if dom_for_fun_level > domainControllerFunctionality:
1090 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!")
1092 domainFunctionality = dom_for_fun_level
1093 forestFunctionality = dom_for_fun_level
1095 # Also wipes the database
1096 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1097 provision_backend=provision_backend, session_info=session_info,
1098 names=names, serverrole=serverrole, schema=schema)
1101 schema = Schema(setup_path, domainsid, schemadn=names.schemadn)
1103 # Load the database, but don's load the global schema and don't connect quite yet
1104 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1105 credentials=provision_backend.credentials, lp=lp, global_schema=False,
1108 logger.info("Pre-loading the Samba 4 and AD schema")
1110 # Load the schema from the one we computed earlier
1111 samdb.set_schema(schema)
1113 # Set the NTDS settings DN manually - in order to have it already around
1114 # before the provisioned tree exists and we connect
1115 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1117 # And now we can connect to the DB - the schema won't be loaded from the DB
1120 if fill == FILL_DRS:
1123 samdb.transaction_start()
1125 # Set the domain functionality levels onto the database.
1126 # Various module (the password_hash module in particular) need
1127 # to know what level of AD we are emulating.
1129 # These will be fixed into the database via the database
1130 # modifictions below, but we need them set from the start.
1131 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1132 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1133 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1135 samdb.set_domain_sid(str(domainsid))
1136 samdb.set_invocation_id(invocationid)
1138 logger.info("Adding DomainDN: %s" % names.domaindn)
1140 #impersonate domain admin
1141 admin_session_info = admin_session(lp, str(domainsid))
1142 samdb.set_session_info(admin_session_info)
1143 if domainguid is not None:
1144 domainguid_line = "objectGUID: %s\n-" % domainguid
1146 domainguid_line = ""
1148 descr = b64encode(get_domain_descriptor(domainsid))
1149 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1150 "DOMAINDN": names.domaindn,
1151 "DOMAINSID": str(domainsid),
1152 "DESCRIPTOR": descr,
1153 "DOMAINGUID": domainguid_line
1156 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1157 "DOMAINDN": names.domaindn,
1158 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1159 "NEXTRID": str(next_rid),
1160 "DEFAULTSITE": names.sitename,
1161 "CONFIGDN": names.configdn,
1162 "POLICYGUID": policyguid,
1163 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1164 "SAMBA_VERSION_STRING": version
1167 logger.info("Adding configuration container")
1168 descr = b64encode(get_config_descriptor(domainsid))
1169 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1170 "CONFIGDN": names.configdn,
1171 "DESCRIPTOR": descr,
1174 # The LDIF here was created when the Schema object was constructed
1175 logger.info("Setting up sam.ldb schema")
1176 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1177 samdb.modify_ldif(schema.schema_dn_modify)
1178 samdb.write_prefixes_from_schema()
1179 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1180 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1181 {"SCHEMADN": names.schemadn})
1183 logger.info("Reopening sam.ldb with new schema")
1185 samdb.transaction_cancel()
1188 samdb.transaction_commit()
1190 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1191 credentials=provision_backend.credentials, lp=lp,
1192 global_schema=False, am_rodc=am_rodc)
1194 # Set the NTDS settings DN manually - in order to have it already around
1195 # before the provisioned tree exists and we connect
1196 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1200 samdb.transaction_start()
1202 samdb.invocation_id = invocationid
1204 logger.info("Setting up sam.ldb configuration data")
1205 descr = b64encode(get_sites_descriptor(domainsid))
1206 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1207 "CONFIGDN": names.configdn,
1208 "NETBIOSNAME": names.netbiosname,
1209 "DEFAULTSITE": names.sitename,
1210 "DNSDOMAIN": names.dnsdomain,
1211 "DOMAIN": names.domain,
1212 "SCHEMADN": names.schemadn,
1213 "DOMAINDN": names.domaindn,
1214 "SERVERDN": names.serverdn,
1215 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1216 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1217 "SITES_DESCRIPTOR": descr
1220 logger.info("Setting up display specifiers")
1221 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1222 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1223 check_all_substituted(display_specifiers_ldif)
1224 samdb.add_ldif(display_specifiers_ldif)
1226 logger.info("Adding users container")
1227 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1228 "DOMAINDN": names.domaindn})
1229 logger.info("Modifying users container")
1230 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1231 "DOMAINDN": names.domaindn})
1232 logger.info("Adding computers container")
1233 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1234 "DOMAINDN": names.domaindn})
1235 logger.info("Modifying computers container")
1236 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1237 "DOMAINDN": names.domaindn})
1238 logger.info("Setting up sam.ldb data")
1239 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1240 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1241 "DOMAINDN": names.domaindn,
1242 "NETBIOSNAME": names.netbiosname,
1243 "DEFAULTSITE": names.sitename,
1244 "CONFIGDN": names.configdn,
1245 "SERVERDN": names.serverdn,
1246 "RIDAVAILABLESTART": str(next_rid + 600),
1247 "POLICYGUID_DC": policyguid_dc
1250 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1251 "DOMAINDN": names.domaindn})
1253 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1254 "CONFIGDN": names.configdn,
1255 "SCHEMADN": names.schemadn})
1256 if fill == FILL_FULL:
1257 logger.info("Setting up sam.ldb users and groups")
1258 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1259 "DOMAINDN": names.domaindn,
1260 "DOMAINSID": str(domainsid),
1261 "CONFIGDN": names.configdn,
1262 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1263 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1266 logger.info("Setting up self join")
1267 setup_self_join(samdb, names=names, invocationid=invocationid,
1269 machinepass=machinepass,
1270 domainsid=domainsid,
1272 policyguid=policyguid,
1273 policyguid_dc=policyguid_dc,
1274 setup_path=setup_path,
1275 domainControllerFunctionality=domainControllerFunctionality,
1278 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1279 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1280 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1281 assert isinstance(names.ntdsguid, str)
1283 samdb.transaction_cancel()
1286 samdb.transaction_commit()
1291 FILL_NT4SYNC = "NT4SYNC"
1293 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1294 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)"
1296 def set_dir_acl(path, acl, lp, domsid):
1297 setntacl(lp, path, acl, domsid)
1298 for root, dirs, files in os.walk(path, topdown=False):
1300 setntacl(lp, os.path.join(root, name), acl, domsid)
1302 setntacl(lp, os.path.join(root, name), acl, domsid)
1305 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1306 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1309 :param sysvol: Physical path for the sysvol folder
1310 :param dnsdomain: The DNS name of the domain
1311 :param domainsid: The SID of the domain
1312 :param domaindn: The DN of the domain (ie. DC=...)
1313 :param samdb: An LDB object on the SAM db
1314 :param lp: an LP object
1317 # Set ACL for GPO root folder
1318 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1319 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1321 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1322 attrs=["cn", "nTSecurityDescriptor"],
1323 expression="", scope=ldb.SCOPE_ONELEVEL)
1326 acl = ndr_unpack(security.descriptor,
1327 str(policy["nTSecurityDescriptor"])).as_sddl()
1328 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1329 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1332 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1334 """Set the ACL for the sysvol share and the subfolders
1336 :param samdb: An LDB object on the SAM db
1337 :param netlogon: Physical path for the netlogon folder
1338 :param sysvol: Physical path for the sysvol folder
1339 :param gid: The GID of the "Domain adminstrators" group
1340 :param domainsid: The SID of the domain
1341 :param dnsdomain: The DNS name of the domain
1342 :param domaindn: The DN of the domain (ie. DC=...)
1346 os.chown(sysvol,-1,gid)
1352 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1353 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1354 for root, dirs, files in os.walk(sysvol, topdown=False):
1357 os.chown(os.path.join(root, name), -1, gid)
1358 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1361 os.chown(os.path.join(root, name), -1, gid)
1362 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1364 # Set acls on Policy folder and policies folders
1365 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1368 def provision(setup_dir, logger, session_info,
1369 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1371 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1373 domain=None, hostname=None, hostip=None, hostip6=None,
1374 domainsid=None, next_rid=1000,
1375 adminpass=None, ldapadminpass=None,
1376 krbtgtpass=None, domainguid=None,
1377 policyguid=None, policyguid_dc=None, invocationid=None,
1378 machinepass=None, ntdsguid=None,
1379 dnspass=None, root=None, nobody=None, users=None,
1380 wheel=None, backup=None, aci=None, serverrole=None,
1381 dom_for_fun_level=None,
1382 ldap_backend_extra_port=None, ldap_backend_forced_uri=None, backend_type=None,
1384 ol_mmr_urls=None, ol_olc=None,
1385 setup_ds_path=None, slapd_path=None, nosync=False,
1386 ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1390 :note: caution, this wipes all existing data!
1393 def setup_path(file):
1394 return os.path.join(setup_dir, file)
1396 if domainsid is None:
1397 domainsid = security.random_sid()
1399 domainsid = security.dom_sid(domainsid)
1401 # create/adapt the group policy GUIDs
1402 # Default GUID for default policy are described at
1403 # "How Core Group Policy Works"
1404 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1405 if policyguid is None:
1406 policyguid = DEFAULT_POLICY_GUID
1407 policyguid = policyguid.upper()
1408 if policyguid_dc is None:
1409 policyguid_dc = DEFAULT_DC_POLICY_GUID
1410 policyguid_dc = policyguid_dc.upper()
1412 if adminpass is None:
1413 adminpass = samba.generate_random_password(12, 32)
1414 if krbtgtpass is None:
1415 krbtgtpass = samba.generate_random_password(128, 255)
1416 if machinepass is None:
1417 machinepass = samba.generate_random_password(128, 255)
1419 dnspass = samba.generate_random_password(128, 255)
1420 if ldapadminpass is None:
1421 #Make a new, random password between Samba and it's LDAP server
1422 ldapadminpass=samba.generate_random_password(128, 255)
1424 if backend_type is None:
1425 backend_type = "ldb"
1427 sid_generator = "internal"
1428 if backend_type == "fedora-ds":
1429 sid_generator = "backend"
1431 root_uid = findnss_uid([root or "root"])
1432 nobody_uid = findnss_uid([nobody or "nobody"])
1433 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1435 wheel_gid = findnss_gid(["wheel", "adm"])
1437 wheel_gid = findnss_gid([wheel])
1439 bind_gid = findnss_gid(["bind", "named"])
1443 if targetdir is not None:
1444 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1445 elif smbconf is None:
1446 smbconf = samba.param.default_path()
1447 if not os.path.exists(os.path.dirname(smbconf)):
1448 os.makedirs(os.path.dirname(smbconf))
1450 # only install a new smb.conf if there isn't one there already
1451 if os.path.exists(smbconf):
1452 # if Samba Team members can't figure out the weird errors
1453 # loading an empty smb.conf gives, then we need to be smarter.
1454 # Pretend it just didn't exist --abartlet
1455 data = open(smbconf, 'r').read()
1456 data = data.lstrip()
1457 if data is None or data == "":
1458 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1459 serverrole, targetdir, sid_generator, useeadb,
1462 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1463 targetdir, sid_generator, useeadb, lp=lp)
1466 lp = samba.param.LoadParm()
1468 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1469 dnsdomain=realm, serverrole=serverrole,
1470 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1471 serverdn=serverdn, sitename=sitename)
1472 paths = provision_paths_from_lp(lp, names.dnsdomain)
1474 paths.bind_gid = bind_gid
1477 logger.info("Looking up IPv4 addresses")
1478 hostips = samba.interface_ips(lp, False)
1479 if len(hostips) == 0:
1480 logger.warning("No external IPv4 address has been found. Using loopback.")
1481 hostip = '127.0.0.1'
1484 if len(hostips) > 1:
1485 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1487 if serverrole is None:
1488 serverrole = lp.get("server role")
1490 assert serverrole in ("domain controller", "member server", "standalone")
1491 if invocationid is None:
1492 invocationid = str(uuid.uuid4())
1494 if not os.path.exists(paths.private_dir):
1495 os.mkdir(paths.private_dir)
1496 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1497 os.mkdir(os.path.join(paths.private_dir, "tls"))
1499 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1501 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn)
1503 if backend_type == "ldb":
1504 provision_backend = LDBBackend(backend_type,
1505 paths=paths, setup_path=setup_path,
1506 lp=lp, credentials=credentials,
1509 elif backend_type == "existing":
1510 provision_backend = ExistingBackend(backend_type,
1511 paths=paths, setup_path=setup_path,
1512 lp=lp, credentials=credentials,
1515 ldap_backend_forced_uri=ldap_backend_forced_uri)
1516 elif backend_type == "fedora-ds":
1517 provision_backend = FDSBackend(backend_type,
1518 paths=paths, setup_path=setup_path,
1519 lp=lp, credentials=credentials,
1522 domainsid=domainsid,
1525 ldapadminpass=ldapadminpass,
1526 slapd_path=slapd_path,
1527 ldap_backend_extra_port=ldap_backend_extra_port,
1528 ldap_dryrun_mode=ldap_dryrun_mode,
1530 setup_ds_path=setup_ds_path,
1531 ldap_backend_forced_uri=ldap_backend_forced_uri)
1532 elif backend_type == "openldap":
1533 provision_backend = OpenLDAPBackend(backend_type,
1534 paths=paths, setup_path=setup_path,
1535 lp=lp, credentials=credentials,
1538 domainsid=domainsid,
1541 ldapadminpass=ldapadminpass,
1542 slapd_path=slapd_path,
1543 ldap_backend_extra_port=ldap_backend_extra_port,
1544 ldap_dryrun_mode=ldap_dryrun_mode,
1545 ol_mmr_urls=ol_mmr_urls,
1547 ldap_backend_forced_uri=ldap_backend_forced_uri)
1549 raise ValueError("Unknown LDAP backend type selected")
1551 provision_backend.init()
1552 provision_backend.start()
1554 # only install a new shares config db if there is none
1555 if not os.path.exists(paths.shareconf):
1556 logger.info("Setting up share.ldb")
1557 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1559 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1561 logger.info("Setting up secrets.ldb")
1562 secrets_ldb = setup_secretsdb(paths, setup_path,
1563 session_info=session_info,
1564 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1567 logger.info("Setting up the registry")
1568 setup_registry(paths.hklm, setup_path, session_info,
1571 logger.info("Setting up the privileges database")
1572 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1574 logger.info("Setting up idmap db")
1575 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1578 logger.info("Setting up SAM db")
1579 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1580 provision_backend, lp, names,
1582 domainsid=domainsid,
1583 schema=schema, domainguid=domainguid,
1584 policyguid=policyguid, policyguid_dc=policyguid_dc,
1586 adminpass=adminpass, krbtgtpass=krbtgtpass,
1587 invocationid=invocationid,
1588 machinepass=machinepass, dnspass=dnspass,
1589 ntdsguid=ntdsguid, serverrole=serverrole,
1590 dom_for_fun_level=dom_for_fun_level,
1591 am_rodc=am_rodc, next_rid=next_rid)
1593 if serverrole == "domain controller":
1594 if paths.netlogon is None:
1595 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1596 logger.info("Please either remove %s or see the template at %s" %
1597 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1598 assert paths.netlogon is not None
1600 if paths.sysvol is None:
1601 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1602 " are configuring a DC.")
1603 logger.info("Please either remove %s or see the template at %s" %
1604 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1605 assert paths.sysvol is not None
1607 if not os.path.isdir(paths.netlogon):
1608 os.makedirs(paths.netlogon, 0755)
1610 if samdb_fill == FILL_FULL:
1611 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1612 root_uid=root_uid, nobody_uid=nobody_uid,
1613 users_gid=users_gid, wheel_gid=wheel_gid)
1615 if serverrole == "domain controller":
1616 # Set up group policies (domain policy and domain controller policy)
1617 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1618 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1619 domainsid, names.dnsdomain, names.domaindn, lp)
1621 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1622 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1624 secretsdb_self_join(secrets_ldb, domain=names.domain,
1626 dnsdomain=names.dnsdomain,
1627 netbiosname=names.netbiosname,
1628 domainsid=domainsid,
1629 machinepass=machinepass,
1630 secure_channel_type=SEC_CHAN_BDC)
1632 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1633 # In future, this might be determined from some configuration
1634 kerberos_enctypes = str(ENC_ALL_TYPES)
1637 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1638 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
1639 flags=ldb.FLAG_MOD_REPLACE,
1640 name="msDS-SupportedEncryptionTypes")
1642 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1643 # It might be that this attribute does not exist in this schema
1647 if serverrole == "domain controller":
1648 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1650 realm=names.realm, dnsdomain=names.dnsdomain,
1651 dns_keytab_path=paths.dns_keytab,
1654 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1655 assert isinstance(domainguid, str)
1657 # Only make a zone file on the first DC, it should be replicated
1658 # with DNS replication
1659 create_zone_file(lp, logger, paths, targetdir, setup_path,
1660 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1661 hostname=names.hostname, realm=names.realm,
1662 domainguid=domainguid, ntdsguid=names.ntdsguid)
1664 create_named_conf(paths, setup_path, realm=names.realm,
1665 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1667 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1668 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1669 keytab_name=paths.dns_keytab)
1670 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1671 logger.info("and %s for further documentation required for secure DNS "
1672 "updates", paths.namedtxt)
1674 lastProvisionUSNs = get_last_provision_usn(samdb)
1675 maxUSN = get_max_usn(samdb, str(names.rootdn))
1676 if lastProvisionUSNs is not None:
1677 update_provision_usn(samdb, 0, maxUSN, 1)
1679 set_provision_usn(samdb, 0, maxUSN)
1681 create_krb5_conf(paths.krb5conf, setup_path,
1682 dnsdomain=names.dnsdomain, hostname=names.hostname,
1684 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1685 "generated at %s", paths.krb5conf)
1687 if serverrole == "domain controller":
1688 create_dns_update_list(lp, logger, paths, setup_path)
1690 provision_backend.post_setup()
1691 provision_backend.shutdown()
1693 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1696 secrets_ldb.transaction_cancel()
1699 #Now commit the secrets.ldb to disk
1700 secrets_ldb.transaction_commit()
1702 # the commit creates the dns.keytab, now chown it
1703 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1704 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1706 os.chmod(dns_keytab_path, 0640)
1707 os.chown(dns_keytab_path, -1, paths.bind_gid)
1709 if not os.environ.has_key('SAMBA_SELFTEST'):
1710 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1714 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1715 paths.phpldapadminconfig)
1717 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1718 logger.info("Server Role: %s" % serverrole)
1719 logger.info("Hostname: %s" % names.hostname)
1720 logger.info("NetBIOS Domain: %s" % names.domain)
1721 logger.info("DNS Domain: %s" % names.dnsdomain)
1722 logger.info("DOMAIN SID: %s" % str(domainsid))
1723 if samdb_fill == FILL_FULL:
1724 logger.info("Admin password: %s" % adminpass)
1725 if provision_backend.type is not "ldb":
1726 if provision_backend.credentials.get_bind_dn() is not None:
1727 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1729 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1731 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1733 if provision_backend.slapd_command_escaped is not None:
1734 # now display slapd_command_file.txt to show how slapd must be started next time
1735 logger.info("Use later the following commandline to start slapd, then Samba:")
1736 logger.info(provision_backend.slapd_command_escaped)
1737 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1738 provision_backend.ldapdir)
1740 result = ProvisionResult()
1741 result.domaindn = domaindn
1742 result.paths = paths
1744 result.samdb = samdb
1748 def provision_become_dc(setup_dir=None,
1749 smbconf=None, targetdir=None, realm=None,
1750 rootdn=None, domaindn=None, schemadn=None,
1751 configdn=None, serverdn=None,
1752 domain=None, hostname=None, domainsid=None,
1753 adminpass=None, krbtgtpass=None, domainguid=None,
1754 policyguid=None, policyguid_dc=None, invocationid=None,
1756 dnspass=None, root=None, nobody=None, users=None,
1757 wheel=None, backup=None, serverrole=None,
1758 ldap_backend=None, ldap_backend_type=None,
1759 sitename=None, debuglevel=1):
1761 logger = logging.getLogger("provision")
1762 samba.set_debug_level(debuglevel)
1764 res = provision(setup_dir, logger, system_session(), None,
1765 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1766 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1767 configdn=configdn, serverdn=serverdn, domain=domain,
1768 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1769 machinepass=machinepass, serverrole="domain controller",
1771 res.lp.set("debuglevel", str(debuglevel))
1775 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1776 """Create a PHP LDAP admin configuration file.
1778 :param path: Path to write the configuration to.
1779 :param setup_path: Function to generate setup paths.
1781 setup_file(setup_path("phpldapadmin-config.php"), path,
1782 {"S4_LDAPI_URI": ldapi_uri})
1785 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1786 hostip, hostip6, hostname, realm, domainguid,
1788 """Write out a DNS zone file, from the info in the current database.
1790 :param paths: paths object
1791 :param setup_path: Setup path function.
1792 :param dnsdomain: DNS Domain name
1793 :param domaindn: DN of the Domain
1794 :param hostip: Local IPv4 IP
1795 :param hostip6: Local IPv6 IP
1796 :param hostname: Local hostname
1797 :param realm: Realm name
1798 :param domainguid: GUID of the domain.
1799 :param ntdsguid: GUID of the hosts nTDSDSA record.
1801 assert isinstance(domainguid, str)
1803 if hostip6 is not None:
1804 hostip6_base_line = " IN AAAA " + hostip6
1805 hostip6_host_line = hostname + " IN AAAA " + hostip6
1806 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1808 hostip6_base_line = ""
1809 hostip6_host_line = ""
1810 gc_msdcs_ip6_line = ""
1812 if hostip is not None:
1813 hostip_base_line = " IN A " + hostip
1814 hostip_host_line = hostname + " IN A " + hostip
1815 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1817 hostip_base_line = ""
1818 hostip_host_line = ""
1819 gc_msdcs_ip_line = ""
1821 dns_dir = os.path.dirname(paths.dns)
1824 shutil.rmtree(dns_dir, True)
1828 os.mkdir(dns_dir, 0775)
1830 # we need to freeze the zone while we update the contents
1831 if targetdir is None:
1832 rndc = ' '.join(lp.get("rndc command"))
1833 os.system(rndc + " freeze " + lp.get("realm"))
1835 setup_file(setup_path("provision.zone"), paths.dns, {
1836 "HOSTNAME": hostname,
1837 "DNSDOMAIN": dnsdomain,
1839 "HOSTIP_BASE_LINE": hostip_base_line,
1840 "HOSTIP_HOST_LINE": hostip_host_line,
1841 "DOMAINGUID": domainguid,
1842 "DATESTRING": time.strftime("%Y%m%d%H"),
1843 "DEFAULTSITE": DEFAULTSITE,
1844 "NTDSGUID": ntdsguid,
1845 "HOSTIP6_BASE_LINE": hostip6_base_line,
1846 "HOSTIP6_HOST_LINE": hostip6_host_line,
1847 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1848 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1851 # note that we use no variable substitution on this file
1852 # the substitution is done at runtime by samba_dnsupdate
1853 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1855 # and the SPN update list
1856 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1858 if paths.bind_gid is not None:
1860 os.chown(dns_dir, -1, paths.bind_gid)
1861 os.chown(paths.dns, -1, paths.bind_gid)
1862 # chmod needed to cope with umask
1863 os.chmod(dns_dir, 0775)
1864 os.chmod(paths.dns, 0664)
1866 if not os.environ.has_key('SAMBA_SELFTEST'):
1867 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1869 if targetdir is None:
1870 os.system(rndc + " unfreeze " + lp.get("realm"))
1873 def create_dns_update_list(lp, logger, paths, setup_path):
1874 """Write out a dns_update_list file"""
1875 # note that we use no variable substitution on this file
1876 # the substitution is done at runtime by samba_dnsupdate
1877 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1878 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1881 def create_named_conf(paths, setup_path, realm, dnsdomain,
1883 """Write out a file containing zone statements suitable for inclusion in a
1884 named.conf file (including GSS-TSIG configuration).
1886 :param paths: all paths
1887 :param setup_path: Setup path function.
1888 :param realm: Realm name
1889 :param dnsdomain: DNS Domain name
1890 :param private_dir: Path to private directory
1891 :param keytab_name: File name of DNS keytab file
1894 setup_file(setup_path("named.conf"), paths.namedconf, {
1895 "DNSDOMAIN": dnsdomain,
1897 "ZONE_FILE": paths.dns,
1898 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1899 "NAMED_CONF": paths.namedconf,
1900 "NAMED_CONF_UPDATE": paths.namedconf_update
1903 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1906 def create_named_txt(path, setup_path, realm, dnsdomain,
1907 private_dir, keytab_name):
1908 """Write out a file containing zone statements suitable for inclusion in a
1909 named.conf file (including GSS-TSIG configuration).
1911 :param path: Path of the new named.conf file.
1912 :param setup_path: Setup path function.
1913 :param realm: Realm name
1914 :param dnsdomain: DNS Domain name
1915 :param private_dir: Path to private directory
1916 :param keytab_name: File name of DNS keytab file
1919 setup_file(setup_path("named.txt"), path, {
1920 "DNSDOMAIN": dnsdomain,
1922 "DNS_KEYTAB": keytab_name,
1923 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1924 "PRIVATE_DIR": private_dir
1928 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1929 """Write out a file containing zone statements suitable for inclusion in a
1930 named.conf file (including GSS-TSIG configuration).
1932 :param path: Path of the new named.conf file.
1933 :param setup_path: Setup path function.
1934 :param dnsdomain: DNS Domain name
1935 :param hostname: Local hostname
1936 :param realm: Realm name
1938 setup_file(setup_path("krb5.conf"), path, {
1939 "DNSDOMAIN": dnsdomain,
1940 "HOSTNAME": hostname,
1945 class ProvisioningError(Exception):
1946 """A generic provision error."""
1948 def __init__(self, value):
1952 return "ProvisioningError: " + self.value
1955 class InvalidNetbiosName(Exception):
1956 """A specified name was not a valid NetBIOS name."""
1957 def __init__(self, name):
1958 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)