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 (
79 from samba.schema import Schema
80 from samba.samdb import SamDB
82 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
83 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
84 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
85 DEFAULTSITE = "Default-First-Site-Name"
86 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
90 """Return an absolute path to the provision tempate file specified by file"""
91 return os.path.join(samba.param.setup_dir(), file)
93 # Descriptors of naming contexts and other important objects
95 # "get_schema_descriptor" is located in "schema.py"
97 def get_config_descriptor(domain_sid):
98 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
99 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
100 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
101 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
102 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
103 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
104 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
105 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
106 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
107 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
108 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
109 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
110 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
111 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
112 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
113 sec = security.descriptor.from_sddl(sddl, domain_sid)
117 def get_domain_descriptor(domain_sid):
118 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
119 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
120 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
121 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
122 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
129 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
130 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
131 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
132 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
133 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
134 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
135 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
136 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
137 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
138 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
140 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
141 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
142 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
143 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
144 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
145 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
146 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
147 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
148 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
149 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
150 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
151 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
152 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
153 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
154 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
155 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
156 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
159 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
161 "(A;;RPLCLORC;;;ED)" \
162 "(A;;RPLCLORC;;;AU)" \
163 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
164 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
165 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
166 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
167 sec = security.descriptor.from_sddl(sddl, domain_sid)
171 class ProvisionPaths(object):
174 self.shareconf = None
185 self.dns_keytab = None
188 self.private_dir = None
191 class ProvisionNames(object):
198 self.ldapmanagerdn = None
199 self.dnsdomain = None
201 self.netbiosname = None
207 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
208 """Get key provision parameters (realm, domain, ...) from a given provision
210 :param samdb: An LDB object connected to the sam.ldb file
211 :param secretsdb: An LDB object connected to the secrets.ldb file
212 :param idmapdb: An LDB object connected to the idmap.ldb file
213 :param paths: A list of path to provision object
214 :param smbconf: Path to the smb.conf file
215 :param lp: A LoadParm object
216 :return: A list of key provision parameters
218 names = ProvisionNames()
219 names.adminpass = None
221 # NT domain, kerberos realm, root dn, domain dn, domain dns name
222 names.domain = string.upper(lp.get("workgroup"))
223 names.realm = lp.get("realm")
224 basedn = "DC=" + names.realm.replace(".",",DC=")
225 names.dnsdomain = names.realm.lower()
226 names.realm = string.upper(names.realm)
228 # Get the netbiosname first (could be obtained from smb.conf in theory)
229 res = secretsdb.search(expression="(flatname=%s)" %
230 names.domain,base="CN=Primary Domains",
231 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
232 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
234 names.smbconf = smbconf
236 # That's a bit simplistic but it's ok as long as we have only 3
238 current = samdb.search(expression="(objectClass=*)",
239 base="", scope=ldb.SCOPE_BASE,
240 attrs=["defaultNamingContext", "schemaNamingContext",
241 "configurationNamingContext","rootDomainNamingContext"])
243 names.configdn = current[0]["configurationNamingContext"]
244 configdn = str(names.configdn)
245 names.schemadn = current[0]["schemaNamingContext"]
246 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
247 current[0]["defaultNamingContext"][0]))):
248 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
249 "is not the same ..." % (paths.samdb,
250 str(current[0]["defaultNamingContext"][0]),
251 paths.smbconf, basedn)))
253 names.domaindn=current[0]["defaultNamingContext"]
254 names.rootdn=current[0]["rootDomainNamingContext"]
256 res3 = samdb.search(expression="(objectClass=site)",
257 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
258 names.sitename = str(res3[0]["cn"])
260 # dns hostname and server dn
261 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
262 base="OU=Domain Controllers,%s" % basedn,
263 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
264 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
266 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
267 attrs=[], base=configdn)
268 names.serverdn = server_res[0].dn
270 # invocation id/objectguid
271 res5 = samdb.search(expression="(objectClass=*)",
272 base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
273 attrs=["invocationID", "objectGUID"])
274 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
275 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
278 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
279 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
280 "objectSid","msDS-Behavior-Version" ])
281 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
282 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
283 if res6[0].get("msDS-Behavior-Version") is None or \
284 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
285 names.domainlevel = DS_DOMAIN_FUNCTION_2000
287 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
290 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
291 base="CN=Policies,CN=System," + basedn,
292 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
293 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
295 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
297 base="CN=Policies,CN=System," + basedn,
298 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
300 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
302 names.policyid_dc = None
303 res9 = idmapdb.search(expression="(cn=%s)" %
304 (security.SID_BUILTIN_ADMINISTRATORS),
307 names.wheel_gid = res9[0]["xidNumber"]
309 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
312 def update_provision_usn(samdb, low, high, id, replace=False):
313 """Update the field provisionUSN in sam.ldb
315 This field is used to track range of USN modified by provision and
317 This value is used afterward by next provision to figure out if
318 the field have been modified since last provision.
320 :param samdb: An LDB object connect to sam.ldb
321 :param low: The lowest USN modified by this upgrade
322 :param high: The highest USN modified by this upgrade
323 :param id: The invocation id of the samba's dc
324 :param replace: A boolean indicating if the range should replace any
325 existing one or appended (default)
330 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" %
331 LAST_PROVISION_USN_ATTRIBUTE, base="",
332 scope=ldb.SCOPE_SUBTREE,
333 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
334 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
335 if not re.search(';', e):
336 e = "%s;%s" % (e, id)
339 tab.append("%s-%s;%s" % (low, high, id))
340 delta = ldb.Message()
341 delta.dn = ldb.Dn(samdb, "@PROVISION")
342 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
343 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
344 entry = samdb.search(expression="(&(dn=@PROVISION)(provisionnerID=*))",
345 base="", scope=ldb.SCOPE_SUBTREE,
346 attrs=["provisionnerID"])
347 if len(entry) == 0 or len(entry[0]) == 0:
348 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
352 def set_provision_usn(samdb, low, high, id):
353 """Set the field provisionUSN in sam.ldb
354 This field is used to track range of USN modified by provision and
356 This value is used afterward by next provision to figure out if
357 the field have been modified since last provision.
359 :param samdb: An LDB object connect to sam.ldb
360 :param low: The lowest USN modified by this upgrade
361 :param high: The highest USN modified by this upgrade
362 :param id: The invocationId of the provision"""
365 tab.append("%s-%s;%s" % (low, high, id))
367 delta = ldb.Message()
368 delta.dn = ldb.Dn(samdb, "@PROVISION")
369 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
370 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
374 def get_max_usn(samdb,basedn):
375 """ This function return the biggest USN present in the provision
377 :param samdb: A LDB object pointing to the sam.ldb
378 :param basedn: A string containing the base DN of the provision
380 :return: The biggest USN in the provision"""
382 res = samdb.search(expression="objectClass=*",base=basedn,
383 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
384 controls=["search_options:1:2",
385 "server_sort:1:1:uSNChanged",
386 "paged_results:1:1"])
387 return res[0]["uSNChanged"]
390 def get_last_provision_usn(sam):
391 """Get USNs ranges modified by a provision or an upgradeprovision
393 :param sam: An LDB object pointing to the sam.ldb
394 :return: a dictionnary which keys are invocation id and values are an array
395 of integer representing the different ranges
397 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" %
398 LAST_PROVISION_USN_ATTRIBUTE,
399 base="", scope=ldb.SCOPE_SUBTREE,
400 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
405 if entry[0].get("provisionnerID"):
406 for e in entry[0]["provisionnerID"]:
408 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
409 tab1 = str(r).split(';')
414 if (len(myids) > 0 and id not in myids):
416 tab2 = p.split(tab1[0])
417 if range.get(id) == None:
419 range[id].append(tab2[0])
420 range[id].append(tab2[1])
426 class ProvisionResult(object):
435 def check_install(lp, session_info, credentials):
436 """Check whether the current install seems ok.
438 :param lp: Loadparm context
439 :param session_info: Session information
440 :param credentials: Credentials
442 if lp.get("realm") == "":
443 raise Exception("Realm empty")
444 samdb = Ldb(lp.samdb_url(), session_info=session_info,
445 credentials=credentials, lp=lp)
446 if len(samdb.search("(cn=Administrator)")) != 1:
447 raise ProvisioningError("No administrator account found")
450 def findnss(nssfn, names):
451 """Find a user or group from a list of possibilities.
453 :param nssfn: NSS Function to try (should raise KeyError if not found)
454 :param names: Names to check.
455 :return: Value return by first names list.
462 raise KeyError("Unable to find user/group in %r" % names)
465 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
466 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
469 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
470 """Setup a ldb in the private dir.
472 :param ldb: LDB file to import data into
473 :param ldif_path: Path of the LDIF file to load
474 :param subst_vars: Optional variables to subsitute in LDIF.
475 :param nocontrols: Optional list of controls, can be None for no controls
477 assert isinstance(ldif_path, str)
478 data = read_and_sub_file(ldif_path, subst_vars)
479 ldb.add_ldif(data, controls)
482 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
483 """Modify a ldb in the private dir.
485 :param ldb: LDB object.
486 :param ldif_path: LDIF file path.
487 :param subst_vars: Optional dictionary with substitution variables.
489 data = read_and_sub_file(ldif_path, subst_vars)
490 ldb.modify_ldif(data, controls)
493 def setup_ldb(ldb, ldif_path, subst_vars):
494 """Import a LDIF a file into a LDB handle, optionally substituting
497 :note: Either all LDIF data will be added or none (using transactions).
499 :param ldb: LDB file to import into.
500 :param ldif_path: Path to the LDIF file.
501 :param subst_vars: Dictionary with substitution variables.
503 assert ldb is not None
504 ldb.transaction_start()
506 setup_add_ldif(ldb, ldif_path, subst_vars)
508 ldb.transaction_cancel()
511 ldb.transaction_commit()
514 def provision_paths_from_lp(lp, dnsdomain):
515 """Set the default paths for provisioning.
517 :param lp: Loadparm context.
518 :param dnsdomain: DNS Domain name
520 paths = ProvisionPaths()
521 paths.private_dir = lp.get("private dir")
523 # This is stored without path prefix for the "privateKeytab" attribute in
524 # "secrets_dns.ldif".
525 paths.dns_keytab = "dns.keytab"
526 paths.keytab = "secrets.keytab"
528 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
529 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
530 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
531 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
532 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
533 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
534 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
535 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
536 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
537 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
538 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
539 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
540 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
541 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
542 paths.phpldapadminconfig = os.path.join(paths.private_dir,
543 "phpldapadmin-config.php")
544 paths.hklm = "hklm.ldb"
545 paths.hkcr = "hkcr.ldb"
546 paths.hkcu = "hkcu.ldb"
547 paths.hku = "hku.ldb"
548 paths.hkpd = "hkpd.ldb"
549 paths.hkpt = "hkpt.ldb"
550 paths.sysvol = lp.get("path", "sysvol")
551 paths.netlogon = lp.get("path", "netlogon")
552 paths.smbconf = lp.configfile
556 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
557 serverrole=None, rootdn=None, domaindn=None, configdn=None,
558 schemadn=None, serverdn=None, sitename=None):
559 """Guess configuration settings to use."""
562 hostname = socket.gethostname().split(".")[0]
564 netbiosname = lp.get("netbios name")
565 if netbiosname is None:
566 netbiosname = hostname
567 # remove forbidden chars
569 for x in netbiosname:
570 if x.isalnum() or x in VALID_NETBIOS_CHARS:
571 newnbname = "%s%c" % (newnbname, x)
572 # force the length to be <16
573 netbiosname = newnbname[0:15]
574 assert netbiosname is not None
575 netbiosname = netbiosname.upper()
576 if not valid_netbios_name(netbiosname):
577 raise InvalidNetbiosName(netbiosname)
579 if dnsdomain is None:
580 dnsdomain = lp.get("realm")
581 if dnsdomain is None or dnsdomain == "":
582 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
584 dnsdomain = dnsdomain.lower()
586 if serverrole is None:
587 serverrole = lp.get("server role")
588 if serverrole is None:
589 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
591 serverrole = serverrole.lower()
593 realm = dnsdomain.upper()
595 if lp.get("realm") == "":
596 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
598 if lp.get("realm").upper() != realm:
599 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))
601 if lp.get("server role").lower() != serverrole:
602 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))
604 if serverrole == "domain controller":
606 # This will, for better or worse, default to 'WORKGROUP'
607 domain = lp.get("workgroup")
608 domain = domain.upper()
610 if lp.get("workgroup").upper() != domain:
611 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))
614 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
616 if domain == netbiosname:
617 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
621 domaindn = "DC=" + netbiosname
623 if not valid_netbios_name(domain):
624 raise InvalidNetbiosName(domain)
626 if hostname.upper() == realm:
627 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
628 if netbiosname.upper() == realm:
629 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
631 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
637 configdn = "CN=Configuration," + rootdn
639 schemadn = "CN=Schema," + configdn
644 names = ProvisionNames()
645 names.rootdn = rootdn
646 names.domaindn = domaindn
647 names.configdn = configdn
648 names.schemadn = schemadn
649 names.ldapmanagerdn = "CN=Manager," + rootdn
650 names.dnsdomain = dnsdomain
651 names.domain = domain
653 names.netbiosname = netbiosname
654 names.hostname = hostname
655 names.sitename = sitename
656 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
657 netbiosname, sitename, configdn)
662 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
663 targetdir, sid_generator="internal", eadb=False, lp=None):
664 """Create a new smb.conf file based on a couple of basic settings.
666 assert smbconf is not None
668 hostname = socket.gethostname().split(".")[0]
669 netbiosname = hostname.upper()
670 # remove forbidden chars
672 for x in netbiosname:
673 if x.isalnum() or x in VALID_NETBIOS_CHARS:
674 newnbname = "%s%c" % (newnbname, x)
675 #force the length to be <16
676 netbiosname = newnbname[0:15]
678 netbiosname = hostname.upper()
680 if serverrole is None:
681 serverrole = "standalone"
683 assert serverrole in ("domain controller", "member server", "standalone")
684 if serverrole == "domain controller":
686 elif serverrole == "member server":
687 smbconfsuffix = "member"
688 elif serverrole == "standalone":
689 smbconfsuffix = "standalone"
691 if sid_generator is None:
692 sid_generator = "internal"
694 assert domain is not None
695 domain = domain.upper()
697 assert realm is not None
698 realm = realm.upper()
701 lp = samba.param.LoadParm()
702 #Load non-existant file
703 if os.path.exists(smbconf):
705 if eadb and not lp.get("posix:eadb"):
706 if targetdir is not None:
707 privdir = os.path.join(targetdir, "private")
709 privdir = lp.get("private dir")
710 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
712 if targetdir is not None:
713 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
714 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
716 lp.set("lock dir", os.path.abspath(targetdir))
721 sysvol = os.path.join(lp.get("lock dir"), "sysvol")
722 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
724 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
726 "NETBIOS_NAME": netbiosname,
729 "SERVERROLE": serverrole,
730 "NETLOGONPATH": netlogon,
731 "SYSVOLPATH": sysvol,
732 "PRIVATEDIR_LINE": privatedir_line,
733 "LOCKDIR_LINE": lockdir_line
736 # reload the smb.conf
739 # and dump it without any values that are the default
740 # this ensures that any smb.conf parameters that were set
741 # on the provision/join command line are set in the resulting smb.conf
742 f = open(smbconf, mode='w')
748 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
749 users_gid, wheel_gid):
750 """setup reasonable name mappings for sam names to unix names.
752 :param samdb: SamDB object.
753 :param idmap: IDmap db object.
754 :param sid: The domain sid.
755 :param domaindn: The domain DN.
756 :param root_uid: uid of the UNIX root user.
757 :param nobody_uid: uid of the UNIX nobody user.
758 :param users_gid: gid of the UNIX users group.
759 :param wheel_gid: gid of the UNIX wheel group.
761 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
762 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
764 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
765 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
768 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
769 provision_backend, names, schema, serverrole,
771 """Setup the partitions for the SAM database.
773 Alternatively, provision() may call this, and then populate the database.
775 :note: This will wipe the Sam Database!
777 :note: This function always removes the local SAM LDB file. The erase
778 parameter controls whether to erase the existing data, which
779 may not be stored locally but in LDAP.
782 assert session_info is not None
784 # We use options=["modules:"] to stop the modules loading - we
785 # just want to wipe and re-initialise the database, not start it up
788 os.unlink(samdb_path)
792 samdb = Ldb(url=samdb_path, session_info=session_info,
793 lp=lp, options=["modules:"])
795 ldap_backend_line = "# No LDAP backend"
796 if provision_backend.type is not "ldb":
797 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
799 samdb.transaction_start()
801 logger.info("Setting up sam.ldb partitions and settings")
802 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
803 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
804 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
805 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
806 "LDAP_BACKEND_LINE": ldap_backend_line,
810 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
811 "BACKEND_TYPE": provision_backend.type,
812 "SERVER_ROLE": serverrole
815 logger.info("Setting up sam.ldb rootDSE")
816 setup_samdb_rootdse(samdb, names)
818 samdb.transaction_cancel()
821 samdb.transaction_commit()
824 def secretsdb_self_join(secretsdb, domain,
825 netbiosname, machinepass, domainsid=None,
826 realm=None, dnsdomain=None,
828 key_version_number=1,
829 secure_channel_type=SEC_CHAN_WKSTA):
830 """Add domain join-specific bits to a secrets database.
832 :param secretsdb: Ldb Handle to the secrets database
833 :param machinepass: Machine password
835 attrs = ["whenChanged",
842 if realm is not None:
843 if dnsdomain is None:
844 dnsdomain = realm.lower()
845 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
848 shortname = netbiosname.lower()
850 # We don't need to set msg["flatname"] here, because rdn_name will handle
851 # it, and it causes problems for modifies anyway
852 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
853 msg["secureChannelType"] = [str(secure_channel_type)]
854 msg["objectClass"] = ["top", "primaryDomain"]
855 if dnsname is not None:
856 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
857 msg["realm"] = [realm]
858 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
859 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
860 msg["privateKeytab"] = ["secrets.keytab"]
862 msg["secret"] = [machinepass]
863 msg["samAccountName"] = ["%s$" % netbiosname]
864 msg["secureChannelType"] = [str(secure_channel_type)]
865 if domainsid is not None:
866 msg["objectSid"] = [ndr_pack(domainsid)]
868 # This complex expression tries to ensure that we don't have more
869 # than one record for this SID, realm or netbios domain at a time,
870 # but we don't delete the old record that we are about to modify,
871 # because that would delete the keytab and previous password.
872 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
873 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
874 scope=ldb.SCOPE_ONELEVEL)
877 secretsdb.delete(del_msg.dn)
879 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
882 msg["priorSecret"] = [res[0]["secret"][0]]
883 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
886 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
891 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
897 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
898 secretsdb.modify(msg)
899 secretsdb.rename(res[0].dn, msg.dn)
901 spn = [ 'HOST/%s' % shortname ]
902 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
903 # we are a domain controller then we add servicePrincipalName
904 # entries for the keytab code to update.
905 spn.extend([ 'HOST/%s' % dnsname ])
906 msg["servicePrincipalName"] = spn
911 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
912 dnsdomain, dns_keytab_path, dnspass):
913 """Add DNS specific bits to a secrets database.
915 :param secretsdb: Ldb Handle to the secrets database
916 :param machinepass: Machine password
919 os.unlink(os.path.join(private_dir, dns_keytab_path))
923 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
925 "DNSDOMAIN": dnsdomain,
926 "DNS_KEYTAB": dns_keytab_path,
927 "DNSPASS_B64": b64encode(dnspass),
928 "HOSTNAME": names.hostname,
929 "DNSNAME" : '%s.%s' % (
930 names.netbiosname.lower(), names.dnsdomain.lower())
934 def setup_secretsdb(paths, session_info, backend_credentials, lp):
935 """Setup the secrets database.
937 :note: This function does not handle exceptions and transaction on purpose,
938 it's up to the caller to do this job.
940 :param path: Path to the secrets database.
941 :param session_info: Session info.
942 :param credentials: Credentials
943 :param lp: Loadparm context
944 :return: LDB handle for the created secrets database
946 if os.path.exists(paths.secrets):
947 os.unlink(paths.secrets)
949 keytab_path = os.path.join(paths.private_dir, paths.keytab)
950 if os.path.exists(keytab_path):
951 os.unlink(keytab_path)
953 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
954 if os.path.exists(dns_keytab_path):
955 os.unlink(dns_keytab_path)
959 secrets_ldb = Ldb(path, session_info=session_info,
962 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
963 secrets_ldb = Ldb(path, session_info=session_info,
965 secrets_ldb.transaction_start()
967 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
969 if (backend_credentials is not None and
970 backend_credentials.authentication_requested()):
971 if backend_credentials.get_bind_dn() is not None:
972 setup_add_ldif(secrets_ldb,
973 setup_path("secrets_simple_ldap.ldif"), {
974 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
975 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
978 setup_add_ldif(secrets_ldb,
979 setup_path("secrets_sasl_ldap.ldif"), {
980 "LDAPADMINUSER": backend_credentials.get_username(),
981 "LDAPADMINREALM": backend_credentials.get_realm(),
982 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
987 secrets_ldb.transaction_cancel()
991 def setup_privileges(path, session_info, lp):
992 """Setup the privileges database.
994 :param path: Path to the privileges database.
995 :param session_info: Session info.
996 :param credentials: Credentials
997 :param lp: Loadparm context
998 :return: LDB handle for the created secrets database
1000 if os.path.exists(path):
1002 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1003 privilege_ldb.erase()
1004 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1007 def setup_registry(path, session_info, lp):
1008 """Setup the registry.
1010 :param path: Path to the registry database
1011 :param session_info: Session information
1012 :param credentials: Credentials
1013 :param lp: Loadparm context
1015 reg = samba.registry.Registry()
1016 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1017 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1018 provision_reg = setup_path("provision.reg")
1019 assert os.path.exists(provision_reg)
1020 reg.diff_apply(provision_reg)
1023 def setup_idmapdb(path, session_info, lp):
1024 """Setup the idmap database.
1026 :param path: path to the idmap database
1027 :param session_info: Session information
1028 :param credentials: Credentials
1029 :param lp: Loadparm context
1031 if os.path.exists(path):
1034 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1036 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1040 def setup_samdb_rootdse(samdb, names):
1041 """Setup the SamDB rootdse.
1043 :param samdb: Sam Database handle
1045 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1046 "SCHEMADN": names.schemadn,
1047 "DOMAINDN": names.domaindn,
1048 "ROOTDN": names.rootdn,
1049 "CONFIGDN": names.configdn,
1050 "SERVERDN": names.serverdn,
1054 def setup_self_join(samdb, names, machinepass, dnspass,
1055 domainsid, next_rid, invocationid,
1056 policyguid, policyguid_dc, domainControllerFunctionality,
1058 """Join a host to its own domain."""
1059 assert isinstance(invocationid, str)
1060 if ntdsguid is not None:
1061 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1064 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1065 "CONFIGDN": names.configdn,
1066 "SCHEMADN": names.schemadn,
1067 "DOMAINDN": names.domaindn,
1068 "SERVERDN": names.serverdn,
1069 "INVOCATIONID": invocationid,
1070 "NETBIOSNAME": names.netbiosname,
1071 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1072 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1073 "DOMAINSID": str(domainsid),
1074 "DCRID": str(next_rid),
1075 "SAMBA_VERSION_STRING": version,
1076 "NTDSGUID": ntdsguid_line,
1077 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1078 domainControllerFunctionality)})
1080 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1081 "POLICYGUID": policyguid,
1082 "POLICYGUID_DC": policyguid_dc,
1083 "DNSDOMAIN": names.dnsdomain,
1084 "DOMAINDN": names.domaindn})
1086 # add the NTDSGUID based SPNs
1087 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1088 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1089 expression="", scope=ldb.SCOPE_BASE)
1090 assert isinstance(names.ntdsguid, str)
1092 # Setup fSMORoleOwner entries to point at the newly created DC entry
1093 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1094 "DOMAINDN": names.domaindn,
1095 "CONFIGDN": names.configdn,
1096 "SCHEMADN": names.schemadn,
1097 "DEFAULTSITE": names.sitename,
1098 "SERVERDN": names.serverdn,
1099 "NETBIOSNAME": names.netbiosname,
1100 "RIDALLOCATIONSTART": str(next_rid + 100),
1101 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1104 setup_ad_dns(samdb, names)
1105 # This is Samba4 specific and should be replaced by the correct
1106 # DNS AD-style setup
1107 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1108 "DNSDOMAIN": names.dnsdomain,
1109 "DOMAINDN": names.domaindn,
1110 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1111 "HOSTNAME" : names.hostname,
1112 "DNSNAME" : '%s.%s' % (
1113 names.netbiosname.lower(), names.dnsdomain.lower())
1117 def setup_ad_dns(samdb, names):
1118 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1119 "DOMAINDN": names.domaindn,
1120 "DNSNAME" : '%s.%s' % (
1121 names.netbiosname.lower(), names.dnsdomain.lower())
1124 def getpolicypath(sysvolpath, dnsdomain, guid):
1125 """Return the physical path of policy given its guid.
1127 :param sysvolpath: Path to the sysvol folder
1128 :param dnsdomain: DNS name of the AD domain
1129 :param guid: The GUID of the policy
1130 :return: A string with the complete path to the policy folder
1134 guid = "{%s}" % guid
1135 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1139 def create_gpo_struct(policy_path):
1140 if not os.path.exists(policy_path):
1141 os.makedirs(policy_path, 0775)
1142 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1143 "[General]\r\nVersion=0")
1144 p = os.path.join(policy_path, "MACHINE")
1145 if not os.path.exists(p):
1146 os.makedirs(p, 0775)
1147 p = os.path.join(policy_path, "USER")
1148 if not os.path.exists(p):
1149 os.makedirs(p, 0775)
1152 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1153 """Create the default GPO for a domain
1155 :param sysvolpath: Physical path for the sysvol folder
1156 :param dnsdomain: DNS domain name of the AD domain
1157 :param policyguid: GUID of the default domain policy
1158 :param policyguid_dc: GUID of the default domain controler policy
1160 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1161 create_gpo_struct(policy_path)
1163 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1164 create_gpo_struct(policy_path)
1167 def setup_samdb(path, session_info, provision_backend, lp, names,
1168 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1169 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1170 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1172 """Setup a complete SAM Database.
1174 :note: This will wipe the main SAM database file!
1177 # Provision does not make much sense values larger than 1000000000
1178 # as the upper range of the rIDAvailablePool is 1073741823 and
1179 # we don't want to create a domain that cannot allocate rids.
1180 if next_rid < 1000 or next_rid > 1000000000:
1181 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1182 error += "the valid range is %u-%u. The default is %u." % (
1183 1000, 1000000000, 1000)
1184 raise ProvisioningError(error)
1186 # ATTENTION: Do NOT change these default values without discussion with the
1187 # team and/or release manager. They have a big impact on the whole program!
1188 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1190 if dom_for_fun_level is None:
1191 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1193 if dom_for_fun_level > domainControllerFunctionality:
1194 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!")
1196 domainFunctionality = dom_for_fun_level
1197 forestFunctionality = dom_for_fun_level
1199 # Also wipes the database
1200 setup_samdb_partitions(path, logger=logger, lp=lp,
1201 provision_backend=provision_backend, session_info=session_info,
1202 names=names, serverrole=serverrole, schema=schema)
1205 schema = Schema(domainsid, schemadn=names.schemadn)
1207 # Load the database, but don's load the global schema and don't connect
1209 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1210 credentials=provision_backend.credentials, lp=lp,
1211 global_schema=False, am_rodc=am_rodc)
1213 logger.info("Pre-loading the Samba 4 and AD schema")
1215 # Load the schema from the one we computed earlier
1216 samdb.set_schema(schema)
1218 # Set the NTDS settings DN manually - in order to have it already around
1219 # before the provisioned tree exists and we connect
1220 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1222 # And now we can connect to the DB - the schema won't be loaded from the
1226 if fill == FILL_DRS:
1229 samdb.transaction_start()
1231 # Set the domain functionality levels onto the database.
1232 # Various module (the password_hash module in particular) need
1233 # to know what level of AD we are emulating.
1235 # These will be fixed into the database via the database
1236 # modifictions below, but we need them set from the start.
1237 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1238 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1239 samdb.set_opaque_integer("domainControllerFunctionality",
1240 domainControllerFunctionality)
1242 samdb.set_domain_sid(str(domainsid))
1243 samdb.set_invocation_id(invocationid)
1245 logger.info("Adding DomainDN: %s" % names.domaindn)
1247 # impersonate domain admin
1248 admin_session_info = admin_session(lp, str(domainsid))
1249 samdb.set_session_info(admin_session_info)
1250 if domainguid is not None:
1251 domainguid_line = "objectGUID: %s\n-" % domainguid
1253 domainguid_line = ""
1255 descr = b64encode(get_domain_descriptor(domainsid))
1256 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1257 "DOMAINDN": names.domaindn,
1258 "DOMAINSID": str(domainsid),
1259 "DESCRIPTOR": descr,
1260 "DOMAINGUID": domainguid_line
1263 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1264 "DOMAINDN": names.domaindn,
1265 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1266 "NEXTRID": str(next_rid),
1267 "DEFAULTSITE": names.sitename,
1268 "CONFIGDN": names.configdn,
1269 "POLICYGUID": policyguid,
1270 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1271 "SAMBA_VERSION_STRING": version
1274 logger.info("Adding configuration container")
1275 descr = b64encode(get_config_descriptor(domainsid))
1276 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1277 "CONFIGDN": names.configdn,
1278 "DESCRIPTOR": descr,
1281 # Now register this container in the root of the forest
1282 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1283 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1286 # The LDIF here was created when the Schema object was constructed
1287 logger.info("Setting up sam.ldb schema")
1288 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1289 samdb.modify_ldif(schema.schema_dn_modify)
1290 samdb.write_prefixes_from_schema()
1291 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1292 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1293 {"SCHEMADN": names.schemadn})
1295 logger.info("Reopening sam.ldb with new schema")
1297 samdb.transaction_cancel()
1300 samdb.transaction_commit()
1302 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1303 credentials=provision_backend.credentials, lp=lp,
1304 global_schema=False, am_rodc=am_rodc)
1306 # Set the NTDS settings DN manually - in order to have it already around
1307 # before the provisioned tree exists and we connect
1308 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1311 samdb.transaction_start()
1313 samdb.invocation_id = invocationid
1315 logger.info("Setting up sam.ldb configuration data")
1316 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1317 "CONFIGDN": names.configdn,
1318 "NETBIOSNAME": names.netbiosname,
1319 "DEFAULTSITE": names.sitename,
1320 "DNSDOMAIN": names.dnsdomain,
1321 "DOMAIN": names.domain,
1322 "SCHEMADN": names.schemadn,
1323 "DOMAINDN": names.domaindn,
1324 "SERVERDN": names.serverdn,
1325 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1326 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1329 logger.info("Setting up display specifiers")
1330 display_specifiers_ldif = read_ms_ldif(
1331 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1332 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1333 {"CONFIGDN": names.configdn})
1334 check_all_substituted(display_specifiers_ldif)
1335 samdb.add_ldif(display_specifiers_ldif)
1337 logger.info("Adding users container")
1338 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1339 "DOMAINDN": names.domaindn})
1340 logger.info("Modifying users container")
1341 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1342 "DOMAINDN": names.domaindn})
1343 logger.info("Adding computers container")
1344 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1345 "DOMAINDN": names.domaindn})
1346 logger.info("Modifying computers container")
1347 setup_modify_ldif(samdb,
1348 setup_path("provision_computers_modify.ldif"), {
1349 "DOMAINDN": names.domaindn})
1350 logger.info("Setting up sam.ldb data")
1351 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1352 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1353 "DOMAINDN": names.domaindn,
1354 "NETBIOSNAME": names.netbiosname,
1355 "DEFAULTSITE": names.sitename,
1356 "CONFIGDN": names.configdn,
1357 "SERVERDN": names.serverdn,
1358 "RIDAVAILABLESTART": str(next_rid + 600),
1359 "POLICYGUID_DC": policyguid_dc
1362 setup_modify_ldif(samdb,
1363 setup_path("provision_basedn_references.ldif"), {
1364 "DOMAINDN": names.domaindn})
1366 setup_modify_ldif(samdb,
1367 setup_path("provision_configuration_references.ldif"), {
1368 "CONFIGDN": names.configdn,
1369 "SCHEMADN": names.schemadn})
1370 if fill == FILL_FULL:
1371 logger.info("Setting up sam.ldb users and groups")
1372 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1373 "DOMAINDN": names.domaindn,
1374 "DOMAINSID": str(domainsid),
1375 "CONFIGDN": names.configdn,
1376 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1377 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1380 logger.info("Setting up self join")
1381 setup_self_join(samdb, names=names, invocationid=invocationid,
1383 machinepass=machinepass,
1384 domainsid=domainsid,
1386 policyguid=policyguid,
1387 policyguid_dc=policyguid_dc,
1388 domainControllerFunctionality=domainControllerFunctionality,
1391 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1392 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1393 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1394 assert isinstance(names.ntdsguid, str)
1396 samdb.transaction_cancel()
1399 samdb.transaction_commit()
1404 FILL_NT4SYNC = "NT4SYNC"
1406 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1407 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)"
1410 def set_dir_acl(path, acl, lp, domsid):
1411 setntacl(lp, path, acl, domsid)
1412 for root, dirs, files in os.walk(path, topdown=False):
1414 setntacl(lp, os.path.join(root, name), acl, domsid)
1416 setntacl(lp, os.path.join(root, name), acl, domsid)
1419 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1420 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1423 :param sysvol: Physical path for the sysvol folder
1424 :param dnsdomain: The DNS name of the domain
1425 :param domainsid: The SID of the domain
1426 :param domaindn: The DN of the domain (ie. DC=...)
1427 :param samdb: An LDB object on the SAM db
1428 :param lp: an LP object
1431 # Set ACL for GPO root folder
1432 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1433 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1435 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1436 attrs=["cn", "nTSecurityDescriptor"],
1437 expression="", scope=ldb.SCOPE_ONELEVEL)
1440 acl = ndr_unpack(security.descriptor,
1441 str(policy["nTSecurityDescriptor"])).as_sddl()
1442 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1443 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1447 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1449 """Set the ACL for the sysvol share and the subfolders
1451 :param samdb: An LDB object on the SAM db
1452 :param netlogon: Physical path for the netlogon folder
1453 :param sysvol: Physical path for the sysvol folder
1454 :param gid: The GID of the "Domain adminstrators" group
1455 :param domainsid: The SID of the domain
1456 :param dnsdomain: The DNS name of the domain
1457 :param domaindn: The DN of the domain (ie. DC=...)
1461 os.chown(sysvol, -1, gid)
1467 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1468 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1469 for root, dirs, files in os.walk(sysvol, topdown=False):
1472 os.chown(os.path.join(root, name), -1, gid)
1473 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1476 os.chown(os.path.join(root, name), -1, gid)
1477 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1479 # Set acls on Policy folder and policies folders
1480 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1483 def interface_ips_v4(lp):
1484 '''return only IPv4 IPs'''
1485 ips = samba.interface_ips(lp, False)
1488 if i.find(':') == -1:
1492 def interface_ips_v6(lp, linklocal=False):
1493 '''return only IPv6 IPs'''
1494 ips = samba.interface_ips(lp, False)
1497 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1502 def provision(logger, session_info, credentials, smbconf=None,
1503 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1504 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1505 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1506 next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1507 domainguid=None, policyguid=None, policyguid_dc=None,
1508 invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1509 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1510 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1511 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1512 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1513 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1517 :note: caution, this wipes all existing data!
1520 if domainsid is None:
1521 domainsid = security.random_sid()
1523 domainsid = security.dom_sid(domainsid)
1525 # create/adapt the group policy GUIDs
1526 # Default GUID for default policy are described at
1527 # "How Core Group Policy Works"
1528 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1529 if policyguid is None:
1530 policyguid = DEFAULT_POLICY_GUID
1531 policyguid = policyguid.upper()
1532 if policyguid_dc is None:
1533 policyguid_dc = DEFAULT_DC_POLICY_GUID
1534 policyguid_dc = policyguid_dc.upper()
1536 if adminpass is None:
1537 adminpass = samba.generate_random_password(12, 32)
1538 if krbtgtpass is None:
1539 krbtgtpass = samba.generate_random_password(128, 255)
1540 if machinepass is None:
1541 machinepass = samba.generate_random_password(128, 255)
1543 dnspass = samba.generate_random_password(128, 255)
1544 if ldapadminpass is None:
1545 # Make a new, random password between Samba and it's LDAP server
1546 ldapadminpass=samba.generate_random_password(128, 255)
1548 if backend_type is None:
1549 backend_type = "ldb"
1551 sid_generator = "internal"
1552 if backend_type == "fedora-ds":
1553 sid_generator = "backend"
1555 root_uid = findnss_uid([root or "root"])
1556 nobody_uid = findnss_uid([nobody or "nobody"])
1557 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1559 wheel_gid = findnss_gid(["wheel", "adm"])
1561 wheel_gid = findnss_gid([wheel])
1563 bind_gid = findnss_gid(["bind", "named"])
1567 if targetdir is not None:
1568 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1569 elif smbconf is None:
1570 smbconf = samba.param.default_path()
1571 if not os.path.exists(os.path.dirname(smbconf)):
1572 os.makedirs(os.path.dirname(smbconf))
1574 # only install a new smb.conf if there isn't one there already
1575 if os.path.exists(smbconf):
1576 # if Samba Team members can't figure out the weird errors
1577 # loading an empty smb.conf gives, then we need to be smarter.
1578 # Pretend it just didn't exist --abartlet
1579 data = open(smbconf, 'r').read()
1580 data = data.lstrip()
1581 if data is None or data == "":
1582 make_smbconf(smbconf, hostname, domain, realm,
1583 serverrole, targetdir, sid_generator, useeadb,
1586 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1587 targetdir, sid_generator, useeadb, lp=lp)
1590 lp = samba.param.LoadParm()
1592 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1593 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1594 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1596 paths = provision_paths_from_lp(lp, names.dnsdomain)
1598 paths.bind_gid = bind_gid
1601 logger.info("Looking up IPv4 addresses")
1602 hostips = interface_ips_v4(lp)
1603 if len(hostips) > 0:
1605 if len(hostips) > 1:
1606 logger.warning("More than one IPv4 address found. Using %s",
1608 if hostip == "127.0.0.1":
1611 logger.warning("No IPv4 address will be assigned")
1614 logger.info("Looking up IPv6 addresses")
1615 hostips = interface_ips_v6(lp, linklocal=False)
1617 hostip6 = hostips[0]
1618 if len(hostips) > 1:
1619 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1621 logger.warning("No IPv6 address will be assigned")
1623 if serverrole is None:
1624 serverrole = lp.get("server role")
1626 assert serverrole in ("domain controller", "member server", "standalone")
1627 if invocationid is None:
1628 invocationid = str(uuid.uuid4())
1630 if not os.path.exists(paths.private_dir):
1631 os.mkdir(paths.private_dir)
1632 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1633 os.mkdir(os.path.join(paths.private_dir, "tls"))
1635 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1637 schema = Schema(domainsid, invocationid=invocationid,
1638 schemadn=names.schemadn)
1640 if backend_type == "ldb":
1641 provision_backend = LDBBackend(backend_type, paths=paths,
1642 lp=lp, credentials=credentials,
1643 names=names, logger=logger)
1644 elif backend_type == "existing":
1645 provision_backend = ExistingBackend(backend_type, paths=paths,
1646 lp=lp, credentials=credentials,
1647 names=names, logger=logger,
1648 ldap_backend_forced_uri=ldap_backend_forced_uri)
1649 elif backend_type == "fedora-ds":
1650 provision_backend = FDSBackend(backend_type, paths=paths,
1651 lp=lp, credentials=credentials,
1652 names=names, logger=logger, domainsid=domainsid,
1653 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1654 slapd_path=slapd_path,
1655 ldap_backend_extra_port=ldap_backend_extra_port,
1656 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1657 setup_ds_path=setup_ds_path,
1658 ldap_backend_forced_uri=ldap_backend_forced_uri)
1659 elif backend_type == "openldap":
1660 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1661 lp=lp, credentials=credentials,
1662 names=names, logger=logger, domainsid=domainsid,
1663 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1664 slapd_path=slapd_path,
1665 ldap_backend_extra_port=ldap_backend_extra_port,
1666 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1668 ldap_backend_forced_uri=ldap_backend_forced_uri)
1670 raise ValueError("Unknown LDAP backend type selected")
1672 provision_backend.init()
1673 provision_backend.start()
1675 # only install a new shares config db if there is none
1676 if not os.path.exists(paths.shareconf):
1677 logger.info("Setting up share.ldb")
1678 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1680 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1682 logger.info("Setting up secrets.ldb")
1683 secrets_ldb = setup_secretsdb(paths,
1684 session_info=session_info,
1685 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1688 logger.info("Setting up the registry")
1689 setup_registry(paths.hklm, session_info,
1692 logger.info("Setting up the privileges database")
1693 setup_privileges(paths.privilege, session_info, lp=lp)
1695 logger.info("Setting up idmap db")
1696 idmap = setup_idmapdb(paths.idmapdb,
1697 session_info=session_info, lp=lp)
1699 logger.info("Setting up SAM db")
1700 samdb = setup_samdb(paths.samdb, session_info,
1701 provision_backend, lp, names, logger=logger,
1702 domainsid=domainsid, schema=schema, domainguid=domainguid,
1703 policyguid=policyguid, policyguid_dc=policyguid_dc,
1704 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1705 invocationid=invocationid, machinepass=machinepass,
1706 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1707 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1710 if serverrole == "domain controller":
1711 if paths.netlogon is None:
1712 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1713 logger.info("Please either remove %s or see the template at %s" %
1714 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1715 assert paths.netlogon is not None
1717 if paths.sysvol is None:
1718 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1719 " are configuring a DC.")
1720 logger.info("Please either remove %s or see the template at %s" %
1721 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1722 assert paths.sysvol is not None
1724 if not os.path.isdir(paths.netlogon):
1725 os.makedirs(paths.netlogon, 0755)
1727 if samdb_fill == FILL_FULL:
1728 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1729 root_uid=root_uid, nobody_uid=nobody_uid,
1730 users_gid=users_gid, wheel_gid=wheel_gid)
1732 if serverrole == "domain controller":
1733 # Set up group policies (domain policy and domain controller
1735 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1737 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1738 domainsid, names.dnsdomain, names.domaindn, lp)
1740 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1741 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1743 secretsdb_self_join(secrets_ldb, domain=names.domain,
1744 realm=names.realm, dnsdomain=names.dnsdomain,
1745 netbiosname=names.netbiosname, domainsid=domainsid,
1746 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1748 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1749 # In future, this might be determined from some configuration
1750 kerberos_enctypes = str(ENC_ALL_TYPES)
1753 msg = ldb.Message(ldb.Dn(samdb,
1754 samdb.searchone("distinguishedName",
1755 expression="samAccountName=%s$" % names.netbiosname,
1756 scope=ldb.SCOPE_SUBTREE)))
1757 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1758 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1759 name="msDS-SupportedEncryptionTypes")
1761 except ldb.LdbError, (enum, estr):
1762 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1763 # It might be that this attribute does not exist in this schema
1766 if serverrole == "domain controller":
1767 secretsdb_setup_dns(secrets_ldb, names,
1768 paths.private_dir, realm=names.realm,
1769 dnsdomain=names.dnsdomain,
1770 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1772 domainguid = samdb.searchone(basedn=domaindn,
1773 attribute="objectGUID")
1774 assert isinstance(domainguid, str)
1776 # Only make a zone file on the first DC, it should be
1777 # replicated with DNS replication
1778 create_zone_file(lp, logger, paths, targetdir,
1779 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1780 hostname=names.hostname, realm=names.realm,
1781 domainguid=domainguid, ntdsguid=names.ntdsguid)
1783 create_named_conf(paths, realm=names.realm,
1784 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1786 create_named_txt(paths.namedtxt,
1787 realm=names.realm, dnsdomain=names.dnsdomain,
1788 dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1789 private_dir=paths.private_dir,
1790 keytab_name=paths.dns_keytab)
1791 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1792 logger.info("and %s for further documentation required for secure DNS "
1793 "updates", paths.namedtxt)
1795 lastProvisionUSNs = get_last_provision_usn(samdb)
1796 maxUSN = get_max_usn(samdb, str(names.rootdn))
1797 if lastProvisionUSNs is not None:
1798 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1800 set_provision_usn(samdb, 0, maxUSN, invocationid)
1802 create_krb5_conf(paths.krb5conf,
1803 dnsdomain=names.dnsdomain, hostname=names.hostname,
1805 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1806 "generated at %s", paths.krb5conf)
1808 if serverrole == "domain controller":
1809 create_dns_update_list(lp, logger, paths)
1811 provision_backend.post_setup()
1812 provision_backend.shutdown()
1814 create_phpldapadmin_config(paths.phpldapadminconfig,
1817 secrets_ldb.transaction_cancel()
1820 # Now commit the secrets.ldb to disk
1821 secrets_ldb.transaction_commit()
1823 # the commit creates the dns.keytab, now chown it
1824 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1825 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1827 os.chmod(dns_keytab_path, 0640)
1828 os.chown(dns_keytab_path, -1, paths.bind_gid)
1830 if not os.environ.has_key('SAMBA_SELFTEST'):
1831 logger.info("Failed to chown %s to bind gid %u",
1832 dns_keytab_path, paths.bind_gid)
1835 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1836 paths.phpldapadminconfig)
1838 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1839 logger.info("Server Role: %s" % serverrole)
1840 logger.info("Hostname: %s" % names.hostname)
1841 logger.info("NetBIOS Domain: %s" % names.domain)
1842 logger.info("DNS Domain: %s" % names.dnsdomain)
1843 logger.info("DOMAIN SID: %s" % str(domainsid))
1844 if samdb_fill == FILL_FULL:
1845 logger.info("Admin password: %s" % adminpass)
1846 if provision_backend.type is not "ldb":
1847 if provision_backend.credentials.get_bind_dn() is not None:
1848 logger.info("LDAP Backend Admin DN: %s" %
1849 provision_backend.credentials.get_bind_dn())
1851 logger.info("LDAP Admin User: %s" %
1852 provision_backend.credentials.get_username())
1854 logger.info("LDAP Admin Password: %s" %
1855 provision_backend.credentials.get_password())
1857 if provision_backend.slapd_command_escaped is not None:
1858 # now display slapd_command_file.txt to show how slapd must be
1860 logger.info("Use later the following commandline to start slapd, then Samba:")
1861 logger.info(provision_backend.slapd_command_escaped)
1862 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1863 provision_backend.ldapdir)
1865 result = ProvisionResult()
1866 result.domaindn = domaindn
1867 result.paths = paths
1869 result.samdb = samdb
1873 def provision_become_dc(smbconf=None, targetdir=None,
1874 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1875 serverdn=None, domain=None, hostname=None, domainsid=None,
1876 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1877 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1878 root=None, nobody=None, users=None, wheel=None, backup=None,
1879 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1880 sitename=None, debuglevel=1):
1882 logger = logging.getLogger("provision")
1883 samba.set_debug_level(debuglevel)
1885 res = provision(logger, system_session(), None,
1886 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1887 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1888 configdn=configdn, serverdn=serverdn, domain=domain,
1889 hostname=hostname, hostip=None, domainsid=domainsid,
1890 machinepass=machinepass, serverrole="domain controller",
1892 res.lp.set("debuglevel", str(debuglevel))
1896 def create_phpldapadmin_config(path, ldapi_uri):
1897 """Create a PHP LDAP admin configuration file.
1899 :param path: Path to write the configuration to.
1901 setup_file(setup_path("phpldapadmin-config.php"), path,
1902 {"S4_LDAPI_URI": ldapi_uri})
1905 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1906 hostip, hostip6, hostname, realm, domainguid,
1908 """Write out a DNS zone file, from the info in the current database.
1910 :param paths: paths object
1911 :param dnsdomain: DNS Domain name
1912 :param domaindn: DN of the Domain
1913 :param hostip: Local IPv4 IP
1914 :param hostip6: Local IPv6 IP
1915 :param hostname: Local hostname
1916 :param realm: Realm name
1917 :param domainguid: GUID of the domain.
1918 :param ntdsguid: GUID of the hosts nTDSDSA record.
1920 assert isinstance(domainguid, str)
1922 if hostip6 is not None:
1923 hostip6_base_line = " IN AAAA " + hostip6
1924 hostip6_host_line = hostname + " IN AAAA " + hostip6
1925 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1927 hostip6_base_line = ""
1928 hostip6_host_line = ""
1929 gc_msdcs_ip6_line = ""
1931 if hostip is not None:
1932 hostip_base_line = " IN A " + hostip
1933 hostip_host_line = hostname + " IN A " + hostip
1934 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1936 hostip_base_line = ""
1937 hostip_host_line = ""
1938 gc_msdcs_ip_line = ""
1940 dns_dir = os.path.dirname(paths.dns)
1943 shutil.rmtree(dns_dir, True)
1947 os.mkdir(dns_dir, 0775)
1949 # we need to freeze the zone while we update the contents
1950 if targetdir is None:
1951 rndc = ' '.join(lp.get("rndc command"))
1952 os.system(rndc + " freeze " + lp.get("realm"))
1954 setup_file(setup_path("provision.zone"), paths.dns, {
1955 "HOSTNAME": hostname,
1956 "DNSDOMAIN": dnsdomain,
1958 "HOSTIP_BASE_LINE": hostip_base_line,
1959 "HOSTIP_HOST_LINE": hostip_host_line,
1960 "DOMAINGUID": domainguid,
1961 "DATESTRING": time.strftime("%Y%m%d%H"),
1962 "DEFAULTSITE": DEFAULTSITE,
1963 "NTDSGUID": ntdsguid,
1964 "HOSTIP6_BASE_LINE": hostip6_base_line,
1965 "HOSTIP6_HOST_LINE": hostip6_host_line,
1966 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1967 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1970 # note that we use no variable substitution on this file
1971 # the substitution is done at runtime by samba_dnsupdate
1972 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1974 # and the SPN update list
1975 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1977 if paths.bind_gid is not None:
1979 os.chown(dns_dir, -1, paths.bind_gid)
1980 os.chown(paths.dns, -1, paths.bind_gid)
1981 # chmod needed to cope with umask
1982 os.chmod(dns_dir, 0775)
1983 os.chmod(paths.dns, 0664)
1985 if not os.environ.has_key('SAMBA_SELFTEST'):
1986 logger.error("Failed to chown %s to bind gid %u" % (
1987 dns_dir, paths.bind_gid))
1989 if targetdir is None:
1990 os.system(rndc + " unfreeze " + lp.get("realm"))
1993 def create_dns_update_list(lp, logger, paths):
1994 """Write out a dns_update_list file"""
1995 # note that we use no variable substitution on this file
1996 # the substitution is done at runtime by samba_dnsupdate
1997 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1998 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2001 def create_named_conf(paths, realm, dnsdomain,
2003 """Write out a file containing zone statements suitable for inclusion in a
2004 named.conf file (including GSS-TSIG configuration).
2006 :param paths: all paths
2007 :param realm: Realm name
2008 :param dnsdomain: DNS Domain name
2009 :param private_dir: Path to private directory
2010 :param keytab_name: File name of DNS keytab file
2013 setup_file(setup_path("named.conf"), paths.namedconf, {
2014 "DNSDOMAIN": dnsdomain,
2016 "ZONE_FILE": paths.dns,
2017 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2018 "NAMED_CONF": paths.namedconf,
2019 "NAMED_CONF_UPDATE": paths.namedconf_update
2022 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2025 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2027 """Write out a file containing zone statements suitable for inclusion in a
2028 named.conf file (including GSS-TSIG configuration).
2030 :param path: Path of the new named.conf file.
2031 :param realm: Realm name
2032 :param dnsdomain: DNS Domain name
2033 :param private_dir: Path to private directory
2034 :param keytab_name: File name of DNS keytab file
2036 setup_file(setup_path("named.txt"), path, {
2037 "DNSDOMAIN": dnsdomain,
2038 "DNSNAME" : dnsname,
2040 "DNS_KEYTAB": keytab_name,
2041 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2042 "PRIVATE_DIR": private_dir
2046 def create_krb5_conf(path, dnsdomain, hostname, realm):
2047 """Write out a file containing zone statements suitable for inclusion in a
2048 named.conf file (including GSS-TSIG configuration).
2050 :param path: Path of the new named.conf file.
2051 :param dnsdomain: DNS Domain name
2052 :param hostname: Local hostname
2053 :param realm: Realm name
2055 setup_file(setup_path("krb5.conf"), path, {
2056 "DNSDOMAIN": dnsdomain,
2057 "HOSTNAME": hostname,
2062 class ProvisioningError(Exception):
2063 """A generic provision error."""
2065 def __init__(self, value):
2069 return "ProvisioningError: " + self.value
2072 class InvalidNetbiosName(Exception):
2073 """A specified name was not a valid NetBIOS name."""
2074 def __init__(self, name):
2075 super(InvalidNetbiosName, self).__init__(
2076 "The name '%r' is not a valid NetBIOS name" % name)