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 __docformat__ = "restructuredText"
30 from base64 import b64encode
45 from samba.auth import system_session, admin_session
47 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
50 check_all_substituted,
57 from samba.dcerpc import security, misc
58 from samba.dcerpc.misc import (
62 from samba.dsdb import (
63 DS_DOMAIN_FUNCTION_2003,
64 DS_DOMAIN_FUNCTION_2008_R2,
67 from samba.idmap import IDmapDB
68 from samba.ms_display_specifiers import read_ms_ldif
69 from samba.ntacls import setntacl, dsacl2fsacl
70 from samba.ndr import ndr_pack, ndr_unpack
71 from samba.provision.backend import (
77 from samba.provision.sambadns import setup_ad_dns
81 from samba.schema import Schema
82 from samba.samdb import SamDB
84 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
85 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
86 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
87 DEFAULTSITE = "Default-First-Site-Name"
88 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
92 """Return an absolute path to the provision tempate file specified by file"""
93 return os.path.join(samba.param.setup_dir(), file)
95 # Descriptors of naming contexts and other important objects
97 # "get_schema_descriptor" is located in "schema.py"
99 def get_config_descriptor(domain_sid):
100 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
101 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
102 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
103 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
104 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
105 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
106 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
107 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
108 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
109 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
110 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
111 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
112 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
113 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
114 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
115 sec = security.descriptor.from_sddl(sddl, domain_sid)
119 def get_domain_descriptor(domain_sid):
120 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
121 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
122 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
129 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
130 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
131 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
132 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
133 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
134 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
135 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
136 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
137 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
138 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
140 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
141 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
142 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
143 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
144 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
145 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
146 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
147 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
148 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
149 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
150 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
151 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
152 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
153 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
154 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
155 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
156 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
157 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
158 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
161 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
163 "(A;;RPLCLORC;;;ED)" \
164 "(A;;RPLCLORC;;;AU)" \
165 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
166 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
167 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
168 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
169 sec = security.descriptor.from_sddl(sddl, domain_sid)
173 class ProvisionPaths(object):
176 self.shareconf = None
187 self.dns_keytab = None
190 self.private_dir = None
193 class ProvisionNames(object):
200 self.ldapmanagerdn = None
201 self.dnsdomain = None
203 self.netbiosname = None
209 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
210 """Get key provision parameters (realm, domain, ...) from a given provision
212 :param samdb: An LDB object connected to the sam.ldb file
213 :param secretsdb: An LDB object connected to the secrets.ldb file
214 :param idmapdb: An LDB object connected to the idmap.ldb file
215 :param paths: A list of path to provision object
216 :param smbconf: Path to the smb.conf file
217 :param lp: A LoadParm object
218 :return: A list of key provision parameters
220 names = ProvisionNames()
221 names.adminpass = None
223 # NT domain, kerberos realm, root dn, domain dn, domain dns name
224 names.domain = string.upper(lp.get("workgroup"))
225 names.realm = lp.get("realm")
226 basedn = "DC=" + names.realm.replace(".",",DC=")
227 names.dnsdomain = names.realm.lower()
228 names.realm = string.upper(names.realm)
230 # Get the netbiosname first (could be obtained from smb.conf in theory)
231 res = secretsdb.search(expression="(flatname=%s)" %
232 names.domain,base="CN=Primary Domains",
233 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
234 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
236 names.smbconf = smbconf
238 # That's a bit simplistic but it's ok as long as we have only 3
240 current = samdb.search(expression="(objectClass=*)",
241 base="", scope=ldb.SCOPE_BASE,
242 attrs=["defaultNamingContext", "schemaNamingContext",
243 "configurationNamingContext","rootDomainNamingContext"])
245 names.configdn = current[0]["configurationNamingContext"]
246 configdn = str(names.configdn)
247 names.schemadn = current[0]["schemaNamingContext"]
248 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
249 current[0]["defaultNamingContext"][0]))):
250 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
251 "is not the same ..." % (paths.samdb,
252 str(current[0]["defaultNamingContext"][0]),
253 paths.smbconf, basedn)))
255 names.domaindn=current[0]["defaultNamingContext"]
256 names.rootdn=current[0]["rootDomainNamingContext"]
258 res3 = samdb.search(expression="(objectClass=site)",
259 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
260 names.sitename = str(res3[0]["cn"])
262 # dns hostname and server dn
263 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
264 base="OU=Domain Controllers,%s" % basedn,
265 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
266 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
268 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
269 attrs=[], base=configdn)
270 names.serverdn = server_res[0].dn
272 # invocation id/objectguid
273 res5 = samdb.search(expression="(objectClass=*)",
274 base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
275 attrs=["invocationID", "objectGUID"])
276 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
277 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
280 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
281 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
282 "objectSid","msDS-Behavior-Version" ])
283 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
284 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
285 if res6[0].get("msDS-Behavior-Version") is None or \
286 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
287 names.domainlevel = DS_DOMAIN_FUNCTION_2000
289 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
292 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
293 base="CN=Policies,CN=System," + basedn,
294 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
295 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
297 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
299 base="CN=Policies,CN=System," + basedn,
300 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
302 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
304 names.policyid_dc = None
305 res9 = idmapdb.search(expression="(cn=%s)" %
306 (security.SID_BUILTIN_ADMINISTRATORS),
309 names.wheel_gid = res9[0]["xidNumber"]
311 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
314 def update_provision_usn(samdb, low, high, id, replace=False):
315 """Update the field provisionUSN in sam.ldb
317 This field is used to track range of USN modified by provision and
319 This value is used afterward by next provision to figure out if
320 the field have been modified since last provision.
322 :param samdb: An LDB object connect to sam.ldb
323 :param low: The lowest USN modified by this upgrade
324 :param high: The highest USN modified by this upgrade
325 :param id: The invocation id of the samba's dc
326 :param replace: A boolean indicating if the range should replace any
327 existing one or appended (default)
332 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" %
333 LAST_PROVISION_USN_ATTRIBUTE, base="",
334 scope=ldb.SCOPE_SUBTREE,
335 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
336 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
337 if not re.search(';', e):
338 e = "%s;%s" % (e, id)
341 tab.append("%s-%s;%s" % (low, high, id))
342 delta = ldb.Message()
343 delta.dn = ldb.Dn(samdb, "@PROVISION")
344 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
345 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
346 entry = samdb.search(expression="(&(dn=@PROVISION)(provisionnerID=*))",
347 base="", scope=ldb.SCOPE_SUBTREE,
348 attrs=["provisionnerID"])
349 if len(entry) == 0 or len(entry[0]) == 0:
350 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
354 def set_provision_usn(samdb, low, high, id):
355 """Set the field provisionUSN in sam.ldb
356 This field is used to track range of USN modified by provision and
358 This value is used afterward by next provision to figure out if
359 the field have been modified since last provision.
361 :param samdb: An LDB object connect to sam.ldb
362 :param low: The lowest USN modified by this upgrade
363 :param high: The highest USN modified by this upgrade
364 :param id: The invocationId of the provision"""
367 tab.append("%s-%s;%s" % (low, high, id))
369 delta = ldb.Message()
370 delta.dn = ldb.Dn(samdb, "@PROVISION")
371 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
372 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
376 def get_max_usn(samdb,basedn):
377 """ This function return the biggest USN present in the provision
379 :param samdb: A LDB object pointing to the sam.ldb
380 :param basedn: A string containing the base DN of the provision
382 :return: The biggest USN in the provision"""
384 res = samdb.search(expression="objectClass=*",base=basedn,
385 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
386 controls=["search_options:1:2",
387 "server_sort:1:1:uSNChanged",
388 "paged_results:1:1"])
389 return res[0]["uSNChanged"]
392 def get_last_provision_usn(sam):
393 """Get USNs ranges modified by a provision or an upgradeprovision
395 :param sam: An LDB object pointing to the sam.ldb
396 :return: a dictionnary which keys are invocation id and values are an array
397 of integer representing the different ranges
399 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" %
400 LAST_PROVISION_USN_ATTRIBUTE,
401 base="", scope=ldb.SCOPE_SUBTREE,
402 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
407 if entry[0].get("provisionnerID"):
408 for e in entry[0]["provisionnerID"]:
410 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
411 tab1 = str(r).split(';')
416 if (len(myids) > 0 and id not in myids):
418 tab2 = p.split(tab1[0])
419 if range.get(id) == None:
421 range[id].append(tab2[0])
422 range[id].append(tab2[1])
428 class ProvisionResult(object):
437 def check_install(lp, session_info, credentials):
438 """Check whether the current install seems ok.
440 :param lp: Loadparm context
441 :param session_info: Session information
442 :param credentials: Credentials
444 if lp.get("realm") == "":
445 raise Exception("Realm empty")
446 samdb = Ldb(lp.samdb_url(), session_info=session_info,
447 credentials=credentials, lp=lp)
448 if len(samdb.search("(cn=Administrator)")) != 1:
449 raise ProvisioningError("No administrator account found")
452 def findnss(nssfn, names):
453 """Find a user or group from a list of possibilities.
455 :param nssfn: NSS Function to try (should raise KeyError if not found)
456 :param names: Names to check.
457 :return: Value return by first names list.
464 raise KeyError("Unable to find user/group in %r" % names)
467 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
468 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
471 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
472 """Setup a ldb in the private dir.
474 :param ldb: LDB file to import data into
475 :param ldif_path: Path of the LDIF file to load
476 :param subst_vars: Optional variables to subsitute in LDIF.
477 :param nocontrols: Optional list of controls, can be None for no controls
479 assert isinstance(ldif_path, str)
480 data = read_and_sub_file(ldif_path, subst_vars)
481 ldb.add_ldif(data, controls)
484 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
485 """Modify a ldb in the private dir.
487 :param ldb: LDB object.
488 :param ldif_path: LDIF file path.
489 :param subst_vars: Optional dictionary with substitution variables.
491 data = read_and_sub_file(ldif_path, subst_vars)
492 ldb.modify_ldif(data, controls)
495 def setup_ldb(ldb, ldif_path, subst_vars):
496 """Import a LDIF a file into a LDB handle, optionally substituting
499 :note: Either all LDIF data will be added or none (using transactions).
501 :param ldb: LDB file to import into.
502 :param ldif_path: Path to the LDIF file.
503 :param subst_vars: Dictionary with substitution variables.
505 assert ldb is not None
506 ldb.transaction_start()
508 setup_add_ldif(ldb, ldif_path, subst_vars)
510 ldb.transaction_cancel()
513 ldb.transaction_commit()
516 def provision_paths_from_lp(lp, dnsdomain):
517 """Set the default paths for provisioning.
519 :param lp: Loadparm context.
520 :param dnsdomain: DNS Domain name
522 paths = ProvisionPaths()
523 paths.private_dir = lp.get("private dir")
525 # This is stored without path prefix for the "privateKeytab" attribute in
526 # "secrets_dns.ldif".
527 paths.dns_keytab = "dns.keytab"
528 paths.keytab = "secrets.keytab"
530 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
531 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
532 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
533 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
534 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
535 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
536 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
537 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
538 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
539 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
540 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
541 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
542 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
543 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
544 paths.phpldapadminconfig = os.path.join(paths.private_dir,
545 "phpldapadmin-config.php")
546 paths.hklm = "hklm.ldb"
547 paths.hkcr = "hkcr.ldb"
548 paths.hkcu = "hkcu.ldb"
549 paths.hku = "hku.ldb"
550 paths.hkpd = "hkpd.ldb"
551 paths.hkpt = "hkpt.ldb"
552 paths.sysvol = lp.get("path", "sysvol")
553 paths.netlogon = lp.get("path", "netlogon")
554 paths.smbconf = lp.configfile
558 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
559 serverrole=None, rootdn=None, domaindn=None, configdn=None,
560 schemadn=None, serverdn=None, sitename=None):
561 """Guess configuration settings to use."""
564 hostname = socket.gethostname().split(".")[0]
566 netbiosname = lp.get("netbios name")
567 if netbiosname is None:
568 netbiosname = hostname
569 # remove forbidden chars
571 for x in netbiosname:
572 if x.isalnum() or x in VALID_NETBIOS_CHARS:
573 newnbname = "%s%c" % (newnbname, x)
574 # force the length to be <16
575 netbiosname = newnbname[0:15]
576 assert netbiosname is not None
577 netbiosname = netbiosname.upper()
578 if not valid_netbios_name(netbiosname):
579 raise InvalidNetbiosName(netbiosname)
581 if dnsdomain is None:
582 dnsdomain = lp.get("realm")
583 if dnsdomain is None or dnsdomain == "":
584 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
586 dnsdomain = dnsdomain.lower()
588 if serverrole is None:
589 serverrole = lp.get("server role")
590 if serverrole is None:
591 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
593 serverrole = serverrole.lower()
595 realm = dnsdomain.upper()
597 if lp.get("realm") == "":
598 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
600 if lp.get("realm").upper() != realm:
601 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))
603 if lp.get("server role").lower() != serverrole:
604 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"), serverrole, lp.configfile))
606 if serverrole == "domain controller":
608 # This will, for better or worse, default to 'WORKGROUP'
609 domain = lp.get("workgroup")
610 domain = domain.upper()
612 if lp.get("workgroup").upper() != domain:
613 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))
616 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
618 if domain == netbiosname:
619 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
623 domaindn = "DC=" + netbiosname
625 if not valid_netbios_name(domain):
626 raise InvalidNetbiosName(domain)
628 if hostname.upper() == realm:
629 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
630 if netbiosname.upper() == realm:
631 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
633 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
639 configdn = "CN=Configuration," + rootdn
641 schemadn = "CN=Schema," + configdn
646 names = ProvisionNames()
647 names.rootdn = rootdn
648 names.domaindn = domaindn
649 names.configdn = configdn
650 names.schemadn = schemadn
651 names.ldapmanagerdn = "CN=Manager," + rootdn
652 names.dnsdomain = dnsdomain
653 names.domain = domain
655 names.netbiosname = netbiosname
656 names.hostname = hostname
657 names.sitename = sitename
658 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
659 netbiosname, sitename, configdn)
664 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
665 targetdir, sid_generator="internal", eadb=False, lp=None):
666 """Create a new smb.conf file based on a couple of basic settings.
668 assert smbconf is not None
670 hostname = socket.gethostname().split(".")[0]
671 netbiosname = hostname.upper()
672 # remove forbidden chars
674 for x in netbiosname:
675 if x.isalnum() or x in VALID_NETBIOS_CHARS:
676 newnbname = "%s%c" % (newnbname, x)
677 #force the length to be <16
678 netbiosname = newnbname[0:15]
680 netbiosname = hostname.upper()
682 if serverrole is None:
683 serverrole = "standalone"
685 assert serverrole in ("domain controller", "member server", "standalone")
686 if serverrole == "domain controller":
688 elif serverrole == "member server":
689 smbconfsuffix = "member"
690 elif serverrole == "standalone":
691 smbconfsuffix = "standalone"
693 if sid_generator is None:
694 sid_generator = "internal"
696 assert domain is not None
697 domain = domain.upper()
699 assert realm is not None
700 realm = realm.upper()
703 lp = samba.param.LoadParm()
704 #Load non-existant file
705 if os.path.exists(smbconf):
707 if eadb and not lp.get("posix:eadb"):
708 if targetdir is not None:
709 privdir = os.path.join(targetdir, "private")
711 privdir = lp.get("private dir")
712 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
714 if targetdir is not None:
715 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
716 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
718 lp.set("lock dir", os.path.abspath(targetdir))
723 sysvol = os.path.join(lp.get("lock dir"), "sysvol")
724 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
726 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
728 "NETBIOS_NAME": netbiosname,
731 "SERVERROLE": serverrole,
732 "NETLOGONPATH": netlogon,
733 "SYSVOLPATH": sysvol,
734 "PRIVATEDIR_LINE": privatedir_line,
735 "LOCKDIR_LINE": lockdir_line
738 # reload the smb.conf
741 # and dump it without any values that are the default
742 # this ensures that any smb.conf parameters that were set
743 # on the provision/join command line are set in the resulting smb.conf
744 f = open(smbconf, mode='w')
750 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
751 users_gid, wheel_gid):
752 """setup reasonable name mappings for sam names to unix names.
754 :param samdb: SamDB object.
755 :param idmap: IDmap db object.
756 :param sid: The domain sid.
757 :param domaindn: The domain DN.
758 :param root_uid: uid of the UNIX root user.
759 :param nobody_uid: uid of the UNIX nobody user.
760 :param users_gid: gid of the UNIX users group.
761 :param wheel_gid: gid of the UNIX wheel group.
763 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
764 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
766 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
767 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
770 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
771 provision_backend, names, schema, serverrole,
773 """Setup the partitions for the SAM database.
775 Alternatively, provision() may call this, and then populate the database.
777 :note: This will wipe the Sam Database!
779 :note: This function always removes the local SAM LDB file. The erase
780 parameter controls whether to erase the existing data, which
781 may not be stored locally but in LDAP.
784 assert session_info is not None
786 # We use options=["modules:"] to stop the modules loading - we
787 # just want to wipe and re-initialise the database, not start it up
790 os.unlink(samdb_path)
794 samdb = Ldb(url=samdb_path, session_info=session_info,
795 lp=lp, options=["modules:"])
797 ldap_backend_line = "# No LDAP backend"
798 if provision_backend.type is not "ldb":
799 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
801 samdb.transaction_start()
803 logger.info("Setting up sam.ldb partitions and settings")
804 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
805 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
806 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
807 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
808 "LDAP_BACKEND_LINE": ldap_backend_line,
812 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
813 "BACKEND_TYPE": provision_backend.type,
814 "SERVER_ROLE": serverrole
817 logger.info("Setting up sam.ldb rootDSE")
818 setup_samdb_rootdse(samdb, names)
820 samdb.transaction_cancel()
823 samdb.transaction_commit()
826 def secretsdb_self_join(secretsdb, domain,
827 netbiosname, machinepass, domainsid=None,
828 realm=None, dnsdomain=None,
830 key_version_number=1,
831 secure_channel_type=SEC_CHAN_WKSTA):
832 """Add domain join-specific bits to a secrets database.
834 :param secretsdb: Ldb Handle to the secrets database
835 :param machinepass: Machine password
837 attrs = ["whenChanged",
844 if realm is not None:
845 if dnsdomain is None:
846 dnsdomain = realm.lower()
847 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
850 shortname = netbiosname.lower()
852 # We don't need to set msg["flatname"] here, because rdn_name will handle
853 # it, and it causes problems for modifies anyway
854 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
855 msg["secureChannelType"] = [str(secure_channel_type)]
856 msg["objectClass"] = ["top", "primaryDomain"]
857 if dnsname is not None:
858 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
859 msg["realm"] = [realm]
860 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
861 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
862 msg["privateKeytab"] = ["secrets.keytab"]
864 msg["secret"] = [machinepass]
865 msg["samAccountName"] = ["%s$" % netbiosname]
866 msg["secureChannelType"] = [str(secure_channel_type)]
867 if domainsid is not None:
868 msg["objectSid"] = [ndr_pack(domainsid)]
870 # This complex expression tries to ensure that we don't have more
871 # than one record for this SID, realm or netbios domain at a time,
872 # but we don't delete the old record that we are about to modify,
873 # because that would delete the keytab and previous password.
874 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
875 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
876 scope=ldb.SCOPE_ONELEVEL)
879 secretsdb.delete(del_msg.dn)
881 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
884 msg["priorSecret"] = [res[0]["secret"][0]]
885 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
888 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
893 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
899 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
900 secretsdb.modify(msg)
901 secretsdb.rename(res[0].dn, msg.dn)
903 spn = [ 'HOST/%s' % shortname ]
904 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
905 # we are a domain controller then we add servicePrincipalName
906 # entries for the keytab code to update.
907 spn.extend([ 'HOST/%s' % dnsname ])
908 msg["servicePrincipalName"] = spn
913 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
914 dnsdomain, dns_keytab_path, dnspass):
915 """Add DNS specific bits to a secrets database.
917 :param secretsdb: Ldb Handle to the secrets database
918 :param machinepass: Machine password
921 os.unlink(os.path.join(private_dir, dns_keytab_path))
925 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
927 "DNSDOMAIN": dnsdomain,
928 "DNS_KEYTAB": dns_keytab_path,
929 "DNSPASS_B64": b64encode(dnspass),
930 "HOSTNAME": names.hostname,
931 "DNSNAME" : '%s.%s' % (
932 names.netbiosname.lower(), names.dnsdomain.lower())
936 def setup_secretsdb(paths, session_info, backend_credentials, lp):
937 """Setup the secrets database.
939 :note: This function does not handle exceptions and transaction on purpose,
940 it's up to the caller to do this job.
942 :param path: Path to the secrets database.
943 :param session_info: Session info.
944 :param credentials: Credentials
945 :param lp: Loadparm context
946 :return: LDB handle for the created secrets database
948 if os.path.exists(paths.secrets):
949 os.unlink(paths.secrets)
951 keytab_path = os.path.join(paths.private_dir, paths.keytab)
952 if os.path.exists(keytab_path):
953 os.unlink(keytab_path)
955 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
956 if os.path.exists(dns_keytab_path):
957 os.unlink(dns_keytab_path)
961 secrets_ldb = Ldb(path, session_info=session_info,
964 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
965 secrets_ldb = Ldb(path, session_info=session_info,
967 secrets_ldb.transaction_start()
969 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
971 if (backend_credentials is not None and
972 backend_credentials.authentication_requested()):
973 if backend_credentials.get_bind_dn() is not None:
974 setup_add_ldif(secrets_ldb,
975 setup_path("secrets_simple_ldap.ldif"), {
976 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
977 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
980 setup_add_ldif(secrets_ldb,
981 setup_path("secrets_sasl_ldap.ldif"), {
982 "LDAPADMINUSER": backend_credentials.get_username(),
983 "LDAPADMINREALM": backend_credentials.get_realm(),
984 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
989 secrets_ldb.transaction_cancel()
993 def setup_privileges(path, session_info, lp):
994 """Setup the privileges database.
996 :param path: Path to the privileges database.
997 :param session_info: Session info.
998 :param credentials: Credentials
999 :param lp: Loadparm context
1000 :return: LDB handle for the created secrets database
1002 if os.path.exists(path):
1004 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1005 privilege_ldb.erase()
1006 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1009 def setup_registry(path, session_info, lp):
1010 """Setup the registry.
1012 :param path: Path to the registry database
1013 :param session_info: Session information
1014 :param credentials: Credentials
1015 :param lp: Loadparm context
1017 reg = samba.registry.Registry()
1018 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1019 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1020 provision_reg = setup_path("provision.reg")
1021 assert os.path.exists(provision_reg)
1022 reg.diff_apply(provision_reg)
1025 def setup_idmapdb(path, session_info, lp):
1026 """Setup the idmap database.
1028 :param path: path to the idmap database
1029 :param session_info: Session information
1030 :param credentials: Credentials
1031 :param lp: Loadparm context
1033 if os.path.exists(path):
1036 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1038 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1042 def setup_samdb_rootdse(samdb, names):
1043 """Setup the SamDB rootdse.
1045 :param samdb: Sam Database handle
1047 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1048 "SCHEMADN": names.schemadn,
1049 "DOMAINDN": names.domaindn,
1050 "ROOTDN": names.rootdn,
1051 "CONFIGDN": names.configdn,
1052 "SERVERDN": names.serverdn,
1056 def setup_self_join(samdb, names, machinepass, dnspass,
1057 domainsid, next_rid, invocationid,
1058 policyguid, policyguid_dc, domainControllerFunctionality,
1060 """Join a host to its own domain."""
1061 assert isinstance(invocationid, str)
1062 if ntdsguid is not None:
1063 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1066 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1067 "CONFIGDN": names.configdn,
1068 "SCHEMADN": names.schemadn,
1069 "DOMAINDN": names.domaindn,
1070 "SERVERDN": names.serverdn,
1071 "INVOCATIONID": invocationid,
1072 "NETBIOSNAME": names.netbiosname,
1073 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1074 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1075 "DOMAINSID": str(domainsid),
1076 "DCRID": str(next_rid),
1077 "SAMBA_VERSION_STRING": version,
1078 "NTDSGUID": ntdsguid_line,
1079 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1080 domainControllerFunctionality)})
1082 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1083 "POLICYGUID": policyguid,
1084 "POLICYGUID_DC": policyguid_dc,
1085 "DNSDOMAIN": names.dnsdomain,
1086 "DOMAINDN": names.domaindn})
1088 # add the NTDSGUID based SPNs
1089 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1090 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1091 expression="", scope=ldb.SCOPE_BASE)
1092 assert isinstance(names.ntdsguid, str)
1094 # Setup fSMORoleOwner entries to point at the newly created DC entry
1095 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1096 "DOMAINDN": names.domaindn,
1097 "CONFIGDN": names.configdn,
1098 "SCHEMADN": names.schemadn,
1099 "DEFAULTSITE": names.sitename,
1100 "SERVERDN": names.serverdn,
1101 "NETBIOSNAME": names.netbiosname,
1102 "RIDALLOCATIONSTART": str(next_rid + 100),
1103 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1106 # This is Samba4 specific and should be replaced by the correct
1107 # DNS AD-style setup
1108 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1109 "DNSDOMAIN": names.dnsdomain,
1110 "DOMAINDN": names.domaindn,
1111 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1112 "HOSTNAME" : names.hostname,
1113 "DNSNAME" : '%s.%s' % (
1114 names.netbiosname.lower(), names.dnsdomain.lower())
1118 def getpolicypath(sysvolpath, dnsdomain, guid):
1119 """Return the physical path of policy given its guid.
1121 :param sysvolpath: Path to the sysvol folder
1122 :param dnsdomain: DNS name of the AD domain
1123 :param guid: The GUID of the policy
1124 :return: A string with the complete path to the policy folder
1128 guid = "{%s}" % guid
1129 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1133 def create_gpo_struct(policy_path):
1134 if not os.path.exists(policy_path):
1135 os.makedirs(policy_path, 0775)
1136 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1137 "[General]\r\nVersion=0")
1138 p = os.path.join(policy_path, "MACHINE")
1139 if not os.path.exists(p):
1140 os.makedirs(p, 0775)
1141 p = os.path.join(policy_path, "USER")
1142 if not os.path.exists(p):
1143 os.makedirs(p, 0775)
1146 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1147 """Create the default GPO for a domain
1149 :param sysvolpath: Physical path for the sysvol folder
1150 :param dnsdomain: DNS domain name of the AD domain
1151 :param policyguid: GUID of the default domain policy
1152 :param policyguid_dc: GUID of the default domain controler policy
1154 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1155 create_gpo_struct(policy_path)
1157 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1158 create_gpo_struct(policy_path)
1161 def setup_samdb(path, session_info, provision_backend, lp, names,
1162 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1163 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1164 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1166 """Setup a complete SAM Database.
1168 :note: This will wipe the main SAM database file!
1171 # Provision does not make much sense values larger than 1000000000
1172 # as the upper range of the rIDAvailablePool is 1073741823 and
1173 # we don't want to create a domain that cannot allocate rids.
1174 if next_rid < 1000 or next_rid > 1000000000:
1175 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1176 error += "the valid range is %u-%u. The default is %u." % (
1177 1000, 1000000000, 1000)
1178 raise ProvisioningError(error)
1180 # ATTENTION: Do NOT change these default values without discussion with the
1181 # team and/or release manager. They have a big impact on the whole program!
1182 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1184 if dom_for_fun_level is None:
1185 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1187 if dom_for_fun_level > domainControllerFunctionality:
1188 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!")
1190 domainFunctionality = dom_for_fun_level
1191 forestFunctionality = dom_for_fun_level
1193 # Also wipes the database
1194 setup_samdb_partitions(path, logger=logger, lp=lp,
1195 provision_backend=provision_backend, session_info=session_info,
1196 names=names, serverrole=serverrole, schema=schema)
1199 schema = Schema(domainsid, schemadn=names.schemadn)
1201 # Load the database, but don's load the global schema and don't connect
1203 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1204 credentials=provision_backend.credentials, lp=lp,
1205 global_schema=False, am_rodc=am_rodc)
1207 logger.info("Pre-loading the Samba 4 and AD schema")
1209 # Load the schema from the one we computed earlier
1210 samdb.set_schema(schema)
1212 # Set the NTDS settings DN manually - in order to have it already around
1213 # before the provisioned tree exists and we connect
1214 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1216 # And now we can connect to the DB - the schema won't be loaded from the
1220 if fill == FILL_DRS:
1223 samdb.transaction_start()
1225 # Set the domain functionality levels onto the database.
1226 # Various module (the password_hash module in particular) need
1227 # to know what level of AD we are emulating.
1229 # These will be fixed into the database via the database
1230 # modifictions below, but we need them set from the start.
1231 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1232 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1233 samdb.set_opaque_integer("domainControllerFunctionality",
1234 domainControllerFunctionality)
1236 samdb.set_domain_sid(str(domainsid))
1237 samdb.set_invocation_id(invocationid)
1239 logger.info("Adding DomainDN: %s" % names.domaindn)
1241 # impersonate domain admin
1242 admin_session_info = admin_session(lp, str(domainsid))
1243 samdb.set_session_info(admin_session_info)
1244 if domainguid is not None:
1245 domainguid_line = "objectGUID: %s\n-" % domainguid
1247 domainguid_line = ""
1249 descr = b64encode(get_domain_descriptor(domainsid))
1250 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1251 "DOMAINDN": names.domaindn,
1252 "DOMAINSID": str(domainsid),
1253 "DESCRIPTOR": descr,
1254 "DOMAINGUID": domainguid_line
1257 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1258 "DOMAINDN": names.domaindn,
1259 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1260 "NEXTRID": str(next_rid),
1261 "DEFAULTSITE": names.sitename,
1262 "CONFIGDN": names.configdn,
1263 "POLICYGUID": policyguid,
1264 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1265 "SAMBA_VERSION_STRING": version
1268 logger.info("Adding configuration container")
1269 descr = b64encode(get_config_descriptor(domainsid))
1270 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1271 "CONFIGDN": names.configdn,
1272 "DESCRIPTOR": descr,
1275 # Now register this container in the root of the forest
1276 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1277 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1280 # The LDIF here was created when the Schema object was constructed
1281 logger.info("Setting up sam.ldb schema")
1282 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1283 samdb.modify_ldif(schema.schema_dn_modify)
1284 samdb.write_prefixes_from_schema()
1285 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1286 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1287 {"SCHEMADN": names.schemadn})
1289 logger.info("Reopening sam.ldb with new schema")
1291 samdb.transaction_cancel()
1294 samdb.transaction_commit()
1296 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1297 credentials=provision_backend.credentials, lp=lp,
1298 global_schema=False, am_rodc=am_rodc)
1300 # Set the NTDS settings DN manually - in order to have it already around
1301 # before the provisioned tree exists and we connect
1302 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1305 samdb.transaction_start()
1307 samdb.invocation_id = invocationid
1309 logger.info("Setting up sam.ldb configuration data")
1310 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1311 "CONFIGDN": names.configdn,
1312 "NETBIOSNAME": names.netbiosname,
1313 "DEFAULTSITE": names.sitename,
1314 "DNSDOMAIN": names.dnsdomain,
1315 "DOMAIN": names.domain,
1316 "SCHEMADN": names.schemadn,
1317 "DOMAINDN": names.domaindn,
1318 "SERVERDN": names.serverdn,
1319 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1320 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1323 logger.info("Setting up display specifiers")
1324 display_specifiers_ldif = read_ms_ldif(
1325 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1326 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1327 {"CONFIGDN": names.configdn})
1328 check_all_substituted(display_specifiers_ldif)
1329 samdb.add_ldif(display_specifiers_ldif)
1331 logger.info("Adding users container")
1332 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1333 "DOMAINDN": names.domaindn})
1334 logger.info("Modifying users container")
1335 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1336 "DOMAINDN": names.domaindn})
1337 logger.info("Adding computers container")
1338 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1339 "DOMAINDN": names.domaindn})
1340 logger.info("Modifying computers container")
1341 setup_modify_ldif(samdb,
1342 setup_path("provision_computers_modify.ldif"), {
1343 "DOMAINDN": names.domaindn})
1344 logger.info("Setting up sam.ldb data")
1345 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1346 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1347 "DOMAINDN": names.domaindn,
1348 "NETBIOSNAME": names.netbiosname,
1349 "DEFAULTSITE": names.sitename,
1350 "CONFIGDN": names.configdn,
1351 "SERVERDN": names.serverdn,
1352 "RIDAVAILABLESTART": str(next_rid + 600),
1353 "POLICYGUID_DC": policyguid_dc
1356 setup_modify_ldif(samdb,
1357 setup_path("provision_basedn_references.ldif"), {
1358 "DOMAINDN": names.domaindn})
1360 setup_modify_ldif(samdb,
1361 setup_path("provision_configuration_references.ldif"), {
1362 "CONFIGDN": names.configdn,
1363 "SCHEMADN": names.schemadn})
1364 if fill == FILL_FULL:
1365 logger.info("Setting up sam.ldb users and groups")
1366 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1367 "DOMAINDN": names.domaindn,
1368 "DOMAINSID": str(domainsid),
1369 "CONFIGDN": names.configdn,
1370 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1371 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1374 logger.info("Setting up self join")
1375 setup_self_join(samdb, names=names, invocationid=invocationid,
1377 machinepass=machinepass,
1378 domainsid=domainsid,
1380 policyguid=policyguid,
1381 policyguid_dc=policyguid_dc,
1382 domainControllerFunctionality=domainControllerFunctionality,
1385 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1386 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1387 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1388 assert isinstance(names.ntdsguid, str)
1390 samdb.transaction_cancel()
1393 samdb.transaction_commit()
1398 FILL_NT4SYNC = "NT4SYNC"
1400 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1401 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)"
1404 def set_dir_acl(path, acl, lp, domsid):
1405 setntacl(lp, path, acl, domsid)
1406 for root, dirs, files in os.walk(path, topdown=False):
1408 setntacl(lp, os.path.join(root, name), acl, domsid)
1410 setntacl(lp, os.path.join(root, name), acl, domsid)
1413 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1414 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1417 :param sysvol: Physical path for the sysvol folder
1418 :param dnsdomain: The DNS name of the domain
1419 :param domainsid: The SID of the domain
1420 :param domaindn: The DN of the domain (ie. DC=...)
1421 :param samdb: An LDB object on the SAM db
1422 :param lp: an LP object
1425 # Set ACL for GPO root folder
1426 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1427 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1429 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1430 attrs=["cn", "nTSecurityDescriptor"],
1431 expression="", scope=ldb.SCOPE_ONELEVEL)
1434 acl = ndr_unpack(security.descriptor,
1435 str(policy["nTSecurityDescriptor"])).as_sddl()
1436 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1437 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1441 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1443 """Set the ACL for the sysvol share and the subfolders
1445 :param samdb: An LDB object on the SAM db
1446 :param netlogon: Physical path for the netlogon folder
1447 :param sysvol: Physical path for the sysvol folder
1448 :param gid: The GID of the "Domain adminstrators" group
1449 :param domainsid: The SID of the domain
1450 :param dnsdomain: The DNS name of the domain
1451 :param domaindn: The DN of the domain (ie. DC=...)
1455 os.chown(sysvol, -1, gid)
1461 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1462 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1463 for root, dirs, files in os.walk(sysvol, topdown=False):
1466 os.chown(os.path.join(root, name), -1, gid)
1467 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1470 os.chown(os.path.join(root, name), -1, gid)
1471 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1473 # Set acls on Policy folder and policies folders
1474 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1477 def interface_ips_v4(lp):
1478 '''return only IPv4 IPs'''
1479 ips = samba.interface_ips(lp, False)
1482 if i.find(':') == -1:
1486 def interface_ips_v6(lp, linklocal=False):
1487 '''return only IPv6 IPs'''
1488 ips = samba.interface_ips(lp, False)
1491 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1496 def provision(logger, session_info, credentials, smbconf=None,
1497 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1498 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1499 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1500 next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1501 domainguid=None, policyguid=None, policyguid_dc=None,
1502 invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1503 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1504 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1505 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1506 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1507 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1511 :note: caution, this wipes all existing data!
1514 if domainsid is None:
1515 domainsid = security.random_sid()
1517 domainsid = security.dom_sid(domainsid)
1519 # create/adapt the group policy GUIDs
1520 # Default GUID for default policy are described at
1521 # "How Core Group Policy Works"
1522 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1523 if policyguid is None:
1524 policyguid = DEFAULT_POLICY_GUID
1525 policyguid = policyguid.upper()
1526 if policyguid_dc is None:
1527 policyguid_dc = DEFAULT_DC_POLICY_GUID
1528 policyguid_dc = policyguid_dc.upper()
1530 if adminpass is None:
1531 adminpass = samba.generate_random_password(12, 32)
1532 if krbtgtpass is None:
1533 krbtgtpass = samba.generate_random_password(128, 255)
1534 if machinepass is None:
1535 machinepass = samba.generate_random_password(128, 255)
1537 dnspass = samba.generate_random_password(128, 255)
1538 if ldapadminpass is None:
1539 # Make a new, random password between Samba and it's LDAP server
1540 ldapadminpass=samba.generate_random_password(128, 255)
1542 if backend_type is None:
1543 backend_type = "ldb"
1545 sid_generator = "internal"
1546 if backend_type == "fedora-ds":
1547 sid_generator = "backend"
1549 root_uid = findnss_uid([root or "root"])
1550 nobody_uid = findnss_uid([nobody or "nobody"])
1551 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1553 wheel_gid = findnss_gid(["wheel", "adm"])
1555 wheel_gid = findnss_gid([wheel])
1557 bind_gid = findnss_gid(["bind", "named"])
1561 if targetdir is not None:
1562 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1563 elif smbconf is None:
1564 smbconf = samba.param.default_path()
1565 if not os.path.exists(os.path.dirname(smbconf)):
1566 os.makedirs(os.path.dirname(smbconf))
1568 # only install a new smb.conf if there isn't one there already
1569 if os.path.exists(smbconf):
1570 # if Samba Team members can't figure out the weird errors
1571 # loading an empty smb.conf gives, then we need to be smarter.
1572 # Pretend it just didn't exist --abartlet
1573 data = open(smbconf, 'r').read()
1574 data = data.lstrip()
1575 if data is None or data == "":
1576 make_smbconf(smbconf, hostname, domain, realm,
1577 serverrole, targetdir, sid_generator, useeadb,
1580 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1581 targetdir, sid_generator, useeadb, lp=lp)
1584 lp = samba.param.LoadParm()
1586 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1587 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1588 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1590 paths = provision_paths_from_lp(lp, names.dnsdomain)
1592 paths.bind_gid = bind_gid
1595 logger.info("Looking up IPv4 addresses")
1596 hostips = interface_ips_v4(lp)
1597 if len(hostips) > 0:
1599 if len(hostips) > 1:
1600 logger.warning("More than one IPv4 address found. Using %s",
1602 if hostip == "127.0.0.1":
1605 logger.warning("No IPv4 address will be assigned")
1608 logger.info("Looking up IPv6 addresses")
1609 hostips = interface_ips_v6(lp, linklocal=False)
1611 hostip6 = hostips[0]
1612 if len(hostips) > 1:
1613 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1615 logger.warning("No IPv6 address will be assigned")
1617 if serverrole is None:
1618 serverrole = lp.get("server role")
1620 assert serverrole in ("domain controller", "member server", "standalone")
1621 if invocationid is None:
1622 invocationid = str(uuid.uuid4())
1624 if not os.path.exists(paths.private_dir):
1625 os.mkdir(paths.private_dir)
1626 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1627 os.mkdir(os.path.join(paths.private_dir, "tls"))
1629 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1631 schema = Schema(domainsid, invocationid=invocationid,
1632 schemadn=names.schemadn)
1634 if backend_type == "ldb":
1635 provision_backend = LDBBackend(backend_type, paths=paths,
1636 lp=lp, credentials=credentials,
1637 names=names, logger=logger)
1638 elif backend_type == "existing":
1639 provision_backend = ExistingBackend(backend_type, paths=paths,
1640 lp=lp, credentials=credentials,
1641 names=names, logger=logger,
1642 ldap_backend_forced_uri=ldap_backend_forced_uri)
1643 elif backend_type == "fedora-ds":
1644 provision_backend = FDSBackend(backend_type, paths=paths,
1645 lp=lp, credentials=credentials,
1646 names=names, logger=logger, domainsid=domainsid,
1647 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1648 slapd_path=slapd_path,
1649 ldap_backend_extra_port=ldap_backend_extra_port,
1650 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1651 setup_ds_path=setup_ds_path,
1652 ldap_backend_forced_uri=ldap_backend_forced_uri)
1653 elif backend_type == "openldap":
1654 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1655 lp=lp, credentials=credentials,
1656 names=names, logger=logger, domainsid=domainsid,
1657 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1658 slapd_path=slapd_path,
1659 ldap_backend_extra_port=ldap_backend_extra_port,
1660 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1662 ldap_backend_forced_uri=ldap_backend_forced_uri)
1664 raise ValueError("Unknown LDAP backend type selected")
1666 provision_backend.init()
1667 provision_backend.start()
1669 # only install a new shares config db if there is none
1670 if not os.path.exists(paths.shareconf):
1671 logger.info("Setting up share.ldb")
1672 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1674 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1676 logger.info("Setting up secrets.ldb")
1677 secrets_ldb = setup_secretsdb(paths,
1678 session_info=session_info,
1679 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1682 logger.info("Setting up the registry")
1683 setup_registry(paths.hklm, session_info,
1686 logger.info("Setting up the privileges database")
1687 setup_privileges(paths.privilege, session_info, lp=lp)
1689 logger.info("Setting up idmap db")
1690 idmap = setup_idmapdb(paths.idmapdb,
1691 session_info=session_info, lp=lp)
1693 logger.info("Setting up SAM db")
1694 samdb = setup_samdb(paths.samdb, session_info,
1695 provision_backend, lp, names, logger=logger,
1696 domainsid=domainsid, schema=schema, domainguid=domainguid,
1697 policyguid=policyguid, policyguid_dc=policyguid_dc,
1698 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1699 invocationid=invocationid, machinepass=machinepass,
1700 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1701 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1704 if serverrole == "domain controller":
1705 if paths.netlogon is None:
1706 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1707 logger.info("Please either remove %s or see the template at %s" %
1708 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1709 assert paths.netlogon is not None
1711 if paths.sysvol is None:
1712 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1713 " are configuring a DC.")
1714 logger.info("Please either remove %s or see the template at %s" %
1715 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1716 assert paths.sysvol is not None
1718 if not os.path.isdir(paths.netlogon):
1719 os.makedirs(paths.netlogon, 0755)
1721 if samdb_fill == FILL_FULL:
1722 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1723 root_uid=root_uid, nobody_uid=nobody_uid,
1724 users_gid=users_gid, wheel_gid=wheel_gid)
1726 if serverrole == "domain controller":
1727 # Set up group policies (domain policy and domain controller
1729 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1731 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1732 domainsid, names.dnsdomain, names.domaindn, lp)
1734 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1735 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1737 secretsdb_self_join(secrets_ldb, domain=names.domain,
1738 realm=names.realm, dnsdomain=names.dnsdomain,
1739 netbiosname=names.netbiosname, domainsid=domainsid,
1740 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1742 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1743 # In future, this might be determined from some configuration
1744 kerberos_enctypes = str(ENC_ALL_TYPES)
1747 msg = ldb.Message(ldb.Dn(samdb,
1748 samdb.searchone("distinguishedName",
1749 expression="samAccountName=%s$" % names.netbiosname,
1750 scope=ldb.SCOPE_SUBTREE)))
1751 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1752 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1753 name="msDS-SupportedEncryptionTypes")
1755 except ldb.LdbError, (enum, estr):
1756 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1757 # It might be that this attribute does not exist in this schema
1760 if serverrole == "domain controller":
1761 secretsdb_setup_dns(secrets_ldb, names,
1762 paths.private_dir, realm=names.realm,
1763 dnsdomain=names.dnsdomain,
1764 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1766 setup_ad_dns(samdb, names=names, hostip=hostip, hostip6=hostip6)
1768 domainguid = samdb.searchone(basedn=domaindn,
1769 attribute="objectGUID")
1770 assert isinstance(domainguid, str)
1772 # Only make a zone file on the first DC, it should be
1773 # replicated with DNS replication
1774 create_zone_file(lp, logger, paths, targetdir,
1775 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1776 hostname=names.hostname, realm=names.realm,
1777 domainguid=domainguid, ntdsguid=names.ntdsguid)
1779 create_named_conf(paths, realm=names.realm,
1780 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1782 create_named_txt(paths.namedtxt,
1783 realm=names.realm, dnsdomain=names.dnsdomain,
1784 dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1785 private_dir=paths.private_dir,
1786 keytab_name=paths.dns_keytab)
1787 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1788 logger.info("and %s for further documentation required for secure DNS "
1789 "updates", paths.namedtxt)
1791 lastProvisionUSNs = get_last_provision_usn(samdb)
1792 maxUSN = get_max_usn(samdb, str(names.rootdn))
1793 if lastProvisionUSNs is not None:
1794 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1796 set_provision_usn(samdb, 0, maxUSN, invocationid)
1798 create_krb5_conf(paths.krb5conf,
1799 dnsdomain=names.dnsdomain, hostname=names.hostname,
1801 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1802 "generated at %s", paths.krb5conf)
1804 if serverrole == "domain controller":
1805 create_dns_update_list(lp, logger, paths)
1807 provision_backend.post_setup()
1808 provision_backend.shutdown()
1810 create_phpldapadmin_config(paths.phpldapadminconfig,
1813 secrets_ldb.transaction_cancel()
1816 # Now commit the secrets.ldb to disk
1817 secrets_ldb.transaction_commit()
1819 # the commit creates the dns.keytab, now chown it
1820 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1821 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1823 os.chmod(dns_keytab_path, 0640)
1824 os.chown(dns_keytab_path, -1, paths.bind_gid)
1826 if not os.environ.has_key('SAMBA_SELFTEST'):
1827 logger.info("Failed to chown %s to bind gid %u",
1828 dns_keytab_path, paths.bind_gid)
1831 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1832 paths.phpldapadminconfig)
1834 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1835 logger.info("Server Role: %s" % serverrole)
1836 logger.info("Hostname: %s" % names.hostname)
1837 logger.info("NetBIOS Domain: %s" % names.domain)
1838 logger.info("DNS Domain: %s" % names.dnsdomain)
1839 logger.info("DOMAIN SID: %s" % str(domainsid))
1840 if samdb_fill == FILL_FULL:
1841 logger.info("Admin password: %s" % adminpass)
1842 if provision_backend.type is not "ldb":
1843 if provision_backend.credentials.get_bind_dn() is not None:
1844 logger.info("LDAP Backend Admin DN: %s" %
1845 provision_backend.credentials.get_bind_dn())
1847 logger.info("LDAP Admin User: %s" %
1848 provision_backend.credentials.get_username())
1850 logger.info("LDAP Admin Password: %s" %
1851 provision_backend.credentials.get_password())
1853 if provision_backend.slapd_command_escaped is not None:
1854 # now display slapd_command_file.txt to show how slapd must be
1856 logger.info("Use later the following commandline to start slapd, then Samba:")
1857 logger.info(provision_backend.slapd_command_escaped)
1858 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1859 provision_backend.ldapdir)
1861 result = ProvisionResult()
1862 result.domaindn = domaindn
1863 result.paths = paths
1865 result.samdb = samdb
1869 def provision_become_dc(smbconf=None, targetdir=None,
1870 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1871 serverdn=None, domain=None, hostname=None, domainsid=None,
1872 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1873 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1874 root=None, nobody=None, users=None, wheel=None, backup=None,
1875 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1876 sitename=None, debuglevel=1):
1878 logger = logging.getLogger("provision")
1879 samba.set_debug_level(debuglevel)
1881 res = provision(logger, system_session(), None,
1882 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1883 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1884 configdn=configdn, serverdn=serverdn, domain=domain,
1885 hostname=hostname, hostip=None, domainsid=domainsid,
1886 machinepass=machinepass, serverrole="domain controller",
1888 res.lp.set("debuglevel", str(debuglevel))
1892 def create_phpldapadmin_config(path, ldapi_uri):
1893 """Create a PHP LDAP admin configuration file.
1895 :param path: Path to write the configuration to.
1897 setup_file(setup_path("phpldapadmin-config.php"), path,
1898 {"S4_LDAPI_URI": ldapi_uri})
1901 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1902 hostip, hostip6, hostname, realm, domainguid,
1904 """Write out a DNS zone file, from the info in the current database.
1906 :param paths: paths object
1907 :param dnsdomain: DNS Domain name
1908 :param domaindn: DN of the Domain
1909 :param hostip: Local IPv4 IP
1910 :param hostip6: Local IPv6 IP
1911 :param hostname: Local hostname
1912 :param realm: Realm name
1913 :param domainguid: GUID of the domain.
1914 :param ntdsguid: GUID of the hosts nTDSDSA record.
1916 assert isinstance(domainguid, str)
1918 if hostip6 is not None:
1919 hostip6_base_line = " IN AAAA " + hostip6
1920 hostip6_host_line = hostname + " IN AAAA " + hostip6
1921 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1923 hostip6_base_line = ""
1924 hostip6_host_line = ""
1925 gc_msdcs_ip6_line = ""
1927 if hostip is not None:
1928 hostip_base_line = " IN A " + hostip
1929 hostip_host_line = hostname + " IN A " + hostip
1930 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1932 hostip_base_line = ""
1933 hostip_host_line = ""
1934 gc_msdcs_ip_line = ""
1936 dns_dir = os.path.dirname(paths.dns)
1939 shutil.rmtree(dns_dir, True)
1943 os.mkdir(dns_dir, 0775)
1945 # we need to freeze the zone while we update the contents
1946 if targetdir is None:
1947 rndc = ' '.join(lp.get("rndc command"))
1948 os.system(rndc + " freeze " + lp.get("realm"))
1950 setup_file(setup_path("provision.zone"), paths.dns, {
1951 "HOSTNAME": hostname,
1952 "DNSDOMAIN": dnsdomain,
1954 "HOSTIP_BASE_LINE": hostip_base_line,
1955 "HOSTIP_HOST_LINE": hostip_host_line,
1956 "DOMAINGUID": domainguid,
1957 "DATESTRING": time.strftime("%Y%m%d%H"),
1958 "DEFAULTSITE": DEFAULTSITE,
1959 "NTDSGUID": ntdsguid,
1960 "HOSTIP6_BASE_LINE": hostip6_base_line,
1961 "HOSTIP6_HOST_LINE": hostip6_host_line,
1962 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1963 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1966 # note that we use no variable substitution on this file
1967 # the substitution is done at runtime by samba_dnsupdate
1968 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1970 # and the SPN update list
1971 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1973 if paths.bind_gid is not None:
1975 os.chown(dns_dir, -1, paths.bind_gid)
1976 os.chown(paths.dns, -1, paths.bind_gid)
1977 # chmod needed to cope with umask
1978 os.chmod(dns_dir, 0775)
1979 os.chmod(paths.dns, 0664)
1981 if not os.environ.has_key('SAMBA_SELFTEST'):
1982 logger.error("Failed to chown %s to bind gid %u" % (
1983 dns_dir, paths.bind_gid))
1985 if targetdir is None:
1986 os.system(rndc + " unfreeze " + lp.get("realm"))
1989 def create_dns_update_list(lp, logger, paths):
1990 """Write out a dns_update_list file"""
1991 # note that we use no variable substitution on this file
1992 # the substitution is done at runtime by samba_dnsupdate
1993 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1994 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1997 def create_named_conf(paths, realm, dnsdomain,
1999 """Write out a file containing zone statements suitable for inclusion in a
2000 named.conf file (including GSS-TSIG configuration).
2002 :param paths: all paths
2003 :param realm: Realm name
2004 :param dnsdomain: DNS Domain name
2005 :param private_dir: Path to private directory
2006 :param keytab_name: File name of DNS keytab file
2009 setup_file(setup_path("named.conf"), paths.namedconf, {
2010 "DNSDOMAIN": dnsdomain,
2012 "ZONE_FILE": paths.dns,
2013 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2014 "NAMED_CONF": paths.namedconf,
2015 "NAMED_CONF_UPDATE": paths.namedconf_update
2018 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2021 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2023 """Write out a file containing zone statements suitable for inclusion in a
2024 named.conf file (including GSS-TSIG configuration).
2026 :param path: Path of the new named.conf file.
2027 :param realm: Realm name
2028 :param dnsdomain: DNS Domain name
2029 :param private_dir: Path to private directory
2030 :param keytab_name: File name of DNS keytab file
2032 setup_file(setup_path("named.txt"), path, {
2033 "DNSDOMAIN": dnsdomain,
2034 "DNSNAME" : dnsname,
2036 "DNS_KEYTAB": keytab_name,
2037 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2038 "PRIVATE_DIR": private_dir
2042 def create_krb5_conf(path, dnsdomain, hostname, realm):
2043 """Write out a file containing zone statements suitable for inclusion in a
2044 named.conf file (including GSS-TSIG configuration).
2046 :param path: Path of the new named.conf file.
2047 :param dnsdomain: DNS Domain name
2048 :param hostname: Local hostname
2049 :param realm: Realm name
2051 setup_file(setup_path("krb5.conf"), path, {
2052 "DNSDOMAIN": dnsdomain,
2053 "HOSTNAME": hostname,
2058 class ProvisioningError(Exception):
2059 """A generic provision error."""
2061 def __init__(self, value):
2065 return "ProvisioningError: " + self.value
2068 class InvalidNetbiosName(Exception):
2069 """A specified name was not a valid NetBIOS name."""
2070 def __init__(self, name):
2071 super(InvalidNetbiosName, self).__init__(
2072 "The name '%r' is not a valid NetBIOS name" % name)