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.samdb_url(), 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, "sam.ldb")
521 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
522 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
523 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
524 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
525 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
526 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
527 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
528 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
529 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
530 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
531 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
532 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
533 paths.phpldapadminconfig = os.path.join(paths.private_dir,
534 "phpldapadmin-config.php")
535 paths.hklm = "hklm.ldb"
536 paths.hkcr = "hkcr.ldb"
537 paths.hkcu = "hkcu.ldb"
538 paths.hku = "hku.ldb"
539 paths.hkpd = "hkpd.ldb"
540 paths.hkpt = "hkpt.ldb"
541 paths.sysvol = lp.get("path", "sysvol")
542 paths.netlogon = lp.get("path", "netlogon")
543 paths.smbconf = lp.configfile
547 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
548 serverrole=None, rootdn=None, domaindn=None, configdn=None,
549 schemadn=None, serverdn=None, sitename=None):
550 """Guess configuration settings to use."""
553 hostname = socket.gethostname().split(".")[0]
555 netbiosname = lp.get("netbios name")
556 if netbiosname is None:
557 netbiosname = hostname
558 # remove forbidden chars
560 for x in netbiosname:
561 if x.isalnum() or x in VALID_NETBIOS_CHARS:
562 newnbname = "%s%c" % (newnbname, x)
563 # force the length to be <16
564 netbiosname = newnbname[0:15]
565 assert netbiosname is not None
566 netbiosname = netbiosname.upper()
567 if not valid_netbios_name(netbiosname):
568 raise InvalidNetbiosName(netbiosname)
570 if dnsdomain is None:
571 dnsdomain = lp.get("realm")
572 if dnsdomain is None or dnsdomain == "":
573 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
575 dnsdomain = dnsdomain.lower()
577 if serverrole is None:
578 serverrole = lp.get("server role")
579 if serverrole is None:
580 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
582 serverrole = serverrole.lower()
584 realm = dnsdomain.upper()
586 if lp.get("realm") == "":
587 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
589 if lp.get("realm").upper() != realm:
590 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))
592 if lp.get("server role").lower() != serverrole:
593 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))
595 if serverrole == "domain controller":
597 # This will, for better or worse, default to 'WORKGROUP'
598 domain = lp.get("workgroup")
599 domain = domain.upper()
601 if lp.get("workgroup").upper() != domain:
602 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))
605 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
607 if domain == netbiosname:
608 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
612 domaindn = "DC=" + netbiosname
614 if not valid_netbios_name(domain):
615 raise InvalidNetbiosName(domain)
617 if hostname.upper() == realm:
618 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
619 if netbiosname.upper() == realm:
620 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
622 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
628 configdn = "CN=Configuration," + rootdn
630 schemadn = "CN=Schema," + configdn
635 names = ProvisionNames()
636 names.rootdn = rootdn
637 names.domaindn = domaindn
638 names.configdn = configdn
639 names.schemadn = schemadn
640 names.ldapmanagerdn = "CN=Manager," + rootdn
641 names.dnsdomain = dnsdomain
642 names.domain = domain
644 names.netbiosname = netbiosname
645 names.hostname = hostname
646 names.sitename = sitename
647 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
648 netbiosname, sitename, configdn)
653 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
654 targetdir, sid_generator="internal", eadb=False, lp=None):
655 """Create a new smb.conf file based on a couple of basic settings.
657 assert smbconf is not None
659 hostname = socket.gethostname().split(".")[0]
660 netbiosname = hostname.upper()
661 # remove forbidden chars
663 for x in netbiosname:
664 if x.isalnum() or x in VALID_NETBIOS_CHARS:
665 newnbname = "%s%c" % (newnbname, x)
666 #force the length to be <16
667 netbiosname = newnbname[0:15]
669 netbiosname = hostname.upper()
671 if serverrole is None:
672 serverrole = "standalone"
674 assert serverrole in ("domain controller", "member server", "standalone")
675 if serverrole == "domain controller":
677 elif serverrole == "member server":
678 smbconfsuffix = "member"
679 elif serverrole == "standalone":
680 smbconfsuffix = "standalone"
682 if sid_generator is None:
683 sid_generator = "internal"
685 assert domain is not None
686 domain = domain.upper()
688 assert realm is not None
689 realm = realm.upper()
692 lp = samba.param.LoadParm()
693 #Load non-existant file
694 if os.path.exists(smbconf):
696 if eadb and not lp.get("posix:eadb"):
697 if targetdir is not None:
698 privdir = os.path.join(targetdir, "private")
700 privdir = lp.get("private dir")
701 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
703 if targetdir is not None:
704 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
705 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
707 lp.set("lock dir", os.path.abspath(targetdir))
712 if sid_generator == "internal":
713 sid_generator_line = ""
715 sid_generator_line = "sid generator = " + sid_generator
717 sysvol = os.path.join(lp.get("lock dir"), "sysvol")
718 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
720 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
722 "NETBIOS_NAME": netbiosname,
725 "SERVERROLE": serverrole,
726 "NETLOGONPATH": netlogon,
727 "SYSVOLPATH": sysvol,
728 "SIDGENERATOR_LINE": sid_generator_line,
729 "PRIVATEDIR_LINE": privatedir_line,
730 "LOCKDIR_LINE": lockdir_line
733 # reload the smb.conf
736 # and dump it without any values that are the default
737 # this ensures that any smb.conf parameters that were set
738 # on the provision/join command line are set in the resulting smb.conf
739 f = open(smbconf, mode='w')
745 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
746 users_gid, wheel_gid):
747 """setup reasonable name mappings for sam names to unix names.
749 :param samdb: SamDB object.
750 :param idmap: IDmap db object.
751 :param sid: The domain sid.
752 :param domaindn: The domain DN.
753 :param root_uid: uid of the UNIX root user.
754 :param nobody_uid: uid of the UNIX nobody user.
755 :param users_gid: gid of the UNIX users group.
756 :param wheel_gid: gid of the UNIX wheel group.
758 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
759 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
761 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
762 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
765 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
766 provision_backend, names, schema, serverrole,
768 """Setup the partitions for the SAM database.
770 Alternatively, provision() may call this, and then populate the database.
772 :note: This will wipe the Sam Database!
774 :note: This function always removes the local SAM LDB file. The erase
775 parameter controls whether to erase the existing data, which
776 may not be stored locally but in LDAP.
779 assert session_info is not None
781 # We use options=["modules:"] to stop the modules loading - we
782 # just want to wipe and re-initialise the database, not start it up
785 os.unlink(samdb_path)
789 samdb = Ldb(url=samdb_path, session_info=session_info,
790 lp=lp, options=["modules:"])
792 ldap_backend_line = "# No LDAP backend"
793 if provision_backend.type is not "ldb":
794 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
796 samdb.transaction_start()
798 logger.info("Setting up sam.ldb partitions and settings")
799 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
800 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
801 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
802 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
803 "LDAP_BACKEND_LINE": ldap_backend_line,
807 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
808 "BACKEND_TYPE": provision_backend.type,
809 "SERVER_ROLE": serverrole
812 logger.info("Setting up sam.ldb rootDSE")
813 setup_samdb_rootdse(samdb, names)
815 samdb.transaction_cancel()
818 samdb.transaction_commit()
821 def secretsdb_self_join(secretsdb, domain,
822 netbiosname, machinepass, domainsid=None,
823 realm=None, dnsdomain=None,
825 key_version_number=1,
826 secure_channel_type=SEC_CHAN_WKSTA):
827 """Add domain join-specific bits to a secrets database.
829 :param secretsdb: Ldb Handle to the secrets database
830 :param machinepass: Machine password
832 attrs = ["whenChanged",
839 if realm is not None:
840 if dnsdomain is None:
841 dnsdomain = realm.lower()
842 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
845 shortname = netbiosname.lower()
847 # We don't need to set msg["flatname"] here, because rdn_name will handle
848 # it, and it causes problems for modifies anyway
849 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
850 msg["secureChannelType"] = [str(secure_channel_type)]
851 msg["objectClass"] = ["top", "primaryDomain"]
852 if dnsname is not None:
853 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
854 msg["realm"] = [realm]
855 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
856 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
857 msg["privateKeytab"] = ["secrets.keytab"]
859 msg["secret"] = [machinepass]
860 msg["samAccountName"] = ["%s$" % netbiosname]
861 msg["secureChannelType"] = [str(secure_channel_type)]
862 if domainsid is not None:
863 msg["objectSid"] = [ndr_pack(domainsid)]
865 # This complex expression tries to ensure that we don't have more
866 # than one record for this SID, realm or netbios domain at a time,
867 # but we don't delete the old record that we are about to modify,
868 # because that would delete the keytab and previous password.
869 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
870 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
871 scope=ldb.SCOPE_ONELEVEL)
874 secretsdb.delete(del_msg.dn)
876 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
879 msg["priorSecret"] = [res[0]["secret"][0]]
880 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
883 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
888 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
894 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
895 secretsdb.modify(msg)
896 secretsdb.rename(res[0].dn, msg.dn)
898 spn = [ 'HOST/%s' % shortname ]
899 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
900 # we are a domain controller then we add servicePrincipalName
901 # entries for the keytab code to update.
902 spn.extend([ 'HOST/%s' % dnsname ])
903 msg["servicePrincipalName"] = spn
908 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
909 dnsdomain, dns_keytab_path, dnspass):
910 """Add DNS specific bits to a secrets database.
912 :param secretsdb: Ldb Handle to the secrets database
913 :param machinepass: Machine password
916 os.unlink(os.path.join(private_dir, dns_keytab_path))
920 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
922 "DNSDOMAIN": dnsdomain,
923 "DNS_KEYTAB": dns_keytab_path,
924 "DNSPASS_B64": b64encode(dnspass),
925 "HOSTNAME": names.hostname,
926 "DNSNAME" : '%s.%s' % (
927 names.netbiosname.lower(), names.dnsdomain.lower())
931 def setup_secretsdb(paths, session_info, backend_credentials, lp):
932 """Setup the secrets database.
934 :note: This function does not handle exceptions and transaction on purpose,
935 it's up to the caller to do this job.
937 :param path: Path to the secrets database.
938 :param session_info: Session info.
939 :param credentials: Credentials
940 :param lp: Loadparm context
941 :return: LDB handle for the created secrets database
943 if os.path.exists(paths.secrets):
944 os.unlink(paths.secrets)
946 keytab_path = os.path.join(paths.private_dir, paths.keytab)
947 if os.path.exists(keytab_path):
948 os.unlink(keytab_path)
950 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
951 if os.path.exists(dns_keytab_path):
952 os.unlink(dns_keytab_path)
956 secrets_ldb = Ldb(path, session_info=session_info,
959 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
960 secrets_ldb = Ldb(path, session_info=session_info,
962 secrets_ldb.transaction_start()
964 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
966 if (backend_credentials is not None and
967 backend_credentials.authentication_requested()):
968 if backend_credentials.get_bind_dn() is not None:
969 setup_add_ldif(secrets_ldb,
970 setup_path("secrets_simple_ldap.ldif"), {
971 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
972 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
975 setup_add_ldif(secrets_ldb,
976 setup_path("secrets_sasl_ldap.ldif"), {
977 "LDAPADMINUSER": backend_credentials.get_username(),
978 "LDAPADMINREALM": backend_credentials.get_realm(),
979 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
984 secrets_ldb.transaction_cancel()
988 def setup_privileges(path, session_info, lp):
989 """Setup the privileges database.
991 :param path: Path to the privileges database.
992 :param session_info: Session info.
993 :param credentials: Credentials
994 :param lp: Loadparm context
995 :return: LDB handle for the created secrets database
997 if os.path.exists(path):
999 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1000 privilege_ldb.erase()
1001 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1004 def setup_registry(path, session_info, lp):
1005 """Setup the registry.
1007 :param path: Path to the registry database
1008 :param session_info: Session information
1009 :param credentials: Credentials
1010 :param lp: Loadparm context
1012 reg = samba.registry.Registry()
1013 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1014 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1015 provision_reg = setup_path("provision.reg")
1016 assert os.path.exists(provision_reg)
1017 reg.diff_apply(provision_reg)
1020 def setup_idmapdb(path, session_info, lp):
1021 """Setup the idmap database.
1023 :param path: path to the idmap database
1024 :param session_info: Session information
1025 :param credentials: Credentials
1026 :param lp: Loadparm context
1028 if os.path.exists(path):
1031 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1033 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1037 def setup_samdb_rootdse(samdb, names):
1038 """Setup the SamDB rootdse.
1040 :param samdb: Sam Database handle
1042 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1043 "SCHEMADN": names.schemadn,
1044 "DOMAINDN": names.domaindn,
1045 "ROOTDN": names.rootdn,
1046 "CONFIGDN": names.configdn,
1047 "SERVERDN": names.serverdn,
1051 def setup_self_join(samdb, names, machinepass, dnspass,
1052 domainsid, next_rid, invocationid,
1053 policyguid, policyguid_dc, domainControllerFunctionality,
1055 """Join a host to its own domain."""
1056 assert isinstance(invocationid, str)
1057 if ntdsguid is not None:
1058 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1061 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1062 "CONFIGDN": names.configdn,
1063 "SCHEMADN": names.schemadn,
1064 "DOMAINDN": names.domaindn,
1065 "SERVERDN": names.serverdn,
1066 "INVOCATIONID": invocationid,
1067 "NETBIOSNAME": names.netbiosname,
1068 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1069 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1070 "DOMAINSID": str(domainsid),
1071 "DCRID": str(next_rid),
1072 "SAMBA_VERSION_STRING": version,
1073 "NTDSGUID": ntdsguid_line,
1074 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1075 domainControllerFunctionality)})
1077 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1078 "POLICYGUID": policyguid,
1079 "POLICYGUID_DC": policyguid_dc,
1080 "DNSDOMAIN": names.dnsdomain,
1081 "DOMAINDN": names.domaindn})
1083 # add the NTDSGUID based SPNs
1084 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1085 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1086 expression="", scope=ldb.SCOPE_BASE)
1087 assert isinstance(names.ntdsguid, str)
1089 # Setup fSMORoleOwner entries to point at the newly created DC entry
1090 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1091 "DOMAINDN": names.domaindn,
1092 "CONFIGDN": names.configdn,
1093 "SCHEMADN": names.schemadn,
1094 "DEFAULTSITE": names.sitename,
1095 "SERVERDN": names.serverdn,
1096 "NETBIOSNAME": names.netbiosname,
1097 "RIDALLOCATIONSTART": str(next_rid + 100),
1098 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1101 # This is partially Samba4 specific and should be replaced by the correct
1102 # DNS AD-style setup
1103 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1104 "DNSDOMAIN": names.dnsdomain,
1105 "DOMAINDN": names.domaindn,
1106 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1107 "HOSTNAME" : names.hostname,
1108 "DNSNAME" : '%s.%s' % (
1109 names.netbiosname.lower(), names.dnsdomain.lower())
1113 def getpolicypath(sysvolpath, dnsdomain, guid):
1114 """Return the physical path of policy given its guid.
1116 :param sysvolpath: Path to the sysvol folder
1117 :param dnsdomain: DNS name of the AD domain
1118 :param guid: The GUID of the policy
1119 :return: A string with the complete path to the policy folder
1123 guid = "{%s}" % guid
1124 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1128 def create_gpo_struct(policy_path):
1129 if not os.path.exists(policy_path):
1130 os.makedirs(policy_path, 0775)
1131 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1132 "[General]\r\nVersion=0")
1133 p = os.path.join(policy_path, "MACHINE")
1134 if not os.path.exists(p):
1135 os.makedirs(p, 0775)
1136 p = os.path.join(policy_path, "USER")
1137 if not os.path.exists(p):
1138 os.makedirs(p, 0775)
1141 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1142 """Create the default GPO for a domain
1144 :param sysvolpath: Physical path for the sysvol folder
1145 :param dnsdomain: DNS domain name of the AD domain
1146 :param policyguid: GUID of the default domain policy
1147 :param policyguid_dc: GUID of the default domain controler policy
1149 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1150 create_gpo_struct(policy_path)
1152 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1153 create_gpo_struct(policy_path)
1156 def setup_samdb(path, session_info, provision_backend, lp, names,
1157 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1158 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1159 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1161 """Setup a complete SAM Database.
1163 :note: This will wipe the main SAM database file!
1166 # Provision does not make much sense values larger than 1000000000
1167 # as the upper range of the rIDAvailablePool is 1073741823 and
1168 # we don't want to create a domain that cannot allocate rids.
1169 if next_rid < 1000 or next_rid > 1000000000:
1170 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1171 error += "the valid range is %u-%u. The default is %u." % (
1172 1000, 1000000000, 1000)
1173 raise ProvisioningError(error)
1175 # ATTENTION: Do NOT change these default values without discussion with the
1176 # team and/or release manager. They have a big impact on the whole program!
1177 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1179 if dom_for_fun_level is None:
1180 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1182 if dom_for_fun_level > domainControllerFunctionality:
1183 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!")
1185 domainFunctionality = dom_for_fun_level
1186 forestFunctionality = dom_for_fun_level
1188 # Also wipes the database
1189 setup_samdb_partitions(path, logger=logger, lp=lp,
1190 provision_backend=provision_backend, session_info=session_info,
1191 names=names, serverrole=serverrole, schema=schema)
1194 schema = Schema(domainsid, schemadn=names.schemadn)
1196 # Load the database, but don's load the global schema and don't connect
1198 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1199 credentials=provision_backend.credentials, lp=lp,
1200 global_schema=False, am_rodc=am_rodc)
1202 logger.info("Pre-loading the Samba 4 and AD schema")
1204 # Load the schema from the one we computed earlier
1205 samdb.set_schema(schema)
1207 # Set the NTDS settings DN manually - in order to have it already around
1208 # before the provisioned tree exists and we connect
1209 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1211 # And now we can connect to the DB - the schema won't be loaded from the
1215 if fill == FILL_DRS:
1218 samdb.transaction_start()
1220 # Set the domain functionality levels onto the database.
1221 # Various module (the password_hash module in particular) need
1222 # to know what level of AD we are emulating.
1224 # These will be fixed into the database via the database
1225 # modifictions below, but we need them set from the start.
1226 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1227 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1228 samdb.set_opaque_integer("domainControllerFunctionality",
1229 domainControllerFunctionality)
1231 samdb.set_domain_sid(str(domainsid))
1232 samdb.set_invocation_id(invocationid)
1234 logger.info("Adding DomainDN: %s" % names.domaindn)
1236 # impersonate domain admin
1237 admin_session_info = admin_session(lp, str(domainsid))
1238 samdb.set_session_info(admin_session_info)
1239 if domainguid is not None:
1240 domainguid_line = "objectGUID: %s\n-" % domainguid
1242 domainguid_line = ""
1244 descr = b64encode(get_domain_descriptor(domainsid))
1245 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1246 "DOMAINDN": names.domaindn,
1247 "DOMAINSID": str(domainsid),
1248 "DESCRIPTOR": descr,
1249 "DOMAINGUID": domainguid_line
1252 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1253 "DOMAINDN": names.domaindn,
1254 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1255 "NEXTRID": str(next_rid),
1256 "DEFAULTSITE": names.sitename,
1257 "CONFIGDN": names.configdn,
1258 "POLICYGUID": policyguid,
1259 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1260 "SAMBA_VERSION_STRING": version
1263 logger.info("Adding configuration container")
1264 descr = b64encode(get_config_descriptor(domainsid))
1265 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1266 "CONFIGDN": names.configdn,
1267 "DESCRIPTOR": descr,
1270 # Now register this container in the root of the forest
1271 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1272 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1275 # The LDIF here was created when the Schema object was constructed
1276 logger.info("Setting up sam.ldb schema")
1277 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1278 samdb.modify_ldif(schema.schema_dn_modify)
1279 samdb.write_prefixes_from_schema()
1280 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1281 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1282 {"SCHEMADN": names.schemadn})
1284 logger.info("Reopening sam.ldb with new schema")
1286 samdb.transaction_cancel()
1289 samdb.transaction_commit()
1291 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1292 credentials=provision_backend.credentials, lp=lp,
1293 global_schema=False, am_rodc=am_rodc)
1295 # Set the NTDS settings DN manually - in order to have it already around
1296 # before the provisioned tree exists and we connect
1297 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1300 samdb.transaction_start()
1302 samdb.invocation_id = invocationid
1304 logger.info("Setting up sam.ldb configuration data")
1305 descr = b64encode(get_sites_descriptor(domainsid))
1306 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1307 "CONFIGDN": names.configdn,
1308 "NETBIOSNAME": names.netbiosname,
1309 "DEFAULTSITE": names.sitename,
1310 "DNSDOMAIN": names.dnsdomain,
1311 "DOMAIN": names.domain,
1312 "SCHEMADN": names.schemadn,
1313 "DOMAINDN": names.domaindn,
1314 "SERVERDN": names.serverdn,
1315 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1316 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1317 "SITES_DESCRIPTOR": descr
1320 logger.info("Setting up display specifiers")
1321 display_specifiers_ldif = read_ms_ldif(
1322 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1323 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1324 {"CONFIGDN": names.configdn})
1325 check_all_substituted(display_specifiers_ldif)
1326 samdb.add_ldif(display_specifiers_ldif)
1328 logger.info("Adding users container")
1329 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1330 "DOMAINDN": names.domaindn})
1331 logger.info("Modifying users container")
1332 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1333 "DOMAINDN": names.domaindn})
1334 logger.info("Adding computers container")
1335 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1336 "DOMAINDN": names.domaindn})
1337 logger.info("Modifying computers container")
1338 setup_modify_ldif(samdb,
1339 setup_path("provision_computers_modify.ldif"), {
1340 "DOMAINDN": names.domaindn})
1341 logger.info("Setting up sam.ldb data")
1342 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1343 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1344 "DOMAINDN": names.domaindn,
1345 "NETBIOSNAME": names.netbiosname,
1346 "DEFAULTSITE": names.sitename,
1347 "CONFIGDN": names.configdn,
1348 "SERVERDN": names.serverdn,
1349 "RIDAVAILABLESTART": str(next_rid + 600),
1350 "POLICYGUID_DC": policyguid_dc
1353 setup_modify_ldif(samdb,
1354 setup_path("provision_basedn_references.ldif"), {
1355 "DOMAINDN": names.domaindn})
1357 setup_modify_ldif(samdb,
1358 setup_path("provision_configuration_references.ldif"), {
1359 "CONFIGDN": names.configdn,
1360 "SCHEMADN": names.schemadn})
1361 if fill == FILL_FULL:
1362 logger.info("Setting up sam.ldb users and groups")
1363 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1364 "DOMAINDN": names.domaindn,
1365 "DOMAINSID": str(domainsid),
1366 "CONFIGDN": names.configdn,
1367 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1368 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1371 logger.info("Setting up self join")
1372 setup_self_join(samdb, names=names, invocationid=invocationid,
1374 machinepass=machinepass,
1375 domainsid=domainsid,
1377 policyguid=policyguid,
1378 policyguid_dc=policyguid_dc,
1379 domainControllerFunctionality=domainControllerFunctionality,
1382 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1383 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1384 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1385 assert isinstance(names.ntdsguid, str)
1387 samdb.transaction_cancel()
1390 samdb.transaction_commit()
1395 FILL_NT4SYNC = "NT4SYNC"
1397 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1398 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)"
1401 def set_dir_acl(path, acl, lp, domsid):
1402 setntacl(lp, path, acl, domsid)
1403 for root, dirs, files in os.walk(path, topdown=False):
1405 setntacl(lp, os.path.join(root, name), acl, domsid)
1407 setntacl(lp, os.path.join(root, name), acl, domsid)
1410 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1411 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1414 :param sysvol: Physical path for the sysvol folder
1415 :param dnsdomain: The DNS name of the domain
1416 :param domainsid: The SID of the domain
1417 :param domaindn: The DN of the domain (ie. DC=...)
1418 :param samdb: An LDB object on the SAM db
1419 :param lp: an LP object
1422 # Set ACL for GPO root folder
1423 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1424 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1426 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1427 attrs=["cn", "nTSecurityDescriptor"],
1428 expression="", scope=ldb.SCOPE_ONELEVEL)
1431 acl = ndr_unpack(security.descriptor,
1432 str(policy["nTSecurityDescriptor"])).as_sddl()
1433 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1434 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1438 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1440 """Set the ACL for the sysvol share and the subfolders
1442 :param samdb: An LDB object on the SAM db
1443 :param netlogon: Physical path for the netlogon folder
1444 :param sysvol: Physical path for the sysvol folder
1445 :param gid: The GID of the "Domain adminstrators" group
1446 :param domainsid: The SID of the domain
1447 :param dnsdomain: The DNS name of the domain
1448 :param domaindn: The DN of the domain (ie. DC=...)
1452 os.chown(sysvol, -1, gid)
1458 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1459 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1460 for root, dirs, files in os.walk(sysvol, topdown=False):
1463 os.chown(os.path.join(root, name), -1, gid)
1464 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1467 os.chown(os.path.join(root, name), -1, gid)
1468 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1470 # Set acls on Policy folder and policies folders
1471 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1474 def provision(logger, session_info, credentials, smbconf=None,
1475 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1476 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1477 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1478 next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1479 domainguid=None, policyguid=None, policyguid_dc=None,
1480 invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1481 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1482 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1483 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1484 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1485 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1489 :note: caution, this wipes all existing data!
1492 if domainsid is None:
1493 domainsid = security.random_sid()
1495 domainsid = security.dom_sid(domainsid)
1497 # create/adapt the group policy GUIDs
1498 # Default GUID for default policy are described at
1499 # "How Core Group Policy Works"
1500 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1501 if policyguid is None:
1502 policyguid = DEFAULT_POLICY_GUID
1503 policyguid = policyguid.upper()
1504 if policyguid_dc is None:
1505 policyguid_dc = DEFAULT_DC_POLICY_GUID
1506 policyguid_dc = policyguid_dc.upper()
1508 if adminpass is None:
1509 adminpass = samba.generate_random_password(12, 32)
1510 if krbtgtpass is None:
1511 krbtgtpass = samba.generate_random_password(128, 255)
1512 if machinepass is None:
1513 machinepass = samba.generate_random_password(128, 255)
1515 dnspass = samba.generate_random_password(128, 255)
1516 if ldapadminpass is None:
1517 # Make a new, random password between Samba and it's LDAP server
1518 ldapadminpass=samba.generate_random_password(128, 255)
1520 if backend_type is None:
1521 backend_type = "ldb"
1523 sid_generator = "internal"
1524 if backend_type == "fedora-ds":
1525 sid_generator = "backend"
1527 root_uid = findnss_uid([root or "root"])
1528 nobody_uid = findnss_uid([nobody or "nobody"])
1529 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1531 wheel_gid = findnss_gid(["wheel", "adm"])
1533 wheel_gid = findnss_gid([wheel])
1535 bind_gid = findnss_gid(["bind", "named"])
1539 if targetdir is not None:
1540 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1541 elif smbconf is None:
1542 smbconf = samba.param.default_path()
1543 if not os.path.exists(os.path.dirname(smbconf)):
1544 os.makedirs(os.path.dirname(smbconf))
1546 # only install a new smb.conf if there isn't one there already
1547 if os.path.exists(smbconf):
1548 # if Samba Team members can't figure out the weird errors
1549 # loading an empty smb.conf gives, then we need to be smarter.
1550 # Pretend it just didn't exist --abartlet
1551 data = open(smbconf, 'r').read()
1552 data = data.lstrip()
1553 if data is None or data == "":
1554 make_smbconf(smbconf, hostname, domain, realm,
1555 serverrole, targetdir, sid_generator, useeadb,
1558 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1559 targetdir, sid_generator, useeadb, lp=lp)
1562 lp = samba.param.LoadParm()
1564 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1565 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1566 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1568 paths = provision_paths_from_lp(lp, names.dnsdomain)
1570 paths.bind_gid = bind_gid
1573 logger.info("Looking up IPv4 addresses")
1574 hostips = samba.interface_ips(lp, False)
1575 if len(hostips) == 0:
1576 logger.warning("No external IPv4 address has been found. Using loopback.")
1577 hostip = '127.0.0.1'
1580 if len(hostips) > 1:
1581 logger.warning("More than one IPv4 address found. Using %s.",
1584 if serverrole is None:
1585 serverrole = lp.get("server role")
1587 assert serverrole in ("domain controller", "member server", "standalone")
1588 if invocationid is None:
1589 invocationid = str(uuid.uuid4())
1591 if not os.path.exists(paths.private_dir):
1592 os.mkdir(paths.private_dir)
1593 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1594 os.mkdir(os.path.join(paths.private_dir, "tls"))
1596 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1598 schema = Schema(domainsid, invocationid=invocationid,
1599 schemadn=names.schemadn)
1601 if backend_type == "ldb":
1602 provision_backend = LDBBackend(backend_type, paths=paths,
1603 lp=lp, credentials=credentials,
1604 names=names, logger=logger)
1605 elif backend_type == "existing":
1606 provision_backend = ExistingBackend(backend_type, paths=paths,
1607 lp=lp, credentials=credentials,
1608 names=names, logger=logger,
1609 ldap_backend_forced_uri=ldap_backend_forced_uri)
1610 elif backend_type == "fedora-ds":
1611 provision_backend = FDSBackend(backend_type, paths=paths,
1612 lp=lp, credentials=credentials,
1613 names=names, logger=logger, domainsid=domainsid,
1614 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1615 slapd_path=slapd_path,
1616 ldap_backend_extra_port=ldap_backend_extra_port,
1617 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1618 setup_ds_path=setup_ds_path,
1619 ldap_backend_forced_uri=ldap_backend_forced_uri)
1620 elif backend_type == "openldap":
1621 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1622 lp=lp, credentials=credentials,
1623 names=names, logger=logger, domainsid=domainsid,
1624 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1625 slapd_path=slapd_path,
1626 ldap_backend_extra_port=ldap_backend_extra_port,
1627 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1629 ldap_backend_forced_uri=ldap_backend_forced_uri)
1631 raise ValueError("Unknown LDAP backend type selected")
1633 provision_backend.init()
1634 provision_backend.start()
1636 # only install a new shares config db if there is none
1637 if not os.path.exists(paths.shareconf):
1638 logger.info("Setting up share.ldb")
1639 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1641 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1643 logger.info("Setting up secrets.ldb")
1644 secrets_ldb = setup_secretsdb(paths,
1645 session_info=session_info,
1646 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1649 logger.info("Setting up the registry")
1650 setup_registry(paths.hklm, session_info,
1653 logger.info("Setting up the privileges database")
1654 setup_privileges(paths.privilege, session_info, lp=lp)
1656 logger.info("Setting up idmap db")
1657 idmap = setup_idmapdb(paths.idmapdb,
1658 session_info=session_info, lp=lp)
1660 logger.info("Setting up SAM db")
1661 samdb = setup_samdb(paths.samdb, session_info,
1662 provision_backend, lp, names, logger=logger,
1663 domainsid=domainsid, schema=schema, domainguid=domainguid,
1664 policyguid=policyguid, policyguid_dc=policyguid_dc,
1665 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1666 invocationid=invocationid, machinepass=machinepass,
1667 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1668 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1671 if serverrole == "domain controller":
1672 if paths.netlogon is None:
1673 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1674 logger.info("Please either remove %s or see the template at %s" %
1675 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1676 assert paths.netlogon is not None
1678 if paths.sysvol is None:
1679 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1680 " are configuring a DC.")
1681 logger.info("Please either remove %s or see the template at %s" %
1682 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1683 assert paths.sysvol is not None
1685 if not os.path.isdir(paths.netlogon):
1686 os.makedirs(paths.netlogon, 0755)
1688 if samdb_fill == FILL_FULL:
1689 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1690 root_uid=root_uid, nobody_uid=nobody_uid,
1691 users_gid=users_gid, wheel_gid=wheel_gid)
1693 if serverrole == "domain controller":
1694 # Set up group policies (domain policy and domain controller
1696 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1698 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1699 domainsid, names.dnsdomain, names.domaindn, lp)
1701 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1702 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1704 secretsdb_self_join(secrets_ldb, domain=names.domain,
1705 realm=names.realm, dnsdomain=names.dnsdomain,
1706 netbiosname=names.netbiosname, domainsid=domainsid,
1707 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1709 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1710 # In future, this might be determined from some configuration
1711 kerberos_enctypes = str(ENC_ALL_TYPES)
1714 msg = ldb.Message(ldb.Dn(samdb,
1715 samdb.searchone("distinguishedName",
1716 expression="samAccountName=%s$" % names.netbiosname,
1717 scope=ldb.SCOPE_SUBTREE)))
1718 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1719 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1720 name="msDS-SupportedEncryptionTypes")
1722 except ldb.LdbError, (enum, estr):
1723 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1724 # It might be that this attribute does not exist in this schema
1727 if serverrole == "domain controller":
1728 secretsdb_setup_dns(secrets_ldb, names,
1729 paths.private_dir, realm=names.realm,
1730 dnsdomain=names.dnsdomain,
1731 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1733 domainguid = samdb.searchone(basedn=domaindn,
1734 attribute="objectGUID")
1735 assert isinstance(domainguid, str)
1737 # Only make a zone file on the first DC, it should be
1738 # replicated with DNS replication
1739 create_zone_file(lp, logger, paths, targetdir,
1740 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1741 hostname=names.hostname, realm=names.realm,
1742 domainguid=domainguid, ntdsguid=names.ntdsguid)
1744 create_named_conf(paths, realm=names.realm,
1745 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1747 create_named_txt(paths.namedtxt,
1748 realm=names.realm, dnsdomain=names.dnsdomain,
1749 dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1750 private_dir=paths.private_dir,
1751 keytab_name=paths.dns_keytab)
1752 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1753 logger.info("and %s for further documentation required for secure DNS "
1754 "updates", paths.namedtxt)
1756 lastProvisionUSNs = get_last_provision_usn(samdb)
1757 maxUSN = get_max_usn(samdb, str(names.rootdn))
1758 if lastProvisionUSNs is not None:
1759 update_provision_usn(samdb, 0, maxUSN, 1)
1761 set_provision_usn(samdb, 0, maxUSN)
1763 create_krb5_conf(paths.krb5conf,
1764 dnsdomain=names.dnsdomain, hostname=names.hostname,
1766 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1767 "generated at %s", paths.krb5conf)
1769 if serverrole == "domain controller":
1770 create_dns_update_list(lp, logger, paths)
1772 provision_backend.post_setup()
1773 provision_backend.shutdown()
1775 create_phpldapadmin_config(paths.phpldapadminconfig,
1778 secrets_ldb.transaction_cancel()
1781 # Now commit the secrets.ldb to disk
1782 secrets_ldb.transaction_commit()
1784 # the commit creates the dns.keytab, now chown it
1785 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1786 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1788 os.chmod(dns_keytab_path, 0640)
1789 os.chown(dns_keytab_path, -1, paths.bind_gid)
1791 if not os.environ.has_key('SAMBA_SELFTEST'):
1792 logger.info("Failed to chown %s to bind gid %u",
1793 dns_keytab_path, paths.bind_gid)
1796 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1797 paths.phpldapadminconfig)
1799 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1800 logger.info("Server Role: %s" % serverrole)
1801 logger.info("Hostname: %s" % names.hostname)
1802 logger.info("NetBIOS Domain: %s" % names.domain)
1803 logger.info("DNS Domain: %s" % names.dnsdomain)
1804 logger.info("DOMAIN SID: %s" % str(domainsid))
1805 if samdb_fill == FILL_FULL:
1806 logger.info("Admin password: %s" % adminpass)
1807 if provision_backend.type is not "ldb":
1808 if provision_backend.credentials.get_bind_dn() is not None:
1809 logger.info("LDAP Backend Admin DN: %s" %
1810 provision_backend.credentials.get_bind_dn())
1812 logger.info("LDAP Admin User: %s" %
1813 provision_backend.credentials.get_username())
1815 logger.info("LDAP Admin Password: %s" %
1816 provision_backend.credentials.get_password())
1818 if provision_backend.slapd_command_escaped is not None:
1819 # now display slapd_command_file.txt to show how slapd must be
1821 logger.info("Use later the following commandline to start slapd, then Samba:")
1822 logger.info(provision_backend.slapd_command_escaped)
1823 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1824 provision_backend.ldapdir)
1826 result = ProvisionResult()
1827 result.domaindn = domaindn
1828 result.paths = paths
1830 result.samdb = samdb
1834 def provision_become_dc(smbconf=None, targetdir=None,
1835 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1836 serverdn=None, domain=None, hostname=None, domainsid=None,
1837 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1838 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1839 root=None, nobody=None, users=None, wheel=None, backup=None,
1840 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1841 sitename=None, debuglevel=1):
1843 logger = logging.getLogger("provision")
1844 samba.set_debug_level(debuglevel)
1846 res = provision(logger, system_session(), None,
1847 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1848 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1849 configdn=configdn, serverdn=serverdn, domain=domain,
1850 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1851 machinepass=machinepass, serverrole="domain controller",
1853 res.lp.set("debuglevel", str(debuglevel))
1857 def create_phpldapadmin_config(path, ldapi_uri):
1858 """Create a PHP LDAP admin configuration file.
1860 :param path: Path to write the configuration to.
1862 setup_file(setup_path("phpldapadmin-config.php"), path,
1863 {"S4_LDAPI_URI": ldapi_uri})
1866 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1867 hostip, hostip6, hostname, realm, domainguid,
1869 """Write out a DNS zone file, from the info in the current database.
1871 :param paths: paths object
1872 :param dnsdomain: DNS Domain name
1873 :param domaindn: DN of the Domain
1874 :param hostip: Local IPv4 IP
1875 :param hostip6: Local IPv6 IP
1876 :param hostname: Local hostname
1877 :param realm: Realm name
1878 :param domainguid: GUID of the domain.
1879 :param ntdsguid: GUID of the hosts nTDSDSA record.
1881 assert isinstance(domainguid, str)
1883 if hostip6 is not None:
1884 hostip6_base_line = " IN AAAA " + hostip6
1885 hostip6_host_line = hostname + " IN AAAA " + hostip6
1886 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1888 hostip6_base_line = ""
1889 hostip6_host_line = ""
1890 gc_msdcs_ip6_line = ""
1892 if hostip is not None:
1893 hostip_base_line = " IN A " + hostip
1894 hostip_host_line = hostname + " IN A " + hostip
1895 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1897 hostip_base_line = ""
1898 hostip_host_line = ""
1899 gc_msdcs_ip_line = ""
1901 dns_dir = os.path.dirname(paths.dns)
1904 shutil.rmtree(dns_dir, True)
1908 os.mkdir(dns_dir, 0775)
1910 # we need to freeze the zone while we update the contents
1911 if targetdir is None:
1912 rndc = ' '.join(lp.get("rndc command"))
1913 os.system(rndc + " freeze " + lp.get("realm"))
1915 setup_file(setup_path("provision.zone"), paths.dns, {
1916 "HOSTNAME": hostname,
1917 "DNSDOMAIN": dnsdomain,
1919 "HOSTIP_BASE_LINE": hostip_base_line,
1920 "HOSTIP_HOST_LINE": hostip_host_line,
1921 "DOMAINGUID": domainguid,
1922 "DATESTRING": time.strftime("%Y%m%d%H"),
1923 "DEFAULTSITE": DEFAULTSITE,
1924 "NTDSGUID": ntdsguid,
1925 "HOSTIP6_BASE_LINE": hostip6_base_line,
1926 "HOSTIP6_HOST_LINE": hostip6_host_line,
1927 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1928 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1931 # note that we use no variable substitution on this file
1932 # the substitution is done at runtime by samba_dnsupdate
1933 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1935 # and the SPN update list
1936 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1938 if paths.bind_gid is not None:
1940 os.chown(dns_dir, -1, paths.bind_gid)
1941 os.chown(paths.dns, -1, paths.bind_gid)
1942 # chmod needed to cope with umask
1943 os.chmod(dns_dir, 0775)
1944 os.chmod(paths.dns, 0664)
1946 if not os.environ.has_key('SAMBA_SELFTEST'):
1947 logger.error("Failed to chown %s to bind gid %u" % (
1948 dns_dir, paths.bind_gid))
1950 if targetdir is None:
1951 os.system(rndc + " unfreeze " + lp.get("realm"))
1954 def create_dns_update_list(lp, logger, paths):
1955 """Write out a dns_update_list file"""
1956 # note that we use no variable substitution on this file
1957 # the substitution is done at runtime by samba_dnsupdate
1958 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1959 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1962 def create_named_conf(paths, realm, dnsdomain,
1964 """Write out a file containing zone statements suitable for inclusion in a
1965 named.conf file (including GSS-TSIG configuration).
1967 :param paths: all paths
1968 :param realm: Realm name
1969 :param dnsdomain: DNS Domain name
1970 :param private_dir: Path to private directory
1971 :param keytab_name: File name of DNS keytab file
1974 setup_file(setup_path("named.conf"), paths.namedconf, {
1975 "DNSDOMAIN": dnsdomain,
1977 "ZONE_FILE": paths.dns,
1978 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1979 "NAMED_CONF": paths.namedconf,
1980 "NAMED_CONF_UPDATE": paths.namedconf_update
1983 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1986 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
1988 """Write out a file containing zone statements suitable for inclusion in a
1989 named.conf file (including GSS-TSIG configuration).
1991 :param path: Path of the new named.conf file.
1992 :param realm: Realm name
1993 :param dnsdomain: DNS Domain name
1994 :param private_dir: Path to private directory
1995 :param keytab_name: File name of DNS keytab file
1997 setup_file(setup_path("named.txt"), path, {
1998 "DNSDOMAIN": dnsdomain,
1999 "DNSNAME" : dnsname,
2001 "DNS_KEYTAB": keytab_name,
2002 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2003 "PRIVATE_DIR": private_dir
2007 def create_krb5_conf(path, dnsdomain, hostname, realm):
2008 """Write out a file containing zone statements suitable for inclusion in a
2009 named.conf file (including GSS-TSIG configuration).
2011 :param path: Path of the new named.conf file.
2012 :param dnsdomain: DNS Domain name
2013 :param hostname: Local hostname
2014 :param realm: Realm name
2016 setup_file(setup_path("krb5.conf"), path, {
2017 "DNSDOMAIN": dnsdomain,
2018 "HOSTNAME": hostname,
2023 class ProvisioningError(Exception):
2024 """A generic provision error."""
2026 def __init__(self, value):
2030 return "ProvisioningError: " + self.value
2033 class InvalidNetbiosName(Exception):
2034 """A specified name was not a valid NetBIOS name."""
2035 def __init__(self, name):
2036 super(InvalidNetbiosName, self).__init__(
2037 "The name '%r' is not a valid NetBIOS name" % name)