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, default_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()
588 if default_lp is None:
589 default_lp = samba.param.LoadParm()
590 #Load non-existant file
591 if os.path.exists(smbconf):
592 default_lp.load(smbconf)
594 if targetdir is not None:
595 privdir = os.path.join(targetdir, "private")
597 privdir = default_lp.get("private dir")
598 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir, "eadb.tdb"))
602 if targetdir is not None:
603 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
604 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
606 default_lp.set("lock dir", os.path.abspath(targetdir))
611 if sid_generator == "internal":
612 sid_generator_line = ""
614 sid_generator_line = "sid generator = " + sid_generator
616 used_setup_dir = setup_path("")
617 default_setup_dir = default_lp.get("setup directory")
619 if used_setup_dir != default_setup_dir:
620 setupdir_line = "setup directory = %s" % used_setup_dir
621 default_lp.set("setup directory", used_setup_dir)
623 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
624 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
626 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
628 "NETBIOS_NAME": netbiosname,
631 "SERVERROLE": serverrole,
632 "NETLOGONPATH": netlogon,
633 "SYSVOLPATH": sysvol,
634 "SETUPDIRECTORY_LINE": setupdir_line,
635 "SIDGENERATOR_LINE": sid_generator_line,
636 "PRIVATEDIR_LINE": privatedir_line,
637 "LOCKDIR_LINE": lockdir_line,
638 "POSIXEADB_LINE": posixeadb_line
642 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
643 users_gid, wheel_gid):
644 """setup reasonable name mappings for sam names to unix names.
646 :param samdb: SamDB object.
647 :param idmap: IDmap db object.
648 :param sid: The domain sid.
649 :param domaindn: The domain DN.
650 :param root_uid: uid of the UNIX root user.
651 :param nobody_uid: uid of the UNIX nobody user.
652 :param users_gid: gid of the UNIX users group.
653 :param wheel_gid: gid of the UNIX wheel group."""
654 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
655 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
657 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
658 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
661 def setup_samdb_partitions(samdb_path, setup_path, logger, lp, session_info,
662 provision_backend, names, schema, serverrole,
664 """Setup the partitions for the SAM database.
666 Alternatively, provision() may call this, and then populate the database.
668 :note: This will wipe the Sam Database!
670 :note: This function always removes the local SAM LDB file. The erase
671 parameter controls whether to erase the existing data, which
672 may not be stored locally but in LDAP.
675 assert session_info is not None
677 # We use options=["modules:"] to stop the modules loading - we
678 # just want to wipe and re-initialise the database, not start it up
681 os.unlink(samdb_path)
685 samdb = Ldb(url=samdb_path, session_info=session_info,
686 lp=lp, options=["modules:"])
688 ldap_backend_line = "# No LDAP backend"
689 if provision_backend.type is not "ldb":
690 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
692 samdb.transaction_start()
694 logger.info("Setting up sam.ldb partitions and settings")
695 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
696 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
697 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
698 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
699 "LDAP_BACKEND_LINE": ldap_backend_line,
703 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
704 "BACKEND_TYPE": provision_backend.type,
705 "SERVER_ROLE": serverrole
708 logger.info("Setting up sam.ldb rootDSE")
709 setup_samdb_rootdse(samdb, setup_path, names)
711 samdb.transaction_cancel()
714 samdb.transaction_commit()
717 def secretsdb_self_join(secretsdb, domain,
718 netbiosname, machinepass, domainsid=None,
719 realm=None, dnsdomain=None,
721 key_version_number=1,
722 secure_channel_type=SEC_CHAN_WKSTA):
723 """Add domain join-specific bits to a secrets database.
725 :param secretsdb: Ldb Handle to the secrets database
726 :param machinepass: Machine password
728 attrs=["whenChanged",
735 if realm is not None:
736 if dnsdomain is None:
737 dnsdomain = realm.lower()
738 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
741 shortname = netbiosname.lower()
743 #We don't need to set msg["flatname"] here, because rdn_name will handle it, and it causes problems for modifies anyway
744 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
745 msg["secureChannelType"] = [str(secure_channel_type)]
746 msg["objectClass"] = ["top", "primaryDomain"]
747 if dnsname is not None:
748 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
749 msg["realm"] = [realm]
750 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
751 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
752 msg["privateKeytab"] = ["secrets.keytab"]
754 msg["secret"] = [machinepass]
755 msg["samAccountName"] = ["%s$" % netbiosname]
756 msg["secureChannelType"] = [str(secure_channel_type)]
757 if domainsid is not None:
758 msg["objectSid"] = [ndr_pack(domainsid)]
760 # This complex expression tries to ensure that we don't have more
761 # than one record for this SID, realm or netbios domain at a time,
762 # but we don't delete the old record that we are about to modify,
763 # because that would delete the keytab and previous password.
764 res = secretsdb.search(base="cn=Primary Domains",
766 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
767 scope=ldb.SCOPE_ONELEVEL)
770 secretsdb.delete(del_msg.dn)
772 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
775 msg["priorSecret"] = [res[0]["secret"][0]]
776 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
779 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
784 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
790 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
791 secretsdb.modify(msg)
792 secretsdb.rename(res[0].dn, msg.dn)
794 spn = [ 'HOST/%s' % shortname ]
795 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
796 # we are a domain controller then we add servicePrincipalName entries
797 # for the keytab code to update
798 spn.extend([ 'HOST/%s' % dnsname ])
799 msg["servicePrincipalName"] = spn
804 def secretsdb_setup_dns(secretsdb, setup_path, names, private_dir,
806 dns_keytab_path, dnspass):
807 """Add DNS specific bits to a secrets database.
809 :param secretsdb: Ldb Handle to the secrets database
810 :param setup_path: Setup path function
811 :param machinepass: Machine password
814 os.unlink(os.path.join(private_dir, dns_keytab_path))
818 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
820 "DNSDOMAIN": dnsdomain,
821 "DNS_KEYTAB": dns_keytab_path,
822 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
823 "HOSTNAME": names.hostname,
824 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
828 def setup_secretsdb(paths, setup_path, session_info, backend_credentials, lp):
829 """Setup the secrets database.
831 :note: This function does not handle exceptions and transaction on purpose,
832 it's up to the caller to do this job.
834 :param path: Path to the secrets database.
835 :param setup_path: Get the path to a setup file.
836 :param session_info: Session info.
837 :param credentials: Credentials
838 :param lp: Loadparm context
839 :return: LDB handle for the created secrets database
841 if os.path.exists(paths.secrets):
842 os.unlink(paths.secrets)
844 keytab_path = os.path.join(paths.private_dir, paths.keytab)
845 if os.path.exists(keytab_path):
846 os.unlink(keytab_path)
848 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
849 if os.path.exists(dns_keytab_path):
850 os.unlink(dns_keytab_path)
854 secrets_ldb = Ldb(path, session_info=session_info,
857 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
858 secrets_ldb = Ldb(path, session_info=session_info,
860 secrets_ldb.transaction_start()
862 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
864 if backend_credentials is not None and backend_credentials.authentication_requested():
865 if backend_credentials.get_bind_dn() is not None:
866 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
867 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
868 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
871 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
872 "LDAPADMINUSER": backend_credentials.get_username(),
873 "LDAPADMINREALM": backend_credentials.get_realm(),
874 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
879 secrets_ldb.transaction_cancel()
882 def setup_privileges(path, setup_path, session_info, lp):
883 """Setup the privileges database.
885 :param path: Path to the privileges database.
886 :param setup_path: Get the path to a setup file.
887 :param session_info: Session info.
888 :param credentials: Credentials
889 :param lp: Loadparm context
890 :return: LDB handle for the created secrets database
892 if os.path.exists(path):
894 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
895 privilege_ldb.erase()
896 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
899 def setup_registry(path, setup_path, session_info, lp):
900 """Setup the registry.
902 :param path: Path to the registry database
903 :param setup_path: Function that returns the path to a setup.
904 :param session_info: Session information
905 :param credentials: Credentials
906 :param lp: Loadparm context
908 reg = samba.registry.Registry()
909 hive = samba.registry.open_ldb(path, session_info=session_info,
911 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
912 provision_reg = setup_path("provision.reg")
913 assert os.path.exists(provision_reg)
914 reg.diff_apply(provision_reg)
917 def setup_idmapdb(path, setup_path, session_info, lp):
918 """Setup the idmap database.
920 :param path: path to the idmap database
921 :param setup_path: Function that returns a path to a setup file
922 :param session_info: Session information
923 :param credentials: Credentials
924 :param lp: Loadparm context
926 if os.path.exists(path):
929 idmap_ldb = IDmapDB(path, session_info=session_info,
933 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
937 def setup_samdb_rootdse(samdb, setup_path, names):
938 """Setup the SamDB rootdse.
940 :param samdb: Sam Database handle
941 :param setup_path: Obtain setup path
943 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
944 "SCHEMADN": names.schemadn,
945 "DOMAINDN": names.domaindn,
946 "ROOTDN": names.rootdn,
947 "CONFIGDN": names.configdn,
948 "SERVERDN": names.serverdn,
952 def setup_self_join(samdb, names,
953 machinepass, dnspass,
954 domainsid, next_rid, invocationid, setup_path,
955 policyguid, policyguid_dc, domainControllerFunctionality,
957 """Join a host to its own domain."""
958 assert isinstance(invocationid, str)
959 if ntdsguid is not None:
960 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
963 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
964 "CONFIGDN": names.configdn,
965 "SCHEMADN": names.schemadn,
966 "DOMAINDN": names.domaindn,
967 "SERVERDN": names.serverdn,
968 "INVOCATIONID": invocationid,
969 "NETBIOSNAME": names.netbiosname,
970 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
971 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
972 "DOMAINSID": str(domainsid),
973 "DCRID": str(next_rid),
974 "SAMBA_VERSION_STRING": version,
975 "NTDSGUID": ntdsguid_line,
976 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
978 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
979 "POLICYGUID": policyguid,
980 "POLICYGUID_DC": policyguid_dc,
981 "DNSDOMAIN": names.dnsdomain,
982 "DOMAINDN": names.domaindn})
984 # add the NTDSGUID based SPNs
985 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
986 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
987 expression="", scope=ldb.SCOPE_BASE)
988 assert isinstance(names.ntdsguid, str)
990 # Setup fSMORoleOwner entries to point at the newly created DC entry
991 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
992 "DOMAINDN": names.domaindn,
993 "CONFIGDN": names.configdn,
994 "SCHEMADN": names.schemadn,
995 "DEFAULTSITE": names.sitename,
996 "SERVERDN": names.serverdn,
997 "NETBIOSNAME": names.netbiosname,
998 "RIDALLOCATIONSTART": str(next_rid + 100),
999 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1002 # This is partially Samba4 specific and should be replaced by the correct
1003 # DNS AD-style setup
1004 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1005 "DNSDOMAIN": names.dnsdomain,
1006 "DOMAINDN": names.domaindn,
1007 "DNSPASS_B64": b64encode(dnspass),
1008 "HOSTNAME" : names.hostname,
1009 "DNSNAME" : '%s.%s' % (names.netbiosname.lower(), names.dnsdomain.lower())
1012 def getpolicypath(sysvolpath, dnsdomain, guid):
1013 """Return the physical path of policy given its guid.
1015 :param sysvolpath: Path to the sysvol folder
1016 :param dnsdomain: DNS name of the AD domain
1017 :param guid: The GUID of the policy
1018 :return: A string with the complete path to the policy folder
1022 guid = "{%s}" % guid
1023 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1026 def create_gpo_struct(policy_path):
1027 if not os.path.exists(policy_path):
1028 os.makedirs(policy_path, 0775)
1029 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1030 "[General]\r\nVersion=0")
1031 p = os.path.join(policy_path, "MACHINE")
1032 if not os.path.exists(p):
1033 os.makedirs(p, 0775)
1034 p = os.path.join(policy_path, "USER")
1035 if not os.path.exists(p):
1036 os.makedirs(p, 0775)
1039 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1040 """Create the default GPO for a domain
1042 :param sysvolpath: Physical path for the sysvol folder
1043 :param dnsdomain: DNS domain name of the AD domain
1044 :param policyguid: GUID of the default domain policy
1045 :param policyguid_dc: GUID of the default domain controler policy
1048 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1049 create_gpo_struct(policy_path)
1051 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1052 create_gpo_struct(policy_path)
1055 def setup_samdb(path, setup_path, session_info, provision_backend, lp, names,
1056 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1057 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1058 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1060 """Setup a complete SAM Database.
1062 :note: This will wipe the main SAM database file!
1066 # Provision does not make much sense values larger than 1000000000
1067 # as the upper range of the rIDAvailablePool is 1073741823 and
1068 # we don't want to create a domain that cannot allocate rids.
1069 if next_rid < 1000 or next_rid > 1000000000:
1070 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1071 error += "the valid range is %u-%u. The default is %u." % (1000, 1000000000, 1000)
1072 raise ProvisioningError(error)
1074 # ATTENTION: Do NOT change these default values without discussion with the
1075 # team and/or release manager. They have a big impact on the whole program!
1076 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1078 if dom_for_fun_level is None:
1079 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1081 if dom_for_fun_level > domainControllerFunctionality:
1082 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!")
1084 domainFunctionality = dom_for_fun_level
1085 forestFunctionality = dom_for_fun_level
1087 # Also wipes the database
1088 setup_samdb_partitions(path, setup_path, logger=logger, lp=lp,
1089 provision_backend=provision_backend, session_info=session_info,
1090 names=names, serverrole=serverrole, schema=schema)
1093 schema = Schema(setup_path, domainsid, schemadn=names.schemadn)
1095 # Load the database, but don's load the global schema and don't connect quite yet
1096 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1097 credentials=provision_backend.credentials, lp=lp, global_schema=False,
1100 logger.info("Pre-loading the Samba 4 and AD schema")
1102 # Load the schema from the one we computed earlier
1103 samdb.set_schema(schema)
1105 # Set the NTDS settings DN manually - in order to have it already around
1106 # before the provisioned tree exists and we connect
1107 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1109 # And now we can connect to the DB - the schema won't be loaded from the DB
1112 if fill == FILL_DRS:
1115 samdb.transaction_start()
1117 # Set the domain functionality levels onto the database.
1118 # Various module (the password_hash module in particular) need
1119 # to know what level of AD we are emulating.
1121 # These will be fixed into the database via the database
1122 # modifictions below, but we need them set from the start.
1123 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1124 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1125 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
1127 samdb.set_domain_sid(str(domainsid))
1128 samdb.set_invocation_id(invocationid)
1130 logger.info("Adding DomainDN: %s" % names.domaindn)
1132 #impersonate domain admin
1133 admin_session_info = admin_session(lp, str(domainsid))
1134 samdb.set_session_info(admin_session_info)
1135 if domainguid is not None:
1136 domainguid_line = "objectGUID: %s\n-" % domainguid
1138 domainguid_line = ""
1140 descr = b64encode(get_domain_descriptor(domainsid))
1141 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1142 "DOMAINDN": names.domaindn,
1143 "DOMAINSID": str(domainsid),
1144 "DESCRIPTOR": descr,
1145 "DOMAINGUID": domainguid_line
1148 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1149 "DOMAINDN": names.domaindn,
1150 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1151 "NEXTRID": str(next_rid),
1152 "DEFAULTSITE": names.sitename,
1153 "CONFIGDN": names.configdn,
1154 "POLICYGUID": policyguid,
1155 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1156 "SAMBA_VERSION_STRING": version
1159 logger.info("Adding configuration container")
1160 descr = b64encode(get_config_descriptor(domainsid))
1161 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1162 "CONFIGDN": names.configdn,
1163 "DESCRIPTOR": descr,
1166 # The LDIF here was created when the Schema object was constructed
1167 logger.info("Setting up sam.ldb schema")
1168 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1169 samdb.modify_ldif(schema.schema_dn_modify)
1170 samdb.write_prefixes_from_schema()
1171 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1172 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1173 {"SCHEMADN": names.schemadn})
1175 logger.info("Reopening sam.ldb with new schema")
1177 samdb.transaction_cancel()
1180 samdb.transaction_commit()
1182 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1183 credentials=provision_backend.credentials, lp=lp,
1184 global_schema=False, am_rodc=am_rodc)
1186 # Set the NTDS settings DN manually - in order to have it already around
1187 # before the provisioned tree exists and we connect
1188 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1192 samdb.transaction_start()
1194 samdb.invocation_id = invocationid
1196 logger.info("Setting up sam.ldb configuration data")
1197 descr = b64encode(get_sites_descriptor(domainsid))
1198 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1199 "CONFIGDN": names.configdn,
1200 "NETBIOSNAME": names.netbiosname,
1201 "DEFAULTSITE": names.sitename,
1202 "DNSDOMAIN": names.dnsdomain,
1203 "DOMAIN": names.domain,
1204 "SCHEMADN": names.schemadn,
1205 "DOMAINDN": names.domaindn,
1206 "SERVERDN": names.serverdn,
1207 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1208 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1209 "SITES_DESCRIPTOR": descr
1212 logger.info("Setting up display specifiers")
1213 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1214 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1215 check_all_substituted(display_specifiers_ldif)
1216 samdb.add_ldif(display_specifiers_ldif)
1218 logger.info("Adding users container")
1219 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1220 "DOMAINDN": names.domaindn})
1221 logger.info("Modifying users container")
1222 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1223 "DOMAINDN": names.domaindn})
1224 logger.info("Adding computers container")
1225 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1226 "DOMAINDN": names.domaindn})
1227 logger.info("Modifying computers container")
1228 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1229 "DOMAINDN": names.domaindn})
1230 logger.info("Setting up sam.ldb data")
1231 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1232 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1233 "DOMAINDN": names.domaindn,
1234 "NETBIOSNAME": names.netbiosname,
1235 "DEFAULTSITE": names.sitename,
1236 "CONFIGDN": names.configdn,
1237 "SERVERDN": names.serverdn,
1238 "RIDAVAILABLESTART": str(next_rid + 600),
1239 "POLICYGUID_DC": policyguid_dc
1242 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1243 "DOMAINDN": names.domaindn})
1245 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1246 "CONFIGDN": names.configdn,
1247 "SCHEMADN": names.schemadn})
1248 if fill == FILL_FULL:
1249 logger.info("Setting up sam.ldb users and groups")
1250 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1251 "DOMAINDN": names.domaindn,
1252 "DOMAINSID": str(domainsid),
1253 "CONFIGDN": names.configdn,
1254 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1255 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1258 logger.info("Setting up self join")
1259 setup_self_join(samdb, names=names, invocationid=invocationid,
1261 machinepass=machinepass,
1262 domainsid=domainsid,
1264 policyguid=policyguid,
1265 policyguid_dc=policyguid_dc,
1266 setup_path=setup_path,
1267 domainControllerFunctionality=domainControllerFunctionality,
1270 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1271 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1272 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1273 assert isinstance(names.ntdsguid, str)
1275 samdb.transaction_cancel()
1278 samdb.transaction_commit()
1283 FILL_NT4SYNC = "NT4SYNC"
1285 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1286 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)"
1288 def set_dir_acl(path, acl, lp, domsid):
1289 setntacl(lp, path, acl, domsid)
1290 for root, dirs, files in os.walk(path, topdown=False):
1292 setntacl(lp, os.path.join(root, name), acl, domsid)
1294 setntacl(lp, os.path.join(root, name), acl, domsid)
1297 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1298 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1301 :param sysvol: Physical path for the sysvol folder
1302 :param dnsdomain: The DNS name of the domain
1303 :param domainsid: The SID of the domain
1304 :param domaindn: The DN of the domain (ie. DC=...)
1305 :param samdb: An LDB object on the SAM db
1306 :param lp: an LP object
1309 # Set ACL for GPO root folder
1310 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1311 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1313 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1314 attrs=["cn", "nTSecurityDescriptor"],
1315 expression="", scope=ldb.SCOPE_ONELEVEL)
1318 acl = ndr_unpack(security.descriptor,
1319 str(policy["nTSecurityDescriptor"])).as_sddl()
1320 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1321 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1324 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1326 """Set the ACL for the sysvol share and the subfolders
1328 :param samdb: An LDB object on the SAM db
1329 :param netlogon: Physical path for the netlogon folder
1330 :param sysvol: Physical path for the sysvol folder
1331 :param gid: The GID of the "Domain adminstrators" group
1332 :param domainsid: The SID of the domain
1333 :param dnsdomain: The DNS name of the domain
1334 :param domaindn: The DN of the domain (ie. DC=...)
1338 os.chown(sysvol,-1,gid)
1344 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1345 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1346 for root, dirs, files in os.walk(sysvol, topdown=False):
1349 os.chown(os.path.join(root, name), -1, gid)
1350 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1353 os.chown(os.path.join(root, name), -1, gid)
1354 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1356 # Set acls on Policy folder and policies folders
1357 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1360 def provision(setup_dir, logger, session_info,
1361 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1363 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1365 domain=None, hostname=None, hostip=None, hostip6=None,
1366 domainsid=None, next_rid=1000,
1367 adminpass=None, ldapadminpass=None,
1368 krbtgtpass=None, domainguid=None,
1369 policyguid=None, policyguid_dc=None, invocationid=None,
1370 machinepass=None, ntdsguid=None,
1371 dnspass=None, root=None, nobody=None, users=None,
1372 wheel=None, backup=None, aci=None, serverrole=None,
1373 dom_for_fun_level=None,
1374 ldap_backend_extra_port=None, ldap_backend_forced_uri=None, backend_type=None,
1376 ol_mmr_urls=None, ol_olc=None,
1377 setup_ds_path=None, slapd_path=None, nosync=False,
1378 ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1382 :note: caution, this wipes all existing data!
1385 def setup_path(file):
1386 return os.path.join(setup_dir, file)
1388 if domainsid is None:
1389 domainsid = security.random_sid()
1391 domainsid = security.dom_sid(domainsid)
1393 # create/adapt the group policy GUIDs
1394 # Default GUID for default policy are described at
1395 # "How Core Group Policy Works"
1396 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1397 if policyguid is None:
1398 policyguid = DEFAULT_POLICY_GUID
1399 policyguid = policyguid.upper()
1400 if policyguid_dc is None:
1401 policyguid_dc = DEFAULT_DC_POLICY_GUID
1402 policyguid_dc = policyguid_dc.upper()
1404 if adminpass is None:
1405 adminpass = samba.generate_random_password(12, 32)
1406 if krbtgtpass is None:
1407 krbtgtpass = samba.generate_random_password(128, 255)
1408 if machinepass is None:
1409 machinepass = samba.generate_random_password(128, 255)
1411 dnspass = samba.generate_random_password(128, 255)
1412 if ldapadminpass is None:
1413 #Make a new, random password between Samba and it's LDAP server
1414 ldapadminpass=samba.generate_random_password(128, 255)
1416 if backend_type is None:
1417 backend_type = "ldb"
1419 sid_generator = "internal"
1420 if backend_type == "fedora-ds":
1421 sid_generator = "backend"
1423 root_uid = findnss_uid([root or "root"])
1424 nobody_uid = findnss_uid([nobody or "nobody"])
1425 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1427 wheel_gid = findnss_gid(["wheel", "adm"])
1429 wheel_gid = findnss_gid([wheel])
1431 bind_gid = findnss_gid(["bind", "named"])
1435 if targetdir is not None:
1436 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1437 elif smbconf is None:
1438 smbconf = samba.param.default_path()
1439 if not os.path.exists(os.path.dirname(smbconf)):
1440 os.makedirs(os.path.dirname(smbconf))
1442 # only install a new smb.conf if there isn't one there already
1443 if os.path.exists(smbconf):
1444 # if Samba Team members can't figure out the weird errors
1445 # loading an empty smb.conf gives, then we need to be smarter.
1446 # Pretend it just didn't exist --abartlet
1447 data = open(smbconf, 'r').read()
1448 data = data.lstrip()
1449 if data is None or data == "":
1450 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1451 serverrole, targetdir, sid_generator, useeadb,
1454 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1455 targetdir, sid_generator, useeadb, default_lp=lp)
1458 lp = samba.param.LoadParm()
1460 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1461 dnsdomain=realm, serverrole=serverrole,
1462 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1463 serverdn=serverdn, sitename=sitename)
1464 paths = provision_paths_from_lp(lp, names.dnsdomain)
1466 paths.bind_gid = bind_gid
1469 hostips = samba.interface_ips(lp, False)
1470 if len(hostips) == 0:
1471 logger.warning("No external IPv4 address has been found. Using loopback.")
1472 hostip = '127.0.0.1'
1475 if len(hostips) > 1:
1476 logger.warning("More than one IPv4 address found. Using %s.", hostip)
1480 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1483 if hostip6 == '::1' and ip[-1][0] != '::1':
1485 except socket.gaierror, (socket.EAI_NODATA, msg):
1488 if serverrole is None:
1489 serverrole = lp.get("server role")
1491 assert serverrole in ("domain controller", "member server", "standalone")
1492 if invocationid is None:
1493 invocationid = str(uuid.uuid4())
1495 if not os.path.exists(paths.private_dir):
1496 os.mkdir(paths.private_dir)
1497 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1498 os.mkdir(os.path.join(paths.private_dir, "tls"))
1500 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1502 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn)
1504 if backend_type == "ldb":
1505 provision_backend = LDBBackend(backend_type,
1506 paths=paths, setup_path=setup_path,
1507 lp=lp, credentials=credentials,
1510 elif backend_type == "existing":
1511 provision_backend = ExistingBackend(backend_type,
1512 paths=paths, setup_path=setup_path,
1513 lp=lp, credentials=credentials,
1516 ldap_backend_forced_uri=ldap_backend_forced_uri)
1517 elif backend_type == "fedora-ds":
1518 provision_backend = FDSBackend(backend_type,
1519 paths=paths, setup_path=setup_path,
1520 lp=lp, credentials=credentials,
1523 domainsid=domainsid,
1526 ldapadminpass=ldapadminpass,
1527 slapd_path=slapd_path,
1528 ldap_backend_extra_port=ldap_backend_extra_port,
1529 ldap_dryrun_mode=ldap_dryrun_mode,
1531 setup_ds_path=setup_ds_path,
1532 ldap_backend_forced_uri=ldap_backend_forced_uri)
1533 elif backend_type == "openldap":
1534 provision_backend = OpenLDAPBackend(backend_type,
1535 paths=paths, setup_path=setup_path,
1536 lp=lp, credentials=credentials,
1539 domainsid=domainsid,
1542 ldapadminpass=ldapadminpass,
1543 slapd_path=slapd_path,
1544 ldap_backend_extra_port=ldap_backend_extra_port,
1545 ldap_dryrun_mode=ldap_dryrun_mode,
1546 ol_mmr_urls=ol_mmr_urls,
1548 ldap_backend_forced_uri=ldap_backend_forced_uri)
1550 raise ValueError("Unknown LDAP backend type selected")
1552 provision_backend.init()
1553 provision_backend.start()
1555 # only install a new shares config db if there is none
1556 if not os.path.exists(paths.shareconf):
1557 logger.info("Setting up share.ldb")
1558 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1560 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1562 logger.info("Setting up secrets.ldb")
1563 secrets_ldb = setup_secretsdb(paths, setup_path,
1564 session_info=session_info,
1565 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1568 logger.info("Setting up the registry")
1569 setup_registry(paths.hklm, setup_path, session_info,
1572 logger.info("Setting up the privileges database")
1573 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1575 logger.info("Setting up idmap db")
1576 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1579 logger.info("Setting up SAM db")
1580 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1581 provision_backend, lp, names,
1583 domainsid=domainsid,
1584 schema=schema, domainguid=domainguid,
1585 policyguid=policyguid, policyguid_dc=policyguid_dc,
1587 adminpass=adminpass, krbtgtpass=krbtgtpass,
1588 invocationid=invocationid,
1589 machinepass=machinepass, dnspass=dnspass,
1590 ntdsguid=ntdsguid, serverrole=serverrole,
1591 dom_for_fun_level=dom_for_fun_level,
1592 am_rodc=am_rodc, next_rid=next_rid)
1594 if serverrole == "domain controller":
1595 if paths.netlogon is None:
1596 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1597 logger.info("Please either remove %s or see the template at %s" %
1598 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1599 assert paths.netlogon is not None
1601 if paths.sysvol is None:
1602 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1603 " are configuring a DC.")
1604 logger.info("Please either remove %s or see the template at %s" %
1605 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1606 assert paths.sysvol is not None
1608 if not os.path.isdir(paths.netlogon):
1609 os.makedirs(paths.netlogon, 0755)
1611 if samdb_fill == FILL_FULL:
1612 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1613 root_uid=root_uid, nobody_uid=nobody_uid,
1614 users_gid=users_gid, wheel_gid=wheel_gid)
1616 if serverrole == "domain controller":
1617 # Set up group policies (domain policy and domain controller policy)
1618 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1619 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1620 domainsid, names.dnsdomain, names.domaindn, lp)
1622 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1623 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1625 secretsdb_self_join(secrets_ldb, domain=names.domain,
1627 dnsdomain=names.dnsdomain,
1628 netbiosname=names.netbiosname,
1629 domainsid=domainsid,
1630 machinepass=machinepass,
1631 secure_channel_type=SEC_CHAN_BDC)
1633 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1634 # In future, this might be determined from some configuration
1635 kerberos_enctypes = str(ENC_ALL_TYPES)
1638 msg = ldb.Message(ldb.Dn(samdb, samdb.searchone("distinguishedName", expression="samAccountName=%s$" % names.netbiosname, scope=ldb.SCOPE_SUBTREE)))
1639 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(elements=kerberos_enctypes,
1640 flags=ldb.FLAG_MOD_REPLACE,
1641 name="msDS-SupportedEncryptionTypes")
1643 except ldb.LdbError, (ldb.ERR_NO_SUCH_ATTRIBUTE, _):
1644 # It might be that this attribute does not exist in this schema
1648 if serverrole == "domain controller":
1649 secretsdb_setup_dns(secrets_ldb, setup_path, names,
1651 realm=names.realm, dnsdomain=names.dnsdomain,
1652 dns_keytab_path=paths.dns_keytab,
1655 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1656 assert isinstance(domainguid, str)
1658 # Only make a zone file on the first DC, it should be replicated
1659 # with DNS replication
1660 create_zone_file(lp, logger, paths, targetdir, setup_path,
1661 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1662 hostname=names.hostname, realm=names.realm,
1663 domainguid=domainguid, ntdsguid=names.ntdsguid)
1665 create_named_conf(paths, setup_path, realm=names.realm,
1666 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1668 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1669 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1670 keytab_name=paths.dns_keytab)
1671 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1672 logger.info("and %s for further documentation required for secure DNS "
1673 "updates", paths.namedtxt)
1675 lastProvisionUSNs = get_last_provision_usn(samdb)
1676 maxUSN = get_max_usn(samdb, str(names.rootdn))
1677 if lastProvisionUSNs is not None:
1678 update_provision_usn(samdb, 0, maxUSN, 1)
1680 set_provision_usn(samdb, 0, maxUSN)
1682 create_krb5_conf(paths.krb5conf, setup_path,
1683 dnsdomain=names.dnsdomain, hostname=names.hostname,
1685 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1686 "generated at %s", paths.krb5conf)
1688 if serverrole == "domain controller":
1689 create_dns_update_list(lp, logger, paths, setup_path)
1691 provision_backend.post_setup()
1692 provision_backend.shutdown()
1694 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1697 secrets_ldb.transaction_cancel()
1700 #Now commit the secrets.ldb to disk
1701 secrets_ldb.transaction_commit()
1703 # the commit creates the dns.keytab, now chown it
1704 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1705 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1707 os.chmod(dns_keytab_path, 0640)
1708 os.chown(dns_keytab_path, -1, paths.bind_gid)
1710 if not os.environ.has_key('SAMBA_SELFTEST'):
1711 logger.info("Failed to chown %s to bind gid %u", dns_keytab_path,
1715 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1716 paths.phpldapadminconfig)
1718 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1719 logger.info("Server Role: %s" % serverrole)
1720 logger.info("Hostname: %s" % names.hostname)
1721 logger.info("NetBIOS Domain: %s" % names.domain)
1722 logger.info("DNS Domain: %s" % names.dnsdomain)
1723 logger.info("DOMAIN SID: %s" % str(domainsid))
1724 if samdb_fill == FILL_FULL:
1725 logger.info("Admin password: %s" % adminpass)
1726 if provision_backend.type is not "ldb":
1727 if provision_backend.credentials.get_bind_dn() is not None:
1728 logger.info("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1730 logger.info("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1732 logger.info("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1734 if provision_backend.slapd_command_escaped is not None:
1735 # now display slapd_command_file.txt to show how slapd must be started next time
1736 logger.info("Use later the following commandline to start slapd, then Samba:")
1737 logger.info(provision_backend.slapd_command_escaped)
1738 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1739 provision_backend.ldapdir)
1741 result = ProvisionResult()
1742 result.domaindn = domaindn
1743 result.paths = paths
1745 result.samdb = samdb
1749 def provision_become_dc(setup_dir=None,
1750 smbconf=None, targetdir=None, realm=None,
1751 rootdn=None, domaindn=None, schemadn=None,
1752 configdn=None, serverdn=None,
1753 domain=None, hostname=None, domainsid=None,
1754 adminpass=None, krbtgtpass=None, domainguid=None,
1755 policyguid=None, policyguid_dc=None, invocationid=None,
1757 dnspass=None, root=None, nobody=None, users=None,
1758 wheel=None, backup=None, serverrole=None,
1759 ldap_backend=None, ldap_backend_type=None,
1760 sitename=None, debuglevel=1):
1762 logger = logging.getLogger("provision")
1763 samba.set_debug_level(debuglevel)
1765 res = provision(setup_dir, logger, system_session(), None,
1766 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1767 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1768 configdn=configdn, serverdn=serverdn, domain=domain,
1769 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1770 machinepass=machinepass, serverrole="domain controller",
1772 res.lp.set("debuglevel", str(debuglevel))
1776 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1777 """Create a PHP LDAP admin configuration file.
1779 :param path: Path to write the configuration to.
1780 :param setup_path: Function to generate setup paths.
1782 setup_file(setup_path("phpldapadmin-config.php"), path,
1783 {"S4_LDAPI_URI": ldapi_uri})
1786 def create_zone_file(lp, logger, paths, targetdir, setup_path, dnsdomain,
1787 hostip, hostip6, hostname, realm, domainguid,
1789 """Write out a DNS zone file, from the info in the current database.
1791 :param paths: paths object
1792 :param setup_path: Setup path function.
1793 :param dnsdomain: DNS Domain name
1794 :param domaindn: DN of the Domain
1795 :param hostip: Local IPv4 IP
1796 :param hostip6: Local IPv6 IP
1797 :param hostname: Local hostname
1798 :param realm: Realm name
1799 :param domainguid: GUID of the domain.
1800 :param ntdsguid: GUID of the hosts nTDSDSA record.
1802 assert isinstance(domainguid, str)
1804 if hostip6 is not None:
1805 hostip6_base_line = " IN AAAA " + hostip6
1806 hostip6_host_line = hostname + " IN AAAA " + hostip6
1807 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1809 hostip6_base_line = ""
1810 hostip6_host_line = ""
1811 gc_msdcs_ip6_line = ""
1813 if hostip is not None:
1814 hostip_base_line = " IN A " + hostip
1815 hostip_host_line = hostname + " IN A " + hostip
1816 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1818 hostip_base_line = ""
1819 hostip_host_line = ""
1820 gc_msdcs_ip_line = ""
1822 dns_dir = os.path.dirname(paths.dns)
1825 shutil.rmtree(dns_dir, True)
1829 os.mkdir(dns_dir, 0775)
1831 # we need to freeze the zone while we update the contents
1832 if targetdir is None:
1833 rndc = ' '.join(lp.get("rndc command"))
1834 os.system(rndc + " freeze " + lp.get("realm"))
1836 setup_file(setup_path("provision.zone"), paths.dns, {
1837 "HOSTNAME": hostname,
1838 "DNSDOMAIN": dnsdomain,
1840 "HOSTIP_BASE_LINE": hostip_base_line,
1841 "HOSTIP_HOST_LINE": hostip_host_line,
1842 "DOMAINGUID": domainguid,
1843 "DATESTRING": time.strftime("%Y%m%d%H"),
1844 "DEFAULTSITE": DEFAULTSITE,
1845 "NTDSGUID": ntdsguid,
1846 "HOSTIP6_BASE_LINE": hostip6_base_line,
1847 "HOSTIP6_HOST_LINE": hostip6_host_line,
1848 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1849 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1852 # note that we use no variable substitution on this file
1853 # the substitution is done at runtime by samba_dnsupdate
1854 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1856 # and the SPN update list
1857 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1859 if paths.bind_gid is not None:
1861 os.chown(dns_dir, -1, paths.bind_gid)
1862 os.chown(paths.dns, -1, paths.bind_gid)
1863 # chmod needed to cope with umask
1864 os.chmod(dns_dir, 0775)
1865 os.chmod(paths.dns, 0664)
1867 if not os.environ.has_key('SAMBA_SELFTEST'):
1868 logger.error("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1870 if targetdir is None:
1871 os.system(rndc + " unfreeze " + lp.get("realm"))
1874 def create_dns_update_list(lp, logger, paths, setup_path):
1875 """Write out a dns_update_list file"""
1876 # note that we use no variable substitution on this file
1877 # the substitution is done at runtime by samba_dnsupdate
1878 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1879 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1882 def create_named_conf(paths, setup_path, realm, dnsdomain,
1884 """Write out a file containing zone statements suitable for inclusion in a
1885 named.conf file (including GSS-TSIG configuration).
1887 :param paths: all paths
1888 :param setup_path: Setup path function.
1889 :param realm: Realm name
1890 :param dnsdomain: DNS Domain name
1891 :param private_dir: Path to private directory
1892 :param keytab_name: File name of DNS keytab file
1895 setup_file(setup_path("named.conf"), paths.namedconf, {
1896 "DNSDOMAIN": dnsdomain,
1898 "ZONE_FILE": paths.dns,
1899 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1900 "NAMED_CONF": paths.namedconf,
1901 "NAMED_CONF_UPDATE": paths.namedconf_update
1904 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1907 def create_named_txt(path, setup_path, realm, dnsdomain,
1908 private_dir, keytab_name):
1909 """Write out a file containing zone statements suitable for inclusion in a
1910 named.conf file (including GSS-TSIG configuration).
1912 :param path: Path of the new named.conf file.
1913 :param setup_path: Setup path function.
1914 :param realm: Realm name
1915 :param dnsdomain: DNS Domain name
1916 :param private_dir: Path to private directory
1917 :param keytab_name: File name of DNS keytab file
1920 setup_file(setup_path("named.txt"), path, {
1921 "DNSDOMAIN": dnsdomain,
1923 "DNS_KEYTAB": keytab_name,
1924 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1925 "PRIVATE_DIR": private_dir
1929 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1930 """Write out a file containing zone statements suitable for inclusion in a
1931 named.conf file (including GSS-TSIG configuration).
1933 :param path: Path of the new named.conf file.
1934 :param setup_path: Setup path function.
1935 :param dnsdomain: DNS Domain name
1936 :param hostname: Local hostname
1937 :param realm: Realm name
1939 setup_file(setup_path("krb5.conf"), path, {
1940 "DNSDOMAIN": dnsdomain,
1941 "HOSTNAME": hostname,
1946 class ProvisioningError(Exception):
1947 """A generic provision error."""
1949 def __init__(self, value):
1953 return "ProvisioningError: " + self.value
1956 class InvalidNetbiosName(Exception):
1957 """A specified name was not a valid NetBIOS name."""
1958 def __init__(self, name):
1959 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)