2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 __docformat__ = "restructuredText"
30 from base64 import b64encode
45 from samba.auth import system_session, admin_session
47 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
50 check_all_substituted,
57 from samba.dcerpc import security, misc
58 from samba.dcerpc.misc import (
62 from samba.dsdb import (
63 DS_DOMAIN_FUNCTION_2003,
64 DS_DOMAIN_FUNCTION_2008_R2,
67 from samba.idmap import IDmapDB
68 from samba.ms_display_specifiers import read_ms_ldif
69 from samba.ntacls import setntacl, dsacl2fsacl
70 from samba.ndr import ndr_pack, ndr_unpack
71 from samba.provision.backend import (
77 from samba.provision.sambadns import setup_ad_dns
81 from samba.schema import Schema
82 from samba.samdb import SamDB
83 from samba.dbchecker import dbcheck
86 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
87 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
88 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
89 DEFAULTSITE = "Default-First-Site-Name"
90 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
94 """Return an absolute path to the provision tempate file specified by file"""
95 return os.path.join(samba.param.setup_dir(), file)
97 # Descriptors of naming contexts and other important objects
99 # "get_schema_descriptor" is located in "schema.py"
101 def get_config_descriptor(domain_sid):
102 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
103 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
104 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
105 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
106 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
107 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
108 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
109 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
110 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
111 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
112 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
113 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
114 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
115 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
116 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
117 sec = security.descriptor.from_sddl(sddl, domain_sid)
121 def get_domain_descriptor(domain_sid):
122 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
129 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
130 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
131 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
132 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
133 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
134 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
135 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
136 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
137 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
138 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
140 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
141 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
142 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
143 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
144 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
145 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
146 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
147 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
148 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
149 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
150 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
151 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
152 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
153 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
154 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
155 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
156 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
157 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
158 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
159 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
160 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
163 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
165 "(A;;RPLCLORC;;;ED)" \
166 "(A;;RPLCLORC;;;AU)" \
167 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
168 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
169 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
170 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
171 sec = security.descriptor.from_sddl(sddl, domain_sid)
175 class ProvisionPaths(object):
178 self.shareconf = None
189 self.dns_keytab = None
192 self.private_dir = None
195 class ProvisionNames(object):
202 self.ldapmanagerdn = None
203 self.dnsdomain = None
205 self.netbiosname = None
211 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
212 """Get key provision parameters (realm, domain, ...) from a given provision
214 :param samdb: An LDB object connected to the sam.ldb file
215 :param secretsdb: An LDB object connected to the secrets.ldb file
216 :param idmapdb: An LDB object connected to the idmap.ldb file
217 :param paths: A list of path to provision object
218 :param smbconf: Path to the smb.conf file
219 :param lp: A LoadParm object
220 :return: A list of key provision parameters
222 names = ProvisionNames()
223 names.adminpass = None
225 # NT domain, kerberos realm, root dn, domain dn, domain dns name
226 names.domain = string.upper(lp.get("workgroup"))
227 names.realm = lp.get("realm")
228 names.dnsdomain = names.realm.lower()
229 basedn = samba.dn_from_dns_name(names.dnsdomain)
230 names.realm = string.upper(names.realm)
232 # Get the netbiosname first (could be obtained from smb.conf in theory)
233 res = secretsdb.search(expression="(flatname=%s)" %
234 names.domain,base="CN=Primary Domains",
235 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
236 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
238 names.smbconf = smbconf
240 # That's a bit simplistic but it's ok as long as we have only 3
242 current = samdb.search(expression="(objectClass=*)",
243 base="", scope=ldb.SCOPE_BASE,
244 attrs=["defaultNamingContext", "schemaNamingContext",
245 "configurationNamingContext","rootDomainNamingContext"])
247 names.configdn = current[0]["configurationNamingContext"]
248 configdn = str(names.configdn)
249 names.schemadn = current[0]["schemaNamingContext"]
250 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
251 current[0]["defaultNamingContext"][0]))):
252 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
253 "is not the same ..." % (paths.samdb,
254 str(current[0]["defaultNamingContext"][0]),
255 paths.smbconf, basedn)))
257 names.domaindn=current[0]["defaultNamingContext"]
258 names.rootdn=current[0]["rootDomainNamingContext"]
260 res3 = samdb.search(expression="(objectClass=site)",
261 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
262 names.sitename = str(res3[0]["cn"])
264 # dns hostname and server dn
265 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
266 base="OU=Domain Controllers,%s" % basedn,
267 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
268 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
270 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
271 attrs=[], base=configdn)
272 names.serverdn = server_res[0].dn
274 # invocation id/objectguid
275 res5 = samdb.search(expression="(objectClass=*)",
276 base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
277 attrs=["invocationID", "objectGUID"])
278 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
279 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
282 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
283 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
284 "objectSid","msDS-Behavior-Version" ])
285 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
286 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
287 if res6[0].get("msDS-Behavior-Version") is None or \
288 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
289 names.domainlevel = DS_DOMAIN_FUNCTION_2000
291 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
294 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
295 base="CN=Policies,CN=System," + basedn,
296 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
297 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
299 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
301 base="CN=Policies,CN=System," + basedn,
302 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
304 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
306 names.policyid_dc = None
307 res9 = idmapdb.search(expression="(cn=%s)" %
308 (security.SID_BUILTIN_ADMINISTRATORS),
311 names.wheel_gid = res9[0]["xidNumber"]
313 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
316 def update_provision_usn(samdb, low, high, id, replace=False):
317 """Update the field provisionUSN in sam.ldb
319 This field is used to track range of USN modified by provision and
321 This value is used afterward by next provision to figure out if
322 the field have been modified since last provision.
324 :param samdb: An LDB object connect to sam.ldb
325 :param low: The lowest USN modified by this upgrade
326 :param high: The highest USN modified by this upgrade
327 :param id: The invocation id of the samba's dc
328 :param replace: A boolean indicating if the range should replace any
329 existing one or appended (default)
334 entry = samdb.search(base="@PROVISION",
335 scope=ldb.SCOPE_BASE,
336 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
337 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
338 if not re.search(';', e):
339 e = "%s;%s" % (e, id)
342 tab.append("%s-%s;%s" % (low, high, id))
343 delta = ldb.Message()
344 delta.dn = ldb.Dn(samdb, "@PROVISION")
345 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
346 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
347 entry = samdb.search(expression='provisionnerID=*',
348 base="@PROVISION", scope=ldb.SCOPE_BASE,
349 attrs=["provisionnerID"])
350 if len(entry) == 0 or len(entry[0]) == 0:
351 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
355 def set_provision_usn(samdb, low, high, id):
356 """Set the field provisionUSN in sam.ldb
357 This field is used to track range of USN modified by provision and
359 This value is used afterward by next provision to figure out if
360 the field have been modified since last provision.
362 :param samdb: An LDB object connect to sam.ldb
363 :param low: The lowest USN modified by this upgrade
364 :param high: The highest USN modified by this upgrade
365 :param id: The invocationId of the provision"""
368 tab.append("%s-%s;%s" % (low, high, id))
370 delta = ldb.Message()
371 delta.dn = ldb.Dn(samdb, "@PROVISION")
372 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
373 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
377 def get_max_usn(samdb,basedn):
378 """ This function return the biggest USN present in the provision
380 :param samdb: A LDB object pointing to the sam.ldb
381 :param basedn: A string containing the base DN of the provision
383 :return: The biggest USN in the provision"""
385 res = samdb.search(expression="objectClass=*",base=basedn,
386 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
387 controls=["search_options:1:2",
388 "server_sort:1:1:uSNChanged",
389 "paged_results:1:1"])
390 return res[0]["uSNChanged"]
393 def get_last_provision_usn(sam):
394 """Get USNs ranges modified by a provision or an upgradeprovision
396 :param sam: An LDB object pointing to the sam.ldb
397 :return: a dictionnary which keys are invocation id and values are an array
398 of integer representing the different ranges
401 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
402 base="@PROVISION", scope=ldb.SCOPE_BASE,
403 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
404 except ldb.LdbError, (ecode, emsg):
405 if ecode == ldb.ERR_NO_SUCH_OBJECT:
412 if entry[0].get("provisionnerID"):
413 for e in entry[0]["provisionnerID"]:
415 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
416 tab1 = str(r).split(';')
421 if (len(myids) > 0 and id not in myids):
423 tab2 = p.split(tab1[0])
424 if range.get(id) == None:
426 range[id].append(tab2[0])
427 range[id].append(tab2[1])
433 class ProvisionResult(object):
444 def check_install(lp, session_info, credentials):
445 """Check whether the current install seems ok.
447 :param lp: Loadparm context
448 :param session_info: Session information
449 :param credentials: Credentials
451 if lp.get("realm") == "":
452 raise Exception("Realm empty")
453 samdb = Ldb(lp.samdb_url(), session_info=session_info,
454 credentials=credentials, lp=lp)
455 if len(samdb.search("(cn=Administrator)")) != 1:
456 raise ProvisioningError("No administrator account found")
459 def findnss(nssfn, names):
460 """Find a user or group from a list of possibilities.
462 :param nssfn: NSS Function to try (should raise KeyError if not found)
463 :param names: Names to check.
464 :return: Value return by first names list.
471 raise KeyError("Unable to find user/group in %r" % names)
474 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
475 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
478 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
479 """Setup a ldb in the private dir.
481 :param ldb: LDB file to import data into
482 :param ldif_path: Path of the LDIF file to load
483 :param subst_vars: Optional variables to subsitute in LDIF.
484 :param nocontrols: Optional list of controls, can be None for no controls
486 assert isinstance(ldif_path, str)
487 data = read_and_sub_file(ldif_path, subst_vars)
488 ldb.add_ldif(data, controls)
491 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
492 """Modify a ldb in the private dir.
494 :param ldb: LDB object.
495 :param ldif_path: LDIF file path.
496 :param subst_vars: Optional dictionary with substitution variables.
498 data = read_and_sub_file(ldif_path, subst_vars)
499 ldb.modify_ldif(data, controls)
502 def setup_ldb(ldb, ldif_path, subst_vars):
503 """Import a LDIF a file into a LDB handle, optionally substituting
506 :note: Either all LDIF data will be added or none (using transactions).
508 :param ldb: LDB file to import into.
509 :param ldif_path: Path to the LDIF file.
510 :param subst_vars: Dictionary with substitution variables.
512 assert ldb is not None
513 ldb.transaction_start()
515 setup_add_ldif(ldb, ldif_path, subst_vars)
517 ldb.transaction_cancel()
520 ldb.transaction_commit()
523 def provision_paths_from_lp(lp, dnsdomain):
524 """Set the default paths for provisioning.
526 :param lp: Loadparm context.
527 :param dnsdomain: DNS Domain name
529 paths = ProvisionPaths()
530 paths.private_dir = lp.get("private dir")
532 # This is stored without path prefix for the "privateKeytab" attribute in
533 # "secrets_dns.ldif".
534 paths.dns_keytab = "dns.keytab"
535 paths.keytab = "secrets.keytab"
537 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
538 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
539 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
540 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
541 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
542 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
543 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
544 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
545 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
546 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
547 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
548 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
549 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
550 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
551 paths.phpldapadminconfig = os.path.join(paths.private_dir,
552 "phpldapadmin-config.php")
553 paths.hklm = "hklm.ldb"
554 paths.hkcr = "hkcr.ldb"
555 paths.hkcu = "hkcu.ldb"
556 paths.hku = "hku.ldb"
557 paths.hkpd = "hkpd.ldb"
558 paths.hkpt = "hkpt.ldb"
559 paths.sysvol = lp.get("path", "sysvol")
560 paths.netlogon = lp.get("path", "netlogon")
561 paths.smbconf = lp.configfile
565 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
566 serverrole=None, rootdn=None, domaindn=None, configdn=None,
567 schemadn=None, serverdn=None, sitename=None):
568 """Guess configuration settings to use."""
571 hostname = socket.gethostname().split(".")[0]
573 netbiosname = lp.get("netbios name")
574 if netbiosname is None:
575 netbiosname = hostname
576 # remove forbidden chars
578 for x in netbiosname:
579 if x.isalnum() or x in VALID_NETBIOS_CHARS:
580 newnbname = "%s%c" % (newnbname, x)
581 # force the length to be <16
582 netbiosname = newnbname[0:15]
583 assert netbiosname is not None
584 netbiosname = netbiosname.upper()
585 if not valid_netbios_name(netbiosname):
586 raise InvalidNetbiosName(netbiosname)
588 if dnsdomain is None:
589 dnsdomain = lp.get("realm")
590 if dnsdomain is None or dnsdomain == "":
591 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
593 dnsdomain = dnsdomain.lower()
595 if serverrole is None:
596 serverrole = lp.get("server role")
597 if serverrole is None:
598 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
600 serverrole = serverrole.lower()
602 realm = dnsdomain.upper()
604 if lp.get("realm") == "":
605 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
607 if lp.get("realm").upper() != realm:
608 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))
610 if lp.get("server role").lower() != serverrole:
611 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))
613 if serverrole == "domain controller":
615 # This will, for better or worse, default to 'WORKGROUP'
616 domain = lp.get("workgroup")
617 domain = domain.upper()
619 if lp.get("workgroup").upper() != domain:
620 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))
623 domaindn = samba.dn_from_dns_name(dnsdomain)
625 if domain == netbiosname:
626 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
630 domaindn = "DC=" + netbiosname
632 if not valid_netbios_name(domain):
633 raise InvalidNetbiosName(domain)
635 if hostname.upper() == realm:
636 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
637 if netbiosname.upper() == realm:
638 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
640 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
646 configdn = "CN=Configuration," + rootdn
648 schemadn = "CN=Schema," + configdn
653 names = ProvisionNames()
654 names.rootdn = rootdn
655 names.domaindn = domaindn
656 names.configdn = configdn
657 names.schemadn = schemadn
658 names.ldapmanagerdn = "CN=Manager," + rootdn
659 names.dnsdomain = dnsdomain
660 names.domain = domain
662 names.netbiosname = netbiosname
663 names.hostname = hostname
664 names.sitename = sitename
665 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
666 netbiosname, sitename, configdn)
671 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
672 targetdir, sid_generator="internal", eadb=False, lp=None):
673 """Create a new smb.conf file based on a couple of basic settings.
675 assert smbconf is not None
677 hostname = socket.gethostname().split(".")[0]
678 netbiosname = hostname.upper()
679 # remove forbidden chars
681 for x in netbiosname:
682 if x.isalnum() or x in VALID_NETBIOS_CHARS:
683 newnbname = "%s%c" % (newnbname, x)
684 #force the length to be <16
685 netbiosname = newnbname[0:15]
687 netbiosname = hostname.upper()
689 if serverrole is None:
690 serverrole = "standalone"
692 assert serverrole in ("domain controller", "member server", "standalone")
693 if serverrole == "domain controller":
695 elif serverrole == "member server":
696 smbconfsuffix = "member"
697 elif serverrole == "standalone":
698 smbconfsuffix = "standalone"
700 if sid_generator is None:
701 sid_generator = "internal"
703 assert domain is not None
704 domain = domain.upper()
706 assert realm is not None
707 realm = realm.upper()
710 lp = samba.param.LoadParm()
711 #Load non-existant file
712 if os.path.exists(smbconf):
714 if eadb and not lp.get("posix:eadb"):
715 if targetdir is not None:
716 privdir = os.path.join(targetdir, "private")
718 privdir = lp.get("private dir")
719 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
721 if targetdir is not None:
722 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
723 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
724 statedir_line = "state directory = " + os.path.abspath(targetdir)
725 cachedir_line = "cache directory = " + os.path.abspath(targetdir)
727 lp.set("lock dir", os.path.abspath(targetdir))
728 lp.set("state directory", os.path.abspath(targetdir))
729 lp.set("cache directory", os.path.abspath(targetdir))
736 sysvol = os.path.join(lp.get("state directory"), "sysvol")
737 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
739 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
741 "NETBIOS_NAME": netbiosname,
744 "SERVERROLE": serverrole,
745 "NETLOGONPATH": netlogon,
746 "SYSVOLPATH": sysvol,
747 "PRIVATEDIR_LINE": privatedir_line,
748 "LOCKDIR_LINE": lockdir_line,
749 "STATEDIR_LINE": statedir_line,
750 "CACHEDIR_LINE": cachedir_line
753 # reload the smb.conf
756 # and dump it without any values that are the default
757 # this ensures that any smb.conf parameters that were set
758 # on the provision/join command line are set in the resulting smb.conf
759 f = open(smbconf, mode='w')
765 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
766 users_gid, wheel_gid):
767 """setup reasonable name mappings for sam names to unix names.
769 :param samdb: SamDB object.
770 :param idmap: IDmap db object.
771 :param sid: The domain sid.
772 :param domaindn: The domain DN.
773 :param root_uid: uid of the UNIX root user.
774 :param nobody_uid: uid of the UNIX nobody user.
775 :param users_gid: gid of the UNIX users group.
776 :param wheel_gid: gid of the UNIX wheel group.
778 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
779 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
781 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
782 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
785 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
786 provision_backend, names, schema, serverrole,
788 """Setup the partitions for the SAM database.
790 Alternatively, provision() may call this, and then populate the database.
792 :note: This will wipe the Sam Database!
794 :note: This function always removes the local SAM LDB file. The erase
795 parameter controls whether to erase the existing data, which
796 may not be stored locally but in LDAP.
799 assert session_info is not None
801 # We use options=["modules:"] to stop the modules loading - we
802 # just want to wipe and re-initialise the database, not start it up
805 os.unlink(samdb_path)
809 samdb = Ldb(url=samdb_path, session_info=session_info,
810 lp=lp, options=["modules:"])
812 ldap_backend_line = "# No LDAP backend"
813 if provision_backend.type is not "ldb":
814 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
816 samdb.transaction_start()
818 logger.info("Setting up sam.ldb partitions and settings")
819 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
820 "LDAP_BACKEND_LINE": ldap_backend_line
824 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
825 "BACKEND_TYPE": provision_backend.type,
826 "SERVER_ROLE": serverrole
829 logger.info("Setting up sam.ldb rootDSE")
830 setup_samdb_rootdse(samdb, names)
832 samdb.transaction_cancel()
835 samdb.transaction_commit()
838 def secretsdb_self_join(secretsdb, domain,
839 netbiosname, machinepass, domainsid=None,
840 realm=None, dnsdomain=None,
842 key_version_number=1,
843 secure_channel_type=SEC_CHAN_WKSTA):
844 """Add domain join-specific bits to a secrets database.
846 :param secretsdb: Ldb Handle to the secrets database
847 :param machinepass: Machine password
849 attrs = ["whenChanged",
856 if realm is not None:
857 if dnsdomain is None:
858 dnsdomain = realm.lower()
859 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
862 shortname = netbiosname.lower()
864 # We don't need to set msg["flatname"] here, because rdn_name will handle
865 # it, and it causes problems for modifies anyway
866 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
867 msg["secureChannelType"] = [str(secure_channel_type)]
868 msg["objectClass"] = ["top", "primaryDomain"]
869 if dnsname is not None:
870 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
871 msg["realm"] = [realm]
872 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
873 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
874 msg["privateKeytab"] = ["secrets.keytab"]
876 msg["secret"] = [machinepass]
877 msg["samAccountName"] = ["%s$" % netbiosname]
878 msg["secureChannelType"] = [str(secure_channel_type)]
879 if domainsid is not None:
880 msg["objectSid"] = [ndr_pack(domainsid)]
882 # This complex expression tries to ensure that we don't have more
883 # than one record for this SID, realm or netbios domain at a time,
884 # but we don't delete the old record that we are about to modify,
885 # because that would delete the keytab and previous password.
886 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
887 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
888 scope=ldb.SCOPE_ONELEVEL)
891 secretsdb.delete(del_msg.dn)
893 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
896 msg["priorSecret"] = [res[0]["secret"][0]]
897 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
900 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
905 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
911 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
912 secretsdb.modify(msg)
913 secretsdb.rename(res[0].dn, msg.dn)
915 spn = [ 'HOST/%s' % shortname ]
916 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
917 # we are a domain controller then we add servicePrincipalName
918 # entries for the keytab code to update.
919 spn.extend([ 'HOST/%s' % dnsname ])
920 msg["servicePrincipalName"] = spn
925 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
926 dnsdomain, dns_keytab_path, dnspass):
927 """Add DNS specific bits to a secrets database.
929 :param secretsdb: Ldb Handle to the secrets database
930 :param machinepass: Machine password
933 os.unlink(os.path.join(private_dir, dns_keytab_path))
937 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
939 "DNSDOMAIN": dnsdomain,
940 "DNS_KEYTAB": dns_keytab_path,
941 "DNSPASS_B64": b64encode(dnspass),
942 "HOSTNAME": names.hostname,
943 "DNSNAME" : '%s.%s' % (
944 names.netbiosname.lower(), names.dnsdomain.lower())
948 def setup_secretsdb(paths, session_info, backend_credentials, lp):
949 """Setup the secrets database.
951 :note: This function does not handle exceptions and transaction on purpose,
952 it's up to the caller to do this job.
954 :param path: Path to the secrets database.
955 :param session_info: Session info.
956 :param credentials: Credentials
957 :param lp: Loadparm context
958 :return: LDB handle for the created secrets database
960 if os.path.exists(paths.secrets):
961 os.unlink(paths.secrets)
963 keytab_path = os.path.join(paths.private_dir, paths.keytab)
964 if os.path.exists(keytab_path):
965 os.unlink(keytab_path)
967 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
968 if os.path.exists(dns_keytab_path):
969 os.unlink(dns_keytab_path)
973 secrets_ldb = Ldb(path, session_info=session_info,
976 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
977 secrets_ldb = Ldb(path, session_info=session_info,
979 secrets_ldb.transaction_start()
981 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
983 if (backend_credentials is not None and
984 backend_credentials.authentication_requested()):
985 if backend_credentials.get_bind_dn() is not None:
986 setup_add_ldif(secrets_ldb,
987 setup_path("secrets_simple_ldap.ldif"), {
988 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
989 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
992 setup_add_ldif(secrets_ldb,
993 setup_path("secrets_sasl_ldap.ldif"), {
994 "LDAPADMINUSER": backend_credentials.get_username(),
995 "LDAPADMINREALM": backend_credentials.get_realm(),
996 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
1001 secrets_ldb.transaction_cancel()
1005 def setup_privileges(path, session_info, lp):
1006 """Setup the privileges database.
1008 :param path: Path to the privileges database.
1009 :param session_info: Session info.
1010 :param credentials: Credentials
1011 :param lp: Loadparm context
1012 :return: LDB handle for the created secrets database
1014 if os.path.exists(path):
1016 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1017 privilege_ldb.erase()
1018 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1021 def setup_registry(path, session_info, lp):
1022 """Setup the registry.
1024 :param path: Path to the registry database
1025 :param session_info: Session information
1026 :param credentials: Credentials
1027 :param lp: Loadparm context
1029 reg = samba.registry.Registry()
1030 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1031 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1032 provision_reg = setup_path("provision.reg")
1033 assert os.path.exists(provision_reg)
1034 reg.diff_apply(provision_reg)
1037 def setup_idmapdb(path, session_info, lp):
1038 """Setup the idmap database.
1040 :param path: path to the idmap database
1041 :param session_info: Session information
1042 :param credentials: Credentials
1043 :param lp: Loadparm context
1045 if os.path.exists(path):
1048 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1050 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1054 def setup_samdb_rootdse(samdb, names):
1055 """Setup the SamDB rootdse.
1057 :param samdb: Sam Database handle
1059 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1060 "SCHEMADN": names.schemadn,
1061 "DOMAINDN": names.domaindn,
1062 "ROOTDN" : names.rootdn,
1063 "CONFIGDN": names.configdn,
1064 "SERVERDN": names.serverdn,
1068 def setup_self_join(samdb, names, fill, machinepass, dnspass,
1069 domainsid, next_rid, invocationid,
1070 policyguid, policyguid_dc, domainControllerFunctionality,
1071 ntdsguid, dc_rid=None):
1072 """Join a host to its own domain."""
1073 assert isinstance(invocationid, str)
1074 if ntdsguid is not None:
1075 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1082 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1083 "CONFIGDN": names.configdn,
1084 "SCHEMADN": names.schemadn,
1085 "DOMAINDN": names.domaindn,
1086 "SERVERDN": names.serverdn,
1087 "INVOCATIONID": invocationid,
1088 "NETBIOSNAME": names.netbiosname,
1089 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1090 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1091 "DOMAINSID": str(domainsid),
1092 "DCRID": str(dc_rid),
1093 "SAMBA_VERSION_STRING": version,
1094 "NTDSGUID": ntdsguid_line,
1095 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1096 domainControllerFunctionality)})
1098 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1099 "POLICYGUID": policyguid,
1100 "POLICYGUID_DC": policyguid_dc,
1101 "DNSDOMAIN": names.dnsdomain,
1102 "DOMAINDN": names.domaindn})
1104 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1105 if fill == FILL_FULL:
1106 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1107 "CONFIGDN": names.configdn,
1108 "SCHEMADN": names.schemadn,
1109 "DOMAINDN": names.domaindn,
1110 "SERVERDN": names.serverdn,
1111 "INVOCATIONID": invocationid,
1112 "NETBIOSNAME": names.netbiosname,
1113 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1114 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1115 "DOMAINSID": str(domainsid),
1116 "DCRID": str(dc_rid),
1117 "SAMBA_VERSION_STRING": version,
1118 "NTDSGUID": ntdsguid_line,
1119 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1120 domainControllerFunctionality)})
1122 # Setup fSMORoleOwner entries to point at the newly created DC entry
1123 setup_modify_ldif(samdb, setup_path("provision_self_join_modify_config.ldif"), {
1124 "CONFIGDN": names.configdn,
1125 "SCHEMADN": names.schemadn,
1126 "DEFAULTSITE": names.sitename,
1127 "SERVERDN": names.serverdn,
1130 # Setup fSMORoleOwner entries to point at the newly created DC entry
1131 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1132 "DOMAINDN": names.domaindn,
1133 "SERVERDN": names.serverdn,
1134 "NETBIOSNAME": names.netbiosname,
1135 "RIDALLOCATIONSTART": str(next_rid + 100),
1136 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1139 # This is Samba4 specific and should be replaced by the correct
1140 # DNS AD-style setup
1141 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1142 "DNSDOMAIN": names.dnsdomain,
1143 "DOMAINDN": names.domaindn,
1144 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1145 "HOSTNAME" : names.hostname,
1146 "DNSNAME" : '%s.%s' % (
1147 names.netbiosname.lower(), names.dnsdomain.lower())
1151 def getpolicypath(sysvolpath, dnsdomain, guid):
1152 """Return the physical path of policy given its guid.
1154 :param sysvolpath: Path to the sysvol folder
1155 :param dnsdomain: DNS name of the AD domain
1156 :param guid: The GUID of the policy
1157 :return: A string with the complete path to the policy folder
1161 guid = "{%s}" % guid
1162 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1166 def create_gpo_struct(policy_path):
1167 if not os.path.exists(policy_path):
1168 os.makedirs(policy_path, 0775)
1169 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1170 "[General]\r\nVersion=0")
1171 p = os.path.join(policy_path, "MACHINE")
1172 if not os.path.exists(p):
1173 os.makedirs(p, 0775)
1174 p = os.path.join(policy_path, "USER")
1175 if not os.path.exists(p):
1176 os.makedirs(p, 0775)
1179 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1180 """Create the default GPO for a domain
1182 :param sysvolpath: Physical path for the sysvol folder
1183 :param dnsdomain: DNS domain name of the AD domain
1184 :param policyguid: GUID of the default domain policy
1185 :param policyguid_dc: GUID of the default domain controler policy
1187 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1188 create_gpo_struct(policy_path)
1190 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1191 create_gpo_struct(policy_path)
1194 def setup_samdb(path, session_info, provision_backend, lp, names,
1195 logger, fill, serverrole,
1196 am_rodc=False, schema=None):
1197 """Setup a complete SAM Database.
1199 :note: This will wipe the main SAM database file!
1202 # Also wipes the database
1203 setup_samdb_partitions(path, logger=logger, lp=lp,
1204 provision_backend=provision_backend, session_info=session_info,
1205 names=names, serverrole=serverrole, schema=schema)
1208 schema = Schema(domainsid, schemadn=names.schemadn)
1210 # Load the database, but don's load the global schema and don't connect
1212 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1213 credentials=provision_backend.credentials, lp=lp,
1214 global_schema=False, am_rodc=am_rodc)
1216 logger.info("Pre-loading the Samba 4 and AD schema")
1218 # Load the schema from the one we computed earlier
1219 samdb.set_schema(schema)
1221 # Set the NTDS settings DN manually - in order to have it already around
1222 # before the provisioned tree exists and we connect
1223 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1225 # And now we can connect to the DB - the schema won't be loaded from the
1231 def fill_samdb(samdb, lp, names,
1232 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1233 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1234 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1235 next_rid=None, dc_rid=None):
1237 if next_rid is None:
1240 # Provision does not make much sense values larger than 1000000000
1241 # as the upper range of the rIDAvailablePool is 1073741823 and
1242 # we don't want to create a domain that cannot allocate rids.
1243 if next_rid < 1000 or next_rid > 1000000000:
1244 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1245 error += "the valid range is %u-%u. The default is %u." % (
1246 1000, 1000000000, 1000)
1247 raise ProvisioningError(error)
1249 # ATTENTION: Do NOT change these default values without discussion with the
1250 # team and/or release manager. They have a big impact on the whole program!
1251 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1253 if dom_for_fun_level is None:
1254 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1256 if dom_for_fun_level > domainControllerFunctionality:
1257 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!")
1259 domainFunctionality = dom_for_fun_level
1260 forestFunctionality = dom_for_fun_level
1262 # Set the NTDS settings DN manually - in order to have it already around
1263 # before the provisioned tree exists and we connect
1264 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1266 samdb.transaction_start()
1268 # Set the domain functionality levels onto the database.
1269 # Various module (the password_hash module in particular) need
1270 # to know what level of AD we are emulating.
1272 # These will be fixed into the database via the database
1273 # modifictions below, but we need them set from the start.
1274 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1275 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1276 samdb.set_opaque_integer("domainControllerFunctionality",
1277 domainControllerFunctionality)
1279 samdb.set_domain_sid(str(domainsid))
1280 samdb.set_invocation_id(invocationid)
1282 logger.info("Adding DomainDN: %s" % names.domaindn)
1284 # impersonate domain admin
1285 admin_session_info = admin_session(lp, str(domainsid))
1286 samdb.set_session_info(admin_session_info)
1287 if domainguid is not None:
1288 domainguid_line = "objectGUID: %s\n-" % domainguid
1290 domainguid_line = ""
1292 descr = b64encode(get_domain_descriptor(domainsid))
1293 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1294 "DOMAINDN": names.domaindn,
1295 "DOMAINSID": str(domainsid),
1296 "DESCRIPTOR": descr,
1297 "DOMAINGUID": domainguid_line
1300 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1301 "DOMAINDN": names.domaindn,
1302 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1303 "NEXTRID": str(next_rid),
1304 "DEFAULTSITE": names.sitename,
1305 "CONFIGDN": names.configdn,
1306 "POLICYGUID": policyguid,
1307 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1308 "SAMBA_VERSION_STRING": version
1311 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1312 if fill == FILL_FULL:
1313 logger.info("Adding configuration container")
1314 descr = b64encode(get_config_descriptor(domainsid))
1315 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1316 "CONFIGDN": names.configdn,
1317 "DESCRIPTOR": descr,
1320 # The LDIF here was created when the Schema object was constructed
1321 logger.info("Setting up sam.ldb schema")
1322 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1323 samdb.modify_ldif(schema.schema_dn_modify)
1324 samdb.write_prefixes_from_schema()
1325 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1326 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1327 {"SCHEMADN": names.schemadn})
1329 # Now register this container in the root of the forest
1330 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1331 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1335 samdb.transaction_cancel()
1338 samdb.transaction_commit()
1340 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1341 credentials=provision_backend.credentials, lp=lp,
1342 global_schema=False, am_rodc=am_rodc)
1344 # Set the NTDS settings DN manually - in order to have it already around
1345 # before the provisioned tree exists and we connect
1346 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1349 samdb.transaction_start()
1351 samdb.invocation_id = invocationid
1353 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1354 if fill == FILL_FULL:
1355 logger.info("Setting up sam.ldb configuration data")
1356 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1357 "CONFIGDN": names.configdn,
1358 "NETBIOSNAME": names.netbiosname,
1359 "DEFAULTSITE": names.sitename,
1360 "DNSDOMAIN": names.dnsdomain,
1361 "DOMAIN": names.domain,
1362 "SCHEMADN": names.schemadn,
1363 "DOMAINDN": names.domaindn,
1364 "SERVERDN": names.serverdn,
1365 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1366 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1369 logger.info("Setting up display specifiers")
1370 display_specifiers_ldif = read_ms_ldif(
1371 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1372 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1373 {"CONFIGDN": names.configdn})
1374 check_all_substituted(display_specifiers_ldif)
1375 samdb.add_ldif(display_specifiers_ldif)
1377 logger.info("Adding users container")
1378 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1379 "DOMAINDN": names.domaindn})
1380 logger.info("Modifying users container")
1381 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1382 "DOMAINDN": names.domaindn})
1383 logger.info("Adding computers container")
1384 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1385 "DOMAINDN": names.domaindn})
1386 logger.info("Modifying computers container")
1387 setup_modify_ldif(samdb,
1388 setup_path("provision_computers_modify.ldif"), {
1389 "DOMAINDN": names.domaindn})
1390 logger.info("Setting up sam.ldb data")
1391 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1392 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1393 "DOMAINDN": names.domaindn,
1394 "NETBIOSNAME": names.netbiosname,
1395 "DEFAULTSITE": names.sitename,
1396 "CONFIGDN": names.configdn,
1397 "SERVERDN": names.serverdn,
1398 "RIDAVAILABLESTART": str(next_rid + 600),
1399 "POLICYGUID_DC": policyguid_dc
1402 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1403 if fill == FILL_FULL:
1404 setup_modify_ldif(samdb,
1405 setup_path("provision_basedn_references.ldif"),
1406 {"DOMAINDN": names.domaindn})
1408 setup_modify_ldif(samdb,
1409 setup_path("provision_configuration_references.ldif"), {
1410 "CONFIGDN": names.configdn,
1411 "SCHEMADN": names.schemadn})
1412 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1413 logger.info("Setting up sam.ldb users and groups")
1414 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1415 "DOMAINDN": names.domaindn,
1416 "DOMAINSID": str(domainsid),
1417 "CONFIGDN": names.configdn,
1418 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1419 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1422 logger.info("Setting up self join")
1423 setup_self_join(samdb, names=names, fill=fill, invocationid=invocationid,
1425 machinepass=machinepass,
1426 domainsid=domainsid,
1429 policyguid=policyguid,
1430 policyguid_dc=policyguid_dc,
1431 domainControllerFunctionality=domainControllerFunctionality,
1434 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1435 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1436 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1437 assert isinstance(names.ntdsguid, str)
1439 samdb.transaction_cancel()
1442 samdb.transaction_commit()
1447 FILL_SUBDOMAIN = "SUBDOMAIN"
1448 FILL_NT4SYNC = "NT4SYNC"
1450 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1451 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)"
1454 def set_dir_acl(path, acl, lp, domsid):
1455 setntacl(lp, path, acl, domsid)
1456 for root, dirs, files in os.walk(path, topdown=False):
1458 setntacl(lp, os.path.join(root, name), acl, domsid)
1460 setntacl(lp, os.path.join(root, name), acl, domsid)
1463 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1464 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1467 :param sysvol: Physical path for the sysvol folder
1468 :param dnsdomain: The DNS name of the domain
1469 :param domainsid: The SID of the domain
1470 :param domaindn: The DN of the domain (ie. DC=...)
1471 :param samdb: An LDB object on the SAM db
1472 :param lp: an LP object
1475 # Set ACL for GPO root folder
1476 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1477 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1479 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1480 attrs=["cn", "nTSecurityDescriptor"],
1481 expression="", scope=ldb.SCOPE_ONELEVEL)
1484 acl = ndr_unpack(security.descriptor,
1485 str(policy["nTSecurityDescriptor"])).as_sddl()
1486 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1487 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1491 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1493 """Set the ACL for the sysvol share and the subfolders
1495 :param samdb: An LDB object on the SAM db
1496 :param netlogon: Physical path for the netlogon folder
1497 :param sysvol: Physical path for the sysvol folder
1498 :param gid: The GID of the "Domain adminstrators" group
1499 :param domainsid: The SID of the domain
1500 :param dnsdomain: The DNS name of the domain
1501 :param domaindn: The DN of the domain (ie. DC=...)
1505 os.chown(sysvol, -1, gid)
1511 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1512 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1513 for root, dirs, files in os.walk(sysvol, topdown=False):
1516 os.chown(os.path.join(root, name), -1, gid)
1517 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1520 os.chown(os.path.join(root, name), -1, gid)
1521 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1523 # Set acls on Policy folder and policies folders
1524 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1527 def interface_ips_v4(lp):
1528 '''return only IPv4 IPs'''
1529 ips = samba.interface_ips(lp, False)
1532 if i.find(':') == -1:
1536 def interface_ips_v6(lp, linklocal=False):
1537 '''return only IPv6 IPs'''
1538 ips = samba.interface_ips(lp, False)
1541 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1546 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1547 domainsid, schema=None,
1548 targetdir=None, samdb_fill=FILL_FULL,
1549 hostip=None, hostip6=None,
1550 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1551 domainguid=None, policyguid=None, policyguid_dc=None,
1552 invocationid=None, machinepass=None, ntdsguid=None,
1553 dns_backend=None, dnspass=None,
1554 serverrole=None, dom_for_fun_level=None,
1555 am_rodc=False, lp=None):
1556 # create/adapt the group policy GUIDs
1557 # Default GUID for default policy are described at
1558 # "How Core Group Policy Works"
1559 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1560 if policyguid is None:
1561 policyguid = DEFAULT_POLICY_GUID
1562 policyguid = policyguid.upper()
1563 if policyguid_dc is None:
1564 policyguid_dc = DEFAULT_DC_POLICY_GUID
1565 policyguid_dc = policyguid_dc.upper()
1567 if invocationid is None:
1568 invocationid = str(uuid.uuid4())
1570 if adminpass is None:
1571 adminpass = samba.generate_random_password(12, 32)
1572 if krbtgtpass is None:
1573 krbtgtpass = samba.generate_random_password(128, 255)
1574 if machinepass is None:
1575 machinepass = samba.generate_random_password(128, 255)
1577 dnspass = samba.generate_random_password(128, 255)
1579 samdb = fill_samdb(samdb, lp, names, logger=logger,
1580 domainsid=domainsid, schema=schema, domainguid=domainguid,
1581 policyguid=policyguid, policyguid_dc=policyguid_dc,
1582 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1583 invocationid=invocationid, machinepass=machinepass,
1584 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1585 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1586 next_rid=next_rid, dc_rid=dc_rid)
1588 if serverrole == "domain controller":
1589 # Set up group policies (domain policy and domain controller
1591 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1593 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1594 domainsid, names.dnsdomain, names.domaindn, lp)
1596 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1597 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1598 { 'NTDSGUID' : names.ntdsguid })
1600 secretsdb_self_join(secrets_ldb, domain=names.domain,
1601 realm=names.realm, dnsdomain=names.dnsdomain,
1602 netbiosname=names.netbiosname, domainsid=domainsid,
1603 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1605 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1606 # In future, this might be determined from some configuration
1607 kerberos_enctypes = str(ENC_ALL_TYPES)
1610 msg = ldb.Message(ldb.Dn(samdb,
1611 samdb.searchone("distinguishedName",
1612 expression="samAccountName=%s$" % names.netbiosname,
1613 scope=ldb.SCOPE_SUBTREE)))
1614 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1615 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1616 name="msDS-SupportedEncryptionTypes")
1618 except ldb.LdbError, (enum, estr):
1619 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1620 # It might be that this attribute does not exist in this schema
1623 if serverrole == "domain controller":
1624 secretsdb_setup_dns(secrets_ldb, names,
1625 paths.private_dir, realm=names.realm,
1626 dnsdomain=names.dnsdomain,
1627 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1629 # Default DNS backend is BIND9 using txt files for zone information
1631 dns_backend = "BIND9"
1633 setup_ad_dns(samdb, names, logger, hostip=hostip, hostip6=hostip6,
1634 dns_backend=dns_backend, os_level=dom_for_fun_level)
1636 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1637 attribute="objectGUID")
1638 assert isinstance(domainguid, str)
1640 create_dns_dir(logger, paths)
1642 # Only make a zone file on the first DC, it should be
1643 # replicated with DNS replication
1644 if dns_backend == "BIND9":
1645 create_zone_file(lp, logger, paths, targetdir,
1646 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1647 hostname=names.hostname, realm=names.realm,
1648 domainguid=domainguid, ntdsguid=names.ntdsguid)
1650 create_named_conf(paths, realm=names.realm,
1651 dnsdomain=names.dnsdomain, dns_backend=dns_backend)
1653 create_named_txt(paths.namedtxt,
1654 realm=names.realm, dnsdomain=names.dnsdomain,
1655 dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1656 private_dir=paths.private_dir,
1657 keytab_name=paths.dns_keytab)
1658 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1659 logger.info("and %s for further documentation required for secure DNS "
1660 "updates", paths.namedtxt)
1662 lastProvisionUSNs = get_last_provision_usn(samdb)
1663 maxUSN = get_max_usn(samdb, str(names.rootdn))
1664 if lastProvisionUSNs is not None:
1665 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1667 set_provision_usn(samdb, 0, maxUSN, invocationid)
1669 # fix any dangling GUIDs from the provision
1670 logger.info("Fixing provision GUIDs")
1671 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, quiet=True)
1672 samdb.transaction_start()
1673 # a small number of GUIDs are missing because of ordering issues in the
1675 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1676 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1677 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1678 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1679 scope=ldb.SCOPE_ONELEVEL,
1680 attrs=['ipsecOwnersReference',
1681 'ipsecFilterReference',
1682 'ipsecISAKMPReference',
1683 'ipsecNegotiationPolicyReference',
1684 'ipsecNFAReference'])
1685 samdb.transaction_commit()
1688 def provision(logger, session_info, credentials, smbconf=None,
1689 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1690 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1691 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1692 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1693 domainguid=None, policyguid=None, policyguid_dc=None,
1694 dns_backend=None, dnspass=None,
1695 invocationid=None, machinepass=None, ntdsguid=None,
1696 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1697 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1698 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1699 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1700 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1704 :note: caution, this wipes all existing data!
1707 if ldapadminpass is None:
1708 # Make a new, random password between Samba and it's LDAP server
1709 ldapadminpass=samba.generate_random_password(128, 255)
1711 if backend_type is None:
1712 backend_type = "ldb"
1714 if domainsid is None:
1715 domainsid = security.random_sid()
1717 domainsid = security.dom_sid(domainsid)
1719 sid_generator = "internal"
1720 if backend_type == "fedora-ds":
1721 sid_generator = "backend"
1723 root_uid = findnss_uid([root or "root"])
1724 nobody_uid = findnss_uid([nobody or "nobody"])
1725 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1727 wheel_gid = findnss_gid(["wheel", "adm"])
1729 wheel_gid = findnss_gid([wheel])
1731 bind_gid = findnss_gid(["bind", "named"])
1735 if targetdir is not None:
1736 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1737 elif smbconf is None:
1738 smbconf = samba.param.default_path()
1739 if not os.path.exists(os.path.dirname(smbconf)):
1740 os.makedirs(os.path.dirname(smbconf))
1742 # only install a new smb.conf if there isn't one there already
1743 if os.path.exists(smbconf):
1744 # if Samba Team members can't figure out the weird errors
1745 # loading an empty smb.conf gives, then we need to be smarter.
1746 # Pretend it just didn't exist --abartlet
1747 data = open(smbconf, 'r').read()
1748 data = data.lstrip()
1749 if data is None or data == "":
1750 make_smbconf(smbconf, hostname, domain, realm,
1751 serverrole, targetdir, sid_generator, useeadb,
1754 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1755 targetdir, sid_generator, useeadb, lp=lp)
1758 lp = samba.param.LoadParm()
1760 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1761 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1762 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1763 sitename=sitename, rootdn=rootdn)
1764 paths = provision_paths_from_lp(lp, names.dnsdomain)
1766 paths.bind_gid = bind_gid
1767 paths.wheel_gid = wheel_gid
1770 logger.info("Looking up IPv4 addresses")
1771 hostips = interface_ips_v4(lp)
1772 if len(hostips) > 0:
1774 if len(hostips) > 1:
1775 logger.warning("More than one IPv4 address found. Using %s",
1777 if hostip == "127.0.0.1":
1780 logger.warning("No IPv4 address will be assigned")
1783 logger.info("Looking up IPv6 addresses")
1784 hostips = interface_ips_v6(lp, linklocal=False)
1786 hostip6 = hostips[0]
1787 if len(hostips) > 1:
1788 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1790 logger.warning("No IPv6 address will be assigned")
1792 if serverrole is None:
1793 serverrole = lp.get("server role")
1795 assert serverrole in ("domain controller", "member server", "standalone")
1797 if not os.path.exists(paths.private_dir):
1798 os.mkdir(paths.private_dir)
1799 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1800 os.mkdir(os.path.join(paths.private_dir, "tls"))
1802 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1804 schema = Schema(domainsid, invocationid=invocationid,
1805 schemadn=names.schemadn)
1807 if backend_type == "ldb":
1808 provision_backend = LDBBackend(backend_type, paths=paths,
1809 lp=lp, credentials=credentials,
1810 names=names, logger=logger)
1811 elif backend_type == "existing":
1812 provision_backend = ExistingBackend(backend_type, paths=paths,
1813 lp=lp, credentials=credentials,
1814 names=names, logger=logger,
1815 ldap_backend_forced_uri=ldap_backend_forced_uri)
1816 elif backend_type == "fedora-ds":
1817 provision_backend = FDSBackend(backend_type, paths=paths,
1818 lp=lp, credentials=credentials,
1819 names=names, logger=logger, domainsid=domainsid,
1820 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1821 slapd_path=slapd_path,
1822 ldap_backend_extra_port=ldap_backend_extra_port,
1823 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1824 setup_ds_path=setup_ds_path,
1825 ldap_backend_forced_uri=ldap_backend_forced_uri)
1826 elif backend_type == "openldap":
1827 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1828 lp=lp, credentials=credentials,
1829 names=names, logger=logger, domainsid=domainsid,
1830 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1831 slapd_path=slapd_path,
1832 ldap_backend_extra_port=ldap_backend_extra_port,
1833 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1835 ldap_backend_forced_uri=ldap_backend_forced_uri)
1837 raise ValueError("Unknown LDAP backend type selected")
1839 provision_backend.init()
1840 provision_backend.start()
1842 # only install a new shares config db if there is none
1843 if not os.path.exists(paths.shareconf):
1844 logger.info("Setting up share.ldb")
1845 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1847 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1849 logger.info("Setting up secrets.ldb")
1850 secrets_ldb = setup_secretsdb(paths,
1851 session_info=session_info,
1852 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1855 logger.info("Setting up the registry")
1856 setup_registry(paths.hklm, session_info,
1859 logger.info("Setting up the privileges database")
1860 setup_privileges(paths.privilege, session_info, lp=lp)
1862 logger.info("Setting up idmap db")
1863 idmap = setup_idmapdb(paths.idmapdb,
1864 session_info=session_info, lp=lp)
1866 setup_name_mappings(idmap, sid=str(domainsid),
1867 root_uid=root_uid, nobody_uid=nobody_uid,
1868 users_gid=users_gid, wheel_gid=wheel_gid)
1870 logger.info("Setting up SAM db")
1871 samdb = setup_samdb(paths.samdb, session_info,
1872 provision_backend, lp, names, logger=logger,
1873 serverrole=serverrole,
1874 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1876 if serverrole == "domain controller":
1877 if paths.netlogon is None:
1878 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1879 logger.info("Please either remove %s or see the template at %s" %
1880 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1881 assert paths.netlogon is not None
1883 if paths.sysvol is None:
1884 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1885 " are configuring a DC.")
1886 logger.info("Please either remove %s or see the template at %s" %
1887 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1888 assert paths.sysvol is not None
1890 if not os.path.isdir(paths.netlogon):
1891 os.makedirs(paths.netlogon, 0755)
1893 if samdb_fill == FILL_FULL:
1894 provision_fill(samdb, secrets_ldb, logger,
1895 names, paths, schema=schema, targetdir=targetdir,
1896 samdb_fill=samdb_fill, hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1897 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1898 krbtgtpass=krbtgtpass, domainguid=domainguid,
1899 policyguid=policyguid, policyguid_dc=policyguid_dc,
1900 invocationid=invocationid, machinepass=machinepass,
1901 ntdsguid=ntdsguid, dns_backend=dns_backend, dnspass=dnspass,
1902 serverrole=serverrole, dom_for_fun_level=dom_for_fun_level,
1903 am_rodc=am_rodc, lp=lp)
1905 create_krb5_conf(paths.krb5conf,
1906 dnsdomain=names.dnsdomain, hostname=names.hostname,
1908 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1909 "generated at %s", paths.krb5conf)
1911 if serverrole == "domain controller":
1912 create_dns_update_list(lp, logger, paths)
1914 provision_backend.post_setup()
1915 provision_backend.shutdown()
1917 create_phpldapadmin_config(paths.phpldapadminconfig,
1920 secrets_ldb.transaction_cancel()
1923 # Now commit the secrets.ldb to disk
1924 secrets_ldb.transaction_commit()
1926 # the commit creates the dns.keytab, now chown it
1927 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1928 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1930 os.chmod(dns_keytab_path, 0640)
1931 os.chown(dns_keytab_path, -1, paths.bind_gid)
1933 if not os.environ.has_key('SAMBA_SELFTEST'):
1934 logger.info("Failed to chown %s to bind gid %u",
1935 dns_keytab_path, paths.bind_gid)
1937 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1938 paths.phpldapadminconfig)
1940 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1941 logger.info("Server Role: %s" % serverrole)
1942 logger.info("Hostname: %s" % names.hostname)
1943 logger.info("NetBIOS Domain: %s" % names.domain)
1944 logger.info("DNS Domain: %s" % names.dnsdomain)
1945 logger.info("DOMAIN SID: %s" % str(domainsid))
1946 if samdb_fill == FILL_FULL:
1947 logger.info("Admin password: %s" % adminpass)
1948 if provision_backend.type is not "ldb":
1949 if provision_backend.credentials.get_bind_dn() is not None:
1950 logger.info("LDAP Backend Admin DN: %s" %
1951 provision_backend.credentials.get_bind_dn())
1953 logger.info("LDAP Admin User: %s" %
1954 provision_backend.credentials.get_username())
1956 logger.info("LDAP Admin Password: %s" %
1957 provision_backend.credentials.get_password())
1959 if provision_backend.slapd_command_escaped is not None:
1960 # now display slapd_command_file.txt to show how slapd must be
1962 logger.info("Use later the following commandline to start slapd, then Samba:")
1963 logger.info(provision_backend.slapd_command_escaped)
1964 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1965 provision_backend.ldapdir)
1967 result = ProvisionResult()
1968 result.domaindn = domaindn
1969 result.paths = paths
1970 result.names = names
1972 result.samdb = samdb
1973 result.idmap = idmap
1977 def provision_become_dc(smbconf=None, targetdir=None,
1978 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1979 serverdn=None, domain=None, hostname=None, domainsid=None,
1980 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1981 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1982 dns_backend=None, root=None, nobody=None, users=None, wheel=None, backup=None,
1983 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1984 sitename=None, debuglevel=1):
1986 logger = logging.getLogger("provision")
1987 samba.set_debug_level(debuglevel)
1989 res = provision(logger, system_session(), None,
1990 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1991 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1992 configdn=configdn, serverdn=serverdn, domain=domain,
1993 hostname=hostname, hostip=None, domainsid=domainsid,
1994 machinepass=machinepass, serverrole="domain controller",
1995 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1996 res.lp.set("debuglevel", str(debuglevel))
2000 def create_phpldapadmin_config(path, ldapi_uri):
2001 """Create a PHP LDAP admin configuration file.
2003 :param path: Path to write the configuration to.
2005 setup_file(setup_path("phpldapadmin-config.php"), path,
2006 {"S4_LDAPI_URI": ldapi_uri})
2009 def create_dns_dir(logger, paths):
2010 """Write out a DNS zone file, from the info in the current database.
2012 :param logger: Logger object
2013 :param paths: paths object
2015 dns_dir = os.path.dirname(paths.dns)
2018 shutil.rmtree(dns_dir, True)
2022 os.mkdir(dns_dir, 0770)
2024 if paths.bind_gid is not None:
2026 os.chown(dns_dir, -1, paths.bind_gid)
2027 # chmod needed to cope with umask
2028 os.chmod(dns_dir, 0770)
2030 if not os.environ.has_key('SAMBA_SELFTEST'):
2031 logger.error("Failed to chown %s to bind gid %u" % (
2032 dns_dir, paths.bind_gid))
2035 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
2036 hostip, hostip6, hostname, realm, domainguid,
2038 """Write out a DNS zone file, from the info in the current database.
2040 :param paths: paths object
2041 :param dnsdomain: DNS Domain name
2042 :param domaindn: DN of the Domain
2043 :param hostip: Local IPv4 IP
2044 :param hostip6: Local IPv6 IP
2045 :param hostname: Local hostname
2046 :param realm: Realm name
2047 :param domainguid: GUID of the domain.
2048 :param ntdsguid: GUID of the hosts nTDSDSA record.
2050 assert isinstance(domainguid, str)
2052 if hostip6 is not None:
2053 hostip6_base_line = " IN AAAA " + hostip6
2054 hostip6_host_line = hostname + " IN AAAA " + hostip6
2055 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
2057 hostip6_base_line = ""
2058 hostip6_host_line = ""
2059 gc_msdcs_ip6_line = ""
2061 if hostip is not None:
2062 hostip_base_line = " IN A " + hostip
2063 hostip_host_line = hostname + " IN A " + hostip
2064 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
2066 hostip_base_line = ""
2067 hostip_host_line = ""
2068 gc_msdcs_ip_line = ""
2070 # we need to freeze the zone while we update the contents
2071 if targetdir is None:
2072 rndc = ' '.join(lp.get("rndc command"))
2073 os.system(rndc + " freeze " + lp.get("realm"))
2075 setup_file(setup_path("provision.zone"), paths.dns, {
2076 "HOSTNAME": hostname,
2077 "DNSDOMAIN": dnsdomain,
2079 "HOSTIP_BASE_LINE": hostip_base_line,
2080 "HOSTIP_HOST_LINE": hostip_host_line,
2081 "DOMAINGUID": domainguid,
2082 "DATESTRING": time.strftime("%Y%m%d%H"),
2083 "DEFAULTSITE": DEFAULTSITE,
2084 "NTDSGUID": ntdsguid,
2085 "HOSTIP6_BASE_LINE": hostip6_base_line,
2086 "HOSTIP6_HOST_LINE": hostip6_host_line,
2087 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
2088 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
2091 if paths.bind_gid is not None:
2093 os.chown(paths.dns, -1, paths.bind_gid)
2094 # chmod needed to cope with umask
2095 os.chmod(paths.dns, 0664)
2097 if not os.environ.has_key('SAMBA_SELFTEST'):
2098 logger.error("Failed to chown %s to bind gid %u" % (
2099 paths.dns, paths.bind_gid))
2101 if targetdir is None:
2102 os.system(rndc + " unfreeze " + lp.get("realm"))
2105 def create_dns_update_list(lp, logger, paths):
2106 """Write out a dns_update_list file"""
2107 # note that we use no variable substitution on this file
2108 # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
2109 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2110 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2113 def create_named_conf(paths, realm, dnsdomain, dns_backend):
2114 """Write out a file containing zone statements suitable for inclusion in a
2115 named.conf file (including GSS-TSIG configuration).
2117 :param paths: all paths
2118 :param realm: Realm name
2119 :param dnsdomain: DNS Domain name
2120 :param dns_backend: DNS backend type
2121 :param keytab_name: File name of DNS keytab file
2124 if dns_backend == "BIND9":
2125 setup_file(setup_path("named.conf"), paths.namedconf, {
2126 "DNSDOMAIN": dnsdomain,
2128 "ZONE_FILE": paths.dns,
2129 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2130 "NAMED_CONF": paths.namedconf,
2131 "NAMED_CONF_UPDATE": paths.namedconf_update
2134 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2136 elif dns_backend == "BIND9_DLZ":
2137 dlz_module_path = os.path.join(samba.param.modules_dir(),
2138 "bind9/dlz_bind9.so")
2139 setup_file(setup_path("named.conf.dlz"), paths.namedconf, {
2140 "NAMED_CONF": paths.namedconf,
2141 "BIND9_DLZ_MODULE": dlz_module_path,
2146 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2148 """Write out a file containing zone statements suitable for inclusion in a
2149 named.conf file (including GSS-TSIG configuration).
2151 :param path: Path of the new named.conf file.
2152 :param realm: Realm name
2153 :param dnsdomain: DNS Domain name
2154 :param private_dir: Path to private directory
2155 :param keytab_name: File name of DNS keytab file
2157 setup_file(setup_path("named.txt"), path, {
2158 "DNSDOMAIN": dnsdomain,
2159 "DNSNAME" : dnsname,
2161 "DNS_KEYTAB": keytab_name,
2162 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2163 "PRIVATE_DIR": private_dir
2167 def create_krb5_conf(path, dnsdomain, hostname, realm):
2168 """Write out a file containing zone statements suitable for inclusion in a
2169 named.conf file (including GSS-TSIG configuration).
2171 :param path: Path of the new named.conf file.
2172 :param dnsdomain: DNS Domain name
2173 :param hostname: Local hostname
2174 :param realm: Realm name
2176 setup_file(setup_path("krb5.conf"), path, {
2177 "DNSDOMAIN": dnsdomain,
2178 "HOSTNAME": hostname,
2183 class ProvisioningError(Exception):
2184 """A generic provision error."""
2186 def __init__(self, value):
2190 return "ProvisioningError: " + self.value
2193 class InvalidNetbiosName(Exception):
2194 """A specified name was not a valid NetBIOS name."""
2195 def __init__(self, name):
2196 super(InvalidNetbiosName, self).__init__(
2197 "The name '%r' is not a valid NetBIOS name" % name)