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_sites_descriptor(domain_sid):
98 sddl = "D:(A;;RPLCLORC;;;AU)" \
99 "(A;;RPWPCRCCLCLORCWOWDSW;;;EA)" \
100 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
101 "S:AI(AU;CISA;CCDCSDDT;;;WD)" \
102 "(OU;CIIOSA;CR;;f0f8ffab-1191-11d0-a060-00aa006c33ed;WD)" \
103 "(OU;CIIOSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
104 "(OU;CIIOSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967ab3-0de6-11d0-a285-00aa003049e2;WD)" \
105 "(OU;CIIOSA;WP;3e10944c-c354-11d0-aff8-0000f80367c1;b7b13124-b82e-11d0-afee-0000f80367c1;WD)"
106 sec = security.descriptor.from_sddl(sddl, domain_sid)
110 def get_config_descriptor(domain_sid):
111 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
112 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
113 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
114 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
118 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
119 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
120 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
121 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
122 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
123 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
124 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
125 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
126 sec = security.descriptor.from_sddl(sddl, domain_sid)
130 def get_domain_descriptor(domain_sid):
131 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
132 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
133 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
134 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
135 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
136 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
137 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
138 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
139 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
140 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
141 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
142 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
143 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
144 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
145 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
146 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
147 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
148 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
149 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
150 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
151 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
152 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
153 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
154 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
155 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
156 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
157 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
158 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
159 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
160 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
161 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
162 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
163 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
164 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
165 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
166 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
167 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
168 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
169 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
172 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
174 "(A;;RPLCLORC;;;ED)" \
175 "(A;;RPLCLORC;;;AU)" \
176 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
177 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
178 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
179 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
180 sec = security.descriptor.from_sddl(sddl, domain_sid)
184 class ProvisionPaths(object):
187 self.shareconf = None
198 self.dns_keytab = None
201 self.private_dir = None
204 class ProvisionNames(object):
211 self.ldapmanagerdn = None
212 self.dnsdomain = None
214 self.netbiosname = None
220 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
221 """Get key provision parameters (realm, domain, ...) from a given provision
223 :param samdb: An LDB object connected to the sam.ldb file
224 :param secretsdb: An LDB object connected to the secrets.ldb file
225 :param idmapdb: An LDB object connected to the idmap.ldb file
226 :param paths: A list of path to provision object
227 :param smbconf: Path to the smb.conf file
228 :param lp: A LoadParm object
229 :return: A list of key provision parameters
231 names = ProvisionNames()
232 names.adminpass = None
234 # NT domain, kerberos realm, root dn, domain dn, domain dns name
235 names.domain = string.upper(lp.get("workgroup"))
236 names.realm = lp.get("realm")
237 basedn = "DC=" + names.realm.replace(".",",DC=")
238 names.dnsdomain = names.realm.lower()
239 names.realm = string.upper(names.realm)
241 # Get the netbiosname first (could be obtained from smb.conf in theory)
242 res = secretsdb.search(expression="(flatname=%s)" %
243 names.domain,base="CN=Primary Domains",
244 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
245 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
247 names.smbconf = smbconf
249 # That's a bit simplistic but it's ok as long as we have only 3
251 current = samdb.search(expression="(objectClass=*)",
252 base="", scope=ldb.SCOPE_BASE,
253 attrs=["defaultNamingContext", "schemaNamingContext",
254 "configurationNamingContext","rootDomainNamingContext"])
256 names.configdn = current[0]["configurationNamingContext"]
257 configdn = str(names.configdn)
258 names.schemadn = current[0]["schemaNamingContext"]
259 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
260 current[0]["defaultNamingContext"][0]))):
261 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
262 "is not the same ..." % (paths.samdb,
263 str(current[0]["defaultNamingContext"][0]),
264 paths.smbconf, basedn)))
266 names.domaindn=current[0]["defaultNamingContext"]
267 names.rootdn=current[0]["rootDomainNamingContext"]
269 res3 = samdb.search(expression="(objectClass=*)",
270 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
271 names.sitename = str(res3[0]["cn"])
273 # dns hostname and server dn
274 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
275 base="OU=Domain Controllers,%s" % basedn,
276 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
277 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
279 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
280 attrs=[], base=configdn)
281 names.serverdn = server_res[0].dn
283 # invocation id/objectguid
284 res5 = samdb.search(expression="(objectClass=*)",
285 base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
286 attrs=["invocationID", "objectGUID"])
287 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
288 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
291 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
292 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
293 "objectSid","msDS-Behavior-Version" ])
294 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
295 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
296 if res6[0].get("msDS-Behavior-Version") is None or \
297 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
298 names.domainlevel = DS_DOMAIN_FUNCTION_2000
300 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
303 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
304 base="CN=Policies,CN=System," + basedn,
305 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
306 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
308 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
310 base="CN=Policies,CN=System," + basedn,
311 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
313 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
315 names.policyid_dc = None
316 res9 = idmapdb.search(expression="(cn=%s)" %
317 (security.SID_BUILTIN_ADMINISTRATORS),
320 names.wheel_gid = res9[0]["xidNumber"]
322 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
325 def update_provision_usn(samdb, low, high, replace=False):
326 """Update the field provisionUSN in sam.ldb
328 This field is used to track range of USN modified by provision and
330 This value is used afterward by next provision to figure out if
331 the field have been modified since last provision.
333 :param samdb: An LDB object connect to sam.ldb
334 :param low: The lowest USN modified by this upgrade
335 :param high: The highest USN modified by this upgrade
336 :param replace: A boolean indicating if the range should replace any
337 existing one or appended (default)
342 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" %
343 LAST_PROVISION_USN_ATTRIBUTE, base="",
344 scope=ldb.SCOPE_SUBTREE,
345 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
346 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
349 tab.append("%s-%s" % (low, high))
350 delta = ldb.Message()
351 delta.dn = ldb.Dn(samdb, "@PROVISION")
352 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
353 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
357 def set_provision_usn(samdb, low, high):
358 """Set the field provisionUSN in sam.ldb
359 This field is used to track range of USN modified by provision and
361 This value is used afterward by next provision to figure out if
362 the field have been modified since last provision.
364 :param samdb: An LDB object connect to sam.ldb
365 :param low: The lowest USN modified by this upgrade
366 :param high: The highest USN modified by this upgrade"""
368 tab.append("%s-%s" % (low, high))
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 the lastest USN modified by a provision or an upgradeprovision
395 :param sam: An LDB object pointing to the sam.ldb
396 :return: an integer corresponding to the highest USN modified by
397 (upgrade)provision, 0 is this value is unknown
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])
407 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
408 tab = p.split(str(r))
417 class ProvisionResult(object):
426 def check_install(lp, session_info, credentials):
427 """Check whether the current install seems ok.
429 :param lp: Loadparm context
430 :param session_info: Session information
431 :param credentials: Credentials
433 if lp.get("realm") == "":
434 raise Exception("Realm empty")
435 samdb = Ldb(lp.get("sam database"), session_info=session_info,
436 credentials=credentials, lp=lp)
437 if len(samdb.search("(cn=Administrator)")) != 1:
438 raise ProvisioningError("No administrator account found")
441 def findnss(nssfn, names):
442 """Find a user or group from a list of possibilities.
444 :param nssfn: NSS Function to try (should raise KeyError if not found)
445 :param names: Names to check.
446 :return: Value return by first names list.
453 raise KeyError("Unable to find user/group in %r" % names)
456 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
457 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
460 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
461 """Setup a ldb in the private dir.
463 :param ldb: LDB file to import data into
464 :param ldif_path: Path of the LDIF file to load
465 :param subst_vars: Optional variables to subsitute in LDIF.
466 :param nocontrols: Optional list of controls, can be None for no controls
468 assert isinstance(ldif_path, str)
469 data = read_and_sub_file(ldif_path, subst_vars)
470 ldb.add_ldif(data, controls)
473 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
474 """Modify a ldb in the private dir.
476 :param ldb: LDB object.
477 :param ldif_path: LDIF file path.
478 :param subst_vars: Optional dictionary with substitution variables.
480 data = read_and_sub_file(ldif_path, subst_vars)
481 ldb.modify_ldif(data, controls)
484 def setup_ldb(ldb, ldif_path, subst_vars):
485 """Import a LDIF a file into a LDB handle, optionally substituting
488 :note: Either all LDIF data will be added or none (using transactions).
490 :param ldb: LDB file to import into.
491 :param ldif_path: Path to the LDIF file.
492 :param subst_vars: Dictionary with substitution variables.
494 assert ldb is not None
495 ldb.transaction_start()
497 setup_add_ldif(ldb, ldif_path, subst_vars)
499 ldb.transaction_cancel()
502 ldb.transaction_commit()
505 def provision_paths_from_lp(lp, dnsdomain):
506 """Set the default paths for provisioning.
508 :param lp: Loadparm context.
509 :param dnsdomain: DNS Domain name
511 paths = ProvisionPaths()
512 paths.private_dir = lp.get("private dir")
514 # This is stored without path prefix for the "privateKeytab" attribute in
515 # "secrets_dns.ldif".
516 paths.dns_keytab = "dns.keytab"
517 paths.keytab = "secrets.keytab"
519 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
520 paths.samdb = os.path.join(paths.private_dir,
521 lp.get("sam database") or "samdb.ldb")
522 paths.idmapdb = os.path.join(paths.private_dir,
523 lp.get("idmap database") or "idmap.ldb")
524 paths.secrets = os.path.join(paths.private_dir,
525 lp.get("secrets database") or "secrets.ldb")
526 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
527 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
528 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
529 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
530 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
531 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
532 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
533 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
534 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
535 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
536 paths.phpldapadminconfig = os.path.join(paths.private_dir,
537 "phpldapadmin-config.php")
538 paths.hklm = "hklm.ldb"
539 paths.hkcr = "hkcr.ldb"
540 paths.hkcu = "hkcu.ldb"
541 paths.hku = "hku.ldb"
542 paths.hkpd = "hkpd.ldb"
543 paths.hkpt = "hkpt.ldb"
544 paths.sysvol = lp.get("path", "sysvol")
545 paths.netlogon = lp.get("path", "netlogon")
546 paths.smbconf = lp.configfile
550 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
551 serverrole=None, rootdn=None, domaindn=None, configdn=None,
552 schemadn=None, serverdn=None, sitename=None):
553 """Guess configuration settings to use."""
556 hostname = socket.gethostname().split(".")[0]
558 netbiosname = lp.get("netbios name")
559 if netbiosname is None:
560 netbiosname = hostname
561 # remove forbidden chars
563 for x in netbiosname:
564 if x.isalnum() or x in VALID_NETBIOS_CHARS:
565 newnbname = "%s%c" % (newnbname, x)
566 # force the length to be <16
567 netbiosname = newnbname[0:15]
568 assert netbiosname is not None
569 netbiosname = netbiosname.upper()
570 if not valid_netbios_name(netbiosname):
571 raise InvalidNetbiosName(netbiosname)
573 if dnsdomain is None:
574 dnsdomain = lp.get("realm")
575 if dnsdomain is None or dnsdomain == "":
576 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
578 dnsdomain = dnsdomain.lower()
580 if serverrole is None:
581 serverrole = lp.get("server role")
582 if serverrole is None:
583 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
585 serverrole = serverrole.lower()
587 realm = dnsdomain.upper()
589 if lp.get("realm") == "":
590 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
592 if lp.get("realm").upper() != realm:
593 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))
595 if lp.get("server role").lower() != serverrole:
596 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))
598 if serverrole == "domain controller":
600 # This will, for better or worse, default to 'WORKGROUP'
601 domain = lp.get("workgroup")
602 domain = domain.upper()
604 if lp.get("workgroup").upper() != domain:
605 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))
608 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
610 if domain == netbiosname:
611 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
615 domaindn = "DC=" + netbiosname
617 if not valid_netbios_name(domain):
618 raise InvalidNetbiosName(domain)
620 if hostname.upper() == realm:
621 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
622 if netbiosname.upper() == realm:
623 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
625 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
631 configdn = "CN=Configuration," + rootdn
633 schemadn = "CN=Schema," + configdn
638 names = ProvisionNames()
639 names.rootdn = rootdn
640 names.domaindn = domaindn
641 names.configdn = configdn
642 names.schemadn = schemadn
643 names.ldapmanagerdn = "CN=Manager," + rootdn
644 names.dnsdomain = dnsdomain
645 names.domain = domain
647 names.netbiosname = netbiosname
648 names.hostname = hostname
649 names.sitename = sitename
650 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
651 netbiosname, sitename, configdn)
656 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
657 targetdir, sid_generator="internal", eadb=False, lp=None):
658 """Create a new smb.conf file based on a couple of basic settings.
660 assert smbconf is not None
662 hostname = socket.gethostname().split(".")[0]
663 netbiosname = hostname.upper()
664 # remove forbidden chars
666 for x in netbiosname:
667 if x.isalnum() or x in VALID_NETBIOS_CHARS:
668 newnbname = "%s%c" % (newnbname, x)
669 #force the length to be <16
670 netbiosname = newnbname[0:15]
672 netbiosname = hostname.upper()
674 if serverrole is None:
675 serverrole = "standalone"
677 assert serverrole in ("domain controller", "member server", "standalone")
678 if serverrole == "domain controller":
680 elif serverrole == "member server":
681 smbconfsuffix = "member"
682 elif serverrole == "standalone":
683 smbconfsuffix = "standalone"
685 if sid_generator is None:
686 sid_generator = "internal"
688 assert domain is not None
689 domain = domain.upper()
691 assert realm is not None
692 realm = realm.upper()
695 lp = samba.param.LoadParm()
696 #Load non-existant file
697 if os.path.exists(smbconf):
699 if eadb and not lp.get("posix:eadb"):
700 if targetdir is not None:
701 privdir = os.path.join(targetdir, "private")
703 privdir = lp.get("private dir")
704 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
706 if targetdir is not None:
707 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
708 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
710 lp.set("lock dir", os.path.abspath(targetdir))
715 if sid_generator == "internal":
716 sid_generator_line = ""
718 sid_generator_line = "sid generator = " + sid_generator
720 sysvol = os.path.join(lp.get("lock dir"), "sysvol")
721 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
723 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
725 "NETBIOS_NAME": netbiosname,
728 "SERVERROLE": serverrole,
729 "NETLOGONPATH": netlogon,
730 "SYSVOLPATH": sysvol,
731 "SIDGENERATOR_LINE": sid_generator_line,
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 # This is partially Samba4 specific and should be replaced by the correct
1105 # DNS AD-style setup
1106 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1107 "DNSDOMAIN": names.dnsdomain,
1108 "DOMAINDN": names.domaindn,
1109 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1110 "HOSTNAME" : names.hostname,
1111 "DNSNAME" : '%s.%s' % (
1112 names.netbiosname.lower(), names.dnsdomain.lower())
1116 def getpolicypath(sysvolpath, dnsdomain, guid):
1117 """Return the physical path of policy given its guid.
1119 :param sysvolpath: Path to the sysvol folder
1120 :param dnsdomain: DNS name of the AD domain
1121 :param guid: The GUID of the policy
1122 :return: A string with the complete path to the policy folder
1126 guid = "{%s}" % guid
1127 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1131 def create_gpo_struct(policy_path):
1132 if not os.path.exists(policy_path):
1133 os.makedirs(policy_path, 0775)
1134 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1135 "[General]\r\nVersion=0")
1136 p = os.path.join(policy_path, "MACHINE")
1137 if not os.path.exists(p):
1138 os.makedirs(p, 0775)
1139 p = os.path.join(policy_path, "USER")
1140 if not os.path.exists(p):
1141 os.makedirs(p, 0775)
1144 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1145 """Create the default GPO for a domain
1147 :param sysvolpath: Physical path for the sysvol folder
1148 :param dnsdomain: DNS domain name of the AD domain
1149 :param policyguid: GUID of the default domain policy
1150 :param policyguid_dc: GUID of the default domain controler policy
1152 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1153 create_gpo_struct(policy_path)
1155 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1156 create_gpo_struct(policy_path)
1159 def setup_samdb(path, session_info, provision_backend, lp, names,
1160 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1161 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1162 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1164 """Setup a complete SAM Database.
1166 :note: This will wipe the main SAM database file!
1169 # Provision does not make much sense values larger than 1000000000
1170 # as the upper range of the rIDAvailablePool is 1073741823 and
1171 # we don't want to create a domain that cannot allocate rids.
1172 if next_rid < 1000 or next_rid > 1000000000:
1173 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1174 error += "the valid range is %u-%u. The default is %u." % (
1175 1000, 1000000000, 1000)
1176 raise ProvisioningError(error)
1178 # ATTENTION: Do NOT change these default values without discussion with the
1179 # team and/or release manager. They have a big impact on the whole program!
1180 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1182 if dom_for_fun_level is None:
1183 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1185 if dom_for_fun_level > domainControllerFunctionality:
1186 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!")
1188 domainFunctionality = dom_for_fun_level
1189 forestFunctionality = dom_for_fun_level
1191 # Also wipes the database
1192 setup_samdb_partitions(path, logger=logger, lp=lp,
1193 provision_backend=provision_backend, session_info=session_info,
1194 names=names, serverrole=serverrole, schema=schema)
1197 schema = Schema(domainsid, schemadn=names.schemadn)
1199 # Load the database, but don's load the global schema and don't connect
1201 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1202 credentials=provision_backend.credentials, lp=lp,
1203 global_schema=False, am_rodc=am_rodc)
1205 logger.info("Pre-loading the Samba 4 and AD schema")
1207 # Load the schema from the one we computed earlier
1208 samdb.set_schema(schema)
1210 # Set the NTDS settings DN manually - in order to have it already around
1211 # before the provisioned tree exists and we connect
1212 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1214 # And now we can connect to the DB - the schema won't be loaded from the
1218 if fill == FILL_DRS:
1221 samdb.transaction_start()
1223 # Set the domain functionality levels onto the database.
1224 # Various module (the password_hash module in particular) need
1225 # to know what level of AD we are emulating.
1227 # These will be fixed into the database via the database
1228 # modifictions below, but we need them set from the start.
1229 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1230 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1231 samdb.set_opaque_integer("domainControllerFunctionality",
1232 domainControllerFunctionality)
1234 samdb.set_domain_sid(str(domainsid))
1235 samdb.set_invocation_id(invocationid)
1237 logger.info("Adding DomainDN: %s" % names.domaindn)
1239 # impersonate domain admin
1240 admin_session_info = admin_session(lp, str(domainsid))
1241 samdb.set_session_info(admin_session_info)
1242 if domainguid is not None:
1243 domainguid_line = "objectGUID: %s\n-" % domainguid
1245 domainguid_line = ""
1247 descr = b64encode(get_domain_descriptor(domainsid))
1248 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1249 "DOMAINDN": names.domaindn,
1250 "DOMAINSID": str(domainsid),
1251 "DESCRIPTOR": descr,
1252 "DOMAINGUID": domainguid_line
1255 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1256 "DOMAINDN": names.domaindn,
1257 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1258 "NEXTRID": str(next_rid),
1259 "DEFAULTSITE": names.sitename,
1260 "CONFIGDN": names.configdn,
1261 "POLICYGUID": policyguid,
1262 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1263 "SAMBA_VERSION_STRING": version
1266 logger.info("Adding configuration container")
1267 descr = b64encode(get_config_descriptor(domainsid))
1268 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1269 "CONFIGDN": names.configdn,
1270 "DESCRIPTOR": descr,
1273 # Now register this container in the root of the forest
1274 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1275 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1278 # The LDIF here was created when the Schema object was constructed
1279 logger.info("Setting up sam.ldb schema")
1280 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1281 samdb.modify_ldif(schema.schema_dn_modify)
1282 samdb.write_prefixes_from_schema()
1283 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1284 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1285 {"SCHEMADN": names.schemadn})
1287 logger.info("Reopening sam.ldb with new schema")
1289 samdb.transaction_cancel()
1292 samdb.transaction_commit()
1294 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1295 credentials=provision_backend.credentials, lp=lp,
1296 global_schema=False, am_rodc=am_rodc)
1298 # Set the NTDS settings DN manually - in order to have it already around
1299 # before the provisioned tree exists and we connect
1300 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1303 samdb.transaction_start()
1305 samdb.invocation_id = invocationid
1307 logger.info("Setting up sam.ldb configuration data")
1308 descr = b64encode(get_sites_descriptor(domainsid))
1309 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1310 "CONFIGDN": names.configdn,
1311 "NETBIOSNAME": names.netbiosname,
1312 "DEFAULTSITE": names.sitename,
1313 "DNSDOMAIN": names.dnsdomain,
1314 "DOMAIN": names.domain,
1315 "SCHEMADN": names.schemadn,
1316 "DOMAINDN": names.domaindn,
1317 "SERVERDN": names.serverdn,
1318 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1319 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1320 "SITES_DESCRIPTOR": descr
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 provision(logger, session_info, credentials, smbconf=None,
1478 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1479 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1480 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1481 next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1482 domainguid=None, policyguid=None, policyguid_dc=None,
1483 invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1484 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1485 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1486 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1487 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1488 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1492 :note: caution, this wipes all existing data!
1495 if domainsid is None:
1496 domainsid = security.random_sid()
1498 domainsid = security.dom_sid(domainsid)
1500 # create/adapt the group policy GUIDs
1501 # Default GUID for default policy are described at
1502 # "How Core Group Policy Works"
1503 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1504 if policyguid is None:
1505 policyguid = DEFAULT_POLICY_GUID
1506 policyguid = policyguid.upper()
1507 if policyguid_dc is None:
1508 policyguid_dc = DEFAULT_DC_POLICY_GUID
1509 policyguid_dc = policyguid_dc.upper()
1511 if adminpass is None:
1512 adminpass = samba.generate_random_password(12, 32)
1513 if krbtgtpass is None:
1514 krbtgtpass = samba.generate_random_password(128, 255)
1515 if machinepass is None:
1516 machinepass = samba.generate_random_password(128, 255)
1518 dnspass = samba.generate_random_password(128, 255)
1519 if ldapadminpass is None:
1520 # Make a new, random password between Samba and it's LDAP server
1521 ldapadminpass=samba.generate_random_password(128, 255)
1523 if backend_type is None:
1524 backend_type = "ldb"
1526 sid_generator = "internal"
1527 if backend_type == "fedora-ds":
1528 sid_generator = "backend"
1530 root_uid = findnss_uid([root or "root"])
1531 nobody_uid = findnss_uid([nobody or "nobody"])
1532 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1534 wheel_gid = findnss_gid(["wheel", "adm"])
1536 wheel_gid = findnss_gid([wheel])
1538 bind_gid = findnss_gid(["bind", "named"])
1542 if targetdir is not None:
1543 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1544 elif smbconf is None:
1545 smbconf = samba.param.default_path()
1546 if not os.path.exists(os.path.dirname(smbconf)):
1547 os.makedirs(os.path.dirname(smbconf))
1549 # only install a new smb.conf if there isn't one there already
1550 if os.path.exists(smbconf):
1551 # if Samba Team members can't figure out the weird errors
1552 # loading an empty smb.conf gives, then we need to be smarter.
1553 # Pretend it just didn't exist --abartlet
1554 data = open(smbconf, 'r').read()
1555 data = data.lstrip()
1556 if data is None or data == "":
1557 make_smbconf(smbconf, hostname, domain, realm,
1558 serverrole, targetdir, sid_generator, useeadb,
1561 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1562 targetdir, sid_generator, useeadb, lp=lp)
1565 lp = samba.param.LoadParm()
1567 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1568 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1569 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1571 paths = provision_paths_from_lp(lp, names.dnsdomain)
1573 paths.bind_gid = bind_gid
1576 logger.info("Looking up IPv4 addresses")
1577 hostips = samba.interface_ips(lp, False)
1578 if len(hostips) == 0:
1579 logger.warning("No external IPv4 address has been found. Using loopback.")
1580 hostip = '127.0.0.1'
1583 if len(hostips) > 1:
1584 logger.warning("More than one IPv4 address found. Using %s.",
1587 if serverrole is None:
1588 serverrole = lp.get("server role")
1590 assert serverrole in ("domain controller", "member server", "standalone")
1591 if invocationid is None:
1592 invocationid = str(uuid.uuid4())
1594 if not os.path.exists(paths.private_dir):
1595 os.mkdir(paths.private_dir)
1596 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1597 os.mkdir(os.path.join(paths.private_dir, "tls"))
1599 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1601 schema = Schema(domainsid, invocationid=invocationid,
1602 schemadn=names.schemadn)
1604 if backend_type == "ldb":
1605 provision_backend = LDBBackend(backend_type, paths=paths,
1606 lp=lp, credentials=credentials,
1607 names=names, logger=logger)
1608 elif backend_type == "existing":
1609 provision_backend = ExistingBackend(backend_type, paths=paths,
1610 lp=lp, credentials=credentials,
1611 names=names, logger=logger,
1612 ldap_backend_forced_uri=ldap_backend_forced_uri)
1613 elif backend_type == "fedora-ds":
1614 provision_backend = FDSBackend(backend_type, paths=paths,
1615 lp=lp, credentials=credentials,
1616 names=names, logger=logger, domainsid=domainsid,
1617 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1618 slapd_path=slapd_path,
1619 ldap_backend_extra_port=ldap_backend_extra_port,
1620 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1621 setup_ds_path=setup_ds_path,
1622 ldap_backend_forced_uri=ldap_backend_forced_uri)
1623 elif backend_type == "openldap":
1624 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1625 lp=lp, credentials=credentials,
1626 names=names, logger=logger, domainsid=domainsid,
1627 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1628 slapd_path=slapd_path,
1629 ldap_backend_extra_port=ldap_backend_extra_port,
1630 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1632 ldap_backend_forced_uri=ldap_backend_forced_uri)
1634 raise ValueError("Unknown LDAP backend type selected")
1636 provision_backend.init()
1637 provision_backend.start()
1639 # only install a new shares config db if there is none
1640 if not os.path.exists(paths.shareconf):
1641 logger.info("Setting up share.ldb")
1642 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1644 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1646 logger.info("Setting up secrets.ldb")
1647 secrets_ldb = setup_secretsdb(paths,
1648 session_info=session_info,
1649 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1652 logger.info("Setting up the registry")
1653 setup_registry(paths.hklm, session_info,
1656 logger.info("Setting up the privileges database")
1657 setup_privileges(paths.privilege, session_info, lp=lp)
1659 logger.info("Setting up idmap db")
1660 idmap = setup_idmapdb(paths.idmapdb,
1661 session_info=session_info, lp=lp)
1663 logger.info("Setting up SAM db")
1664 samdb = setup_samdb(paths.samdb, session_info,
1665 provision_backend, lp, names, logger=logger,
1666 domainsid=domainsid, schema=schema, domainguid=domainguid,
1667 policyguid=policyguid, policyguid_dc=policyguid_dc,
1668 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1669 invocationid=invocationid, machinepass=machinepass,
1670 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1671 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1674 if serverrole == "domain controller":
1675 if paths.netlogon is None:
1676 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1677 logger.info("Please either remove %s or see the template at %s" %
1678 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1679 assert paths.netlogon is not None
1681 if paths.sysvol is None:
1682 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1683 " are configuring a DC.")
1684 logger.info("Please either remove %s or see the template at %s" %
1685 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1686 assert paths.sysvol is not None
1688 if not os.path.isdir(paths.netlogon):
1689 os.makedirs(paths.netlogon, 0755)
1691 if samdb_fill == FILL_FULL:
1692 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1693 root_uid=root_uid, nobody_uid=nobody_uid,
1694 users_gid=users_gid, wheel_gid=wheel_gid)
1696 if serverrole == "domain controller":
1697 # Set up group policies (domain policy and domain controller
1699 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1701 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1702 domainsid, names.dnsdomain, names.domaindn, lp)
1704 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1705 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1707 secretsdb_self_join(secrets_ldb, domain=names.domain,
1708 realm=names.realm, dnsdomain=names.dnsdomain,
1709 netbiosname=names.netbiosname, domainsid=domainsid,
1710 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1712 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1713 # In future, this might be determined from some configuration
1714 kerberos_enctypes = str(ENC_ALL_TYPES)
1717 msg = ldb.Message(ldb.Dn(samdb,
1718 samdb.searchone("distinguishedName",
1719 expression="samAccountName=%s$" % names.netbiosname,
1720 scope=ldb.SCOPE_SUBTREE)))
1721 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1722 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1723 name="msDS-SupportedEncryptionTypes")
1725 except ldb.LdbError, (enum, estr):
1726 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1727 # It might be that this attribute does not exist in this schema
1730 if serverrole == "domain controller":
1731 secretsdb_setup_dns(secrets_ldb, names,
1732 paths.private_dir, realm=names.realm,
1733 dnsdomain=names.dnsdomain,
1734 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1736 domainguid = samdb.searchone(basedn=domaindn,
1737 attribute="objectGUID")
1738 assert isinstance(domainguid, str)
1740 # Only make a zone file on the first DC, it should be
1741 # replicated with DNS replication
1742 create_zone_file(lp, logger, paths, targetdir,
1743 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1744 hostname=names.hostname, realm=names.realm,
1745 domainguid=domainguid, ntdsguid=names.ntdsguid)
1747 create_named_conf(paths, realm=names.realm,
1748 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1750 create_named_txt(paths.namedtxt,
1751 realm=names.realm, dnsdomain=names.dnsdomain,
1752 dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1753 private_dir=paths.private_dir,
1754 keytab_name=paths.dns_keytab)
1755 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1756 logger.info("and %s for further documentation required for secure DNS "
1757 "updates", paths.namedtxt)
1759 lastProvisionUSNs = get_last_provision_usn(samdb)
1760 maxUSN = get_max_usn(samdb, str(names.rootdn))
1761 if lastProvisionUSNs is not None:
1762 update_provision_usn(samdb, 0, maxUSN, 1)
1764 set_provision_usn(samdb, 0, maxUSN)
1766 create_krb5_conf(paths.krb5conf,
1767 dnsdomain=names.dnsdomain, hostname=names.hostname,
1769 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1770 "generated at %s", paths.krb5conf)
1772 if serverrole == "domain controller":
1773 create_dns_update_list(lp, logger, paths)
1775 provision_backend.post_setup()
1776 provision_backend.shutdown()
1778 create_phpldapadmin_config(paths.phpldapadminconfig,
1781 secrets_ldb.transaction_cancel()
1784 # Now commit the secrets.ldb to disk
1785 secrets_ldb.transaction_commit()
1787 # the commit creates the dns.keytab, now chown it
1788 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1789 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1791 os.chmod(dns_keytab_path, 0640)
1792 os.chown(dns_keytab_path, -1, paths.bind_gid)
1794 if not os.environ.has_key('SAMBA_SELFTEST'):
1795 logger.info("Failed to chown %s to bind gid %u",
1796 dns_keytab_path, paths.bind_gid)
1799 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1800 paths.phpldapadminconfig)
1802 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1803 logger.info("Server Role: %s" % serverrole)
1804 logger.info("Hostname: %s" % names.hostname)
1805 logger.info("NetBIOS Domain: %s" % names.domain)
1806 logger.info("DNS Domain: %s" % names.dnsdomain)
1807 logger.info("DOMAIN SID: %s" % str(domainsid))
1808 if samdb_fill == FILL_FULL:
1809 logger.info("Admin password: %s" % adminpass)
1810 if provision_backend.type is not "ldb":
1811 if provision_backend.credentials.get_bind_dn() is not None:
1812 logger.info("LDAP Backend Admin DN: %s" %
1813 provision_backend.credentials.get_bind_dn())
1815 logger.info("LDAP Admin User: %s" %
1816 provision_backend.credentials.get_username())
1818 logger.info("LDAP Admin Password: %s" %
1819 provision_backend.credentials.get_password())
1821 if provision_backend.slapd_command_escaped is not None:
1822 # now display slapd_command_file.txt to show how slapd must be
1824 logger.info("Use later the following commandline to start slapd, then Samba:")
1825 logger.info(provision_backend.slapd_command_escaped)
1826 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1827 provision_backend.ldapdir)
1829 result = ProvisionResult()
1830 result.domaindn = domaindn
1831 result.paths = paths
1833 result.samdb = samdb
1837 def provision_become_dc(smbconf=None, targetdir=None,
1838 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1839 serverdn=None, domain=None, hostname=None, domainsid=None,
1840 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1841 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1842 root=None, nobody=None, users=None, wheel=None, backup=None,
1843 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1844 sitename=None, debuglevel=1):
1846 logger = logging.getLogger("provision")
1847 samba.set_debug_level(debuglevel)
1849 res = provision(logger, system_session(), None,
1850 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1851 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1852 configdn=configdn, serverdn=serverdn, domain=domain,
1853 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1854 machinepass=machinepass, serverrole="domain controller",
1856 res.lp.set("debuglevel", str(debuglevel))
1860 def create_phpldapadmin_config(path, ldapi_uri):
1861 """Create a PHP LDAP admin configuration file.
1863 :param path: Path to write the configuration to.
1865 setup_file(setup_path("phpldapadmin-config.php"), path,
1866 {"S4_LDAPI_URI": ldapi_uri})
1869 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1870 hostip, hostip6, hostname, realm, domainguid,
1872 """Write out a DNS zone file, from the info in the current database.
1874 :param paths: paths object
1875 :param dnsdomain: DNS Domain name
1876 :param domaindn: DN of the Domain
1877 :param hostip: Local IPv4 IP
1878 :param hostip6: Local IPv6 IP
1879 :param hostname: Local hostname
1880 :param realm: Realm name
1881 :param domainguid: GUID of the domain.
1882 :param ntdsguid: GUID of the hosts nTDSDSA record.
1884 assert isinstance(domainguid, str)
1886 if hostip6 is not None:
1887 hostip6_base_line = " IN AAAA " + hostip6
1888 hostip6_host_line = hostname + " IN AAAA " + hostip6
1889 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1891 hostip6_base_line = ""
1892 hostip6_host_line = ""
1893 gc_msdcs_ip6_line = ""
1895 if hostip is not None:
1896 hostip_base_line = " IN A " + hostip
1897 hostip_host_line = hostname + " IN A " + hostip
1898 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1900 hostip_base_line = ""
1901 hostip_host_line = ""
1902 gc_msdcs_ip_line = ""
1904 dns_dir = os.path.dirname(paths.dns)
1907 shutil.rmtree(dns_dir, True)
1911 os.mkdir(dns_dir, 0775)
1913 # we need to freeze the zone while we update the contents
1914 if targetdir is None:
1915 rndc = ' '.join(lp.get("rndc command"))
1916 os.system(rndc + " freeze " + lp.get("realm"))
1918 setup_file(setup_path("provision.zone"), paths.dns, {
1919 "HOSTNAME": hostname,
1920 "DNSDOMAIN": dnsdomain,
1922 "HOSTIP_BASE_LINE": hostip_base_line,
1923 "HOSTIP_HOST_LINE": hostip_host_line,
1924 "DOMAINGUID": domainguid,
1925 "DATESTRING": time.strftime("%Y%m%d%H"),
1926 "DEFAULTSITE": DEFAULTSITE,
1927 "NTDSGUID": ntdsguid,
1928 "HOSTIP6_BASE_LINE": hostip6_base_line,
1929 "HOSTIP6_HOST_LINE": hostip6_host_line,
1930 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1931 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1934 # note that we use no variable substitution on this file
1935 # the substitution is done at runtime by samba_dnsupdate
1936 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1938 # and the SPN update list
1939 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1941 if paths.bind_gid is not None:
1943 os.chown(dns_dir, -1, paths.bind_gid)
1944 os.chown(paths.dns, -1, paths.bind_gid)
1945 # chmod needed to cope with umask
1946 os.chmod(dns_dir, 0775)
1947 os.chmod(paths.dns, 0664)
1949 if not os.environ.has_key('SAMBA_SELFTEST'):
1950 logger.error("Failed to chown %s to bind gid %u" % (
1951 dns_dir, paths.bind_gid))
1953 if targetdir is None:
1954 os.system(rndc + " unfreeze " + lp.get("realm"))
1957 def create_dns_update_list(lp, logger, paths):
1958 """Write out a dns_update_list file"""
1959 # note that we use no variable substitution on this file
1960 # the substitution is done at runtime by samba_dnsupdate
1961 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1962 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1965 def create_named_conf(paths, realm, dnsdomain,
1967 """Write out a file containing zone statements suitable for inclusion in a
1968 named.conf file (including GSS-TSIG configuration).
1970 :param paths: all paths
1971 :param realm: Realm name
1972 :param dnsdomain: DNS Domain name
1973 :param private_dir: Path to private directory
1974 :param keytab_name: File name of DNS keytab file
1977 setup_file(setup_path("named.conf"), paths.namedconf, {
1978 "DNSDOMAIN": dnsdomain,
1980 "ZONE_FILE": paths.dns,
1981 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1982 "NAMED_CONF": paths.namedconf,
1983 "NAMED_CONF_UPDATE": paths.namedconf_update
1986 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1989 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
1991 """Write out a file containing zone statements suitable for inclusion in a
1992 named.conf file (including GSS-TSIG configuration).
1994 :param path: Path of the new named.conf file.
1995 :param realm: Realm name
1996 :param dnsdomain: DNS Domain name
1997 :param private_dir: Path to private directory
1998 :param keytab_name: File name of DNS keytab file
2000 setup_file(setup_path("named.txt"), path, {
2001 "DNSDOMAIN": dnsdomain,
2002 "DNSNAME" : dnsname,
2004 "DNS_KEYTAB": keytab_name,
2005 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2006 "PRIVATE_DIR": private_dir
2010 def create_krb5_conf(path, dnsdomain, hostname, realm):
2011 """Write out a file containing zone statements suitable for inclusion in a
2012 named.conf file (including GSS-TSIG configuration).
2014 :param path: Path of the new named.conf file.
2015 :param dnsdomain: DNS Domain name
2016 :param hostname: Local hostname
2017 :param realm: Realm name
2019 setup_file(setup_path("krb5.conf"), path, {
2020 "DNSDOMAIN": dnsdomain,
2021 "HOSTNAME": hostname,
2026 class ProvisioningError(Exception):
2027 """A generic provision error."""
2029 def __init__(self, value):
2033 return "ProvisioningError: " + self.value
2036 class InvalidNetbiosName(Exception):
2037 """A specified name was not a valid NetBIOS name."""
2038 def __init__(self, name):
2039 super(InvalidNetbiosName, self).__init__(
2040 "The name '%r' is not a valid NetBIOS name" % name)