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 basedn = "DC=" + names.realm.replace(".",",DC=")
229 names.dnsdomain = names.realm.lower()
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):
442 def check_install(lp, session_info, credentials):
443 """Check whether the current install seems ok.
445 :param lp: Loadparm context
446 :param session_info: Session information
447 :param credentials: Credentials
449 if lp.get("realm") == "":
450 raise Exception("Realm empty")
451 samdb = Ldb(lp.samdb_url(), session_info=session_info,
452 credentials=credentials, lp=lp)
453 if len(samdb.search("(cn=Administrator)")) != 1:
454 raise ProvisioningError("No administrator account found")
457 def findnss(nssfn, names):
458 """Find a user or group from a list of possibilities.
460 :param nssfn: NSS Function to try (should raise KeyError if not found)
461 :param names: Names to check.
462 :return: Value return by first names list.
469 raise KeyError("Unable to find user/group in %r" % names)
472 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
473 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
476 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
477 """Setup a ldb in the private dir.
479 :param ldb: LDB file to import data into
480 :param ldif_path: Path of the LDIF file to load
481 :param subst_vars: Optional variables to subsitute in LDIF.
482 :param nocontrols: Optional list of controls, can be None for no controls
484 assert isinstance(ldif_path, str)
485 data = read_and_sub_file(ldif_path, subst_vars)
486 ldb.add_ldif(data, controls)
489 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
490 """Modify a ldb in the private dir.
492 :param ldb: LDB object.
493 :param ldif_path: LDIF file path.
494 :param subst_vars: Optional dictionary with substitution variables.
496 data = read_and_sub_file(ldif_path, subst_vars)
497 ldb.modify_ldif(data, controls)
500 def setup_ldb(ldb, ldif_path, subst_vars):
501 """Import a LDIF a file into a LDB handle, optionally substituting
504 :note: Either all LDIF data will be added or none (using transactions).
506 :param ldb: LDB file to import into.
507 :param ldif_path: Path to the LDIF file.
508 :param subst_vars: Dictionary with substitution variables.
510 assert ldb is not None
511 ldb.transaction_start()
513 setup_add_ldif(ldb, ldif_path, subst_vars)
515 ldb.transaction_cancel()
518 ldb.transaction_commit()
521 def provision_paths_from_lp(lp, dnsdomain):
522 """Set the default paths for provisioning.
524 :param lp: Loadparm context.
525 :param dnsdomain: DNS Domain name
527 paths = ProvisionPaths()
528 paths.private_dir = lp.get("private dir")
530 # This is stored without path prefix for the "privateKeytab" attribute in
531 # "secrets_dns.ldif".
532 paths.dns_keytab = "dns.keytab"
533 paths.keytab = "secrets.keytab"
535 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
536 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
537 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
538 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
539 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
540 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
541 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
542 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
543 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
544 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
545 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
546 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
547 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
548 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
549 paths.phpldapadminconfig = os.path.join(paths.private_dir,
550 "phpldapadmin-config.php")
551 paths.hklm = "hklm.ldb"
552 paths.hkcr = "hkcr.ldb"
553 paths.hkcu = "hkcu.ldb"
554 paths.hku = "hku.ldb"
555 paths.hkpd = "hkpd.ldb"
556 paths.hkpt = "hkpt.ldb"
557 paths.sysvol = lp.get("path", "sysvol")
558 paths.netlogon = lp.get("path", "netlogon")
559 paths.smbconf = lp.configfile
563 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
564 serverrole=None, rootdn=None, domaindn=None, configdn=None,
565 schemadn=None, serverdn=None, sitename=None):
566 """Guess configuration settings to use."""
569 hostname = socket.gethostname().split(".")[0]
571 netbiosname = lp.get("netbios name")
572 if netbiosname is None:
573 netbiosname = hostname
574 # remove forbidden chars
576 for x in netbiosname:
577 if x.isalnum() or x in VALID_NETBIOS_CHARS:
578 newnbname = "%s%c" % (newnbname, x)
579 # force the length to be <16
580 netbiosname = newnbname[0:15]
581 assert netbiosname is not None
582 netbiosname = netbiosname.upper()
583 if not valid_netbios_name(netbiosname):
584 raise InvalidNetbiosName(netbiosname)
586 if dnsdomain is None:
587 dnsdomain = lp.get("realm")
588 if dnsdomain is None or dnsdomain == "":
589 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
591 dnsdomain = dnsdomain.lower()
593 if serverrole is None:
594 serverrole = lp.get("server role")
595 if serverrole is None:
596 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
598 serverrole = serverrole.lower()
600 realm = dnsdomain.upper()
602 if lp.get("realm") == "":
603 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
605 if lp.get("realm").upper() != realm:
606 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))
608 if lp.get("server role").lower() != serverrole:
609 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))
611 if serverrole == "domain controller":
613 # This will, for better or worse, default to 'WORKGROUP'
614 domain = lp.get("workgroup")
615 domain = domain.upper()
617 if lp.get("workgroup").upper() != domain:
618 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))
621 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
623 if domain == netbiosname:
624 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
628 domaindn = "DC=" + netbiosname
630 if not valid_netbios_name(domain):
631 raise InvalidNetbiosName(domain)
633 if hostname.upper() == realm:
634 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
635 if netbiosname.upper() == realm:
636 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
638 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
644 configdn = "CN=Configuration," + rootdn
646 schemadn = "CN=Schema," + configdn
651 names = ProvisionNames()
652 names.rootdn = rootdn
653 names.domaindn = domaindn
654 names.configdn = configdn
655 names.schemadn = schemadn
656 names.ldapmanagerdn = "CN=Manager," + rootdn
657 names.dnsdomain = dnsdomain
658 names.domain = domain
660 names.netbiosname = netbiosname
661 names.hostname = hostname
662 names.sitename = sitename
663 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
664 netbiosname, sitename, configdn)
669 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
670 targetdir, sid_generator="internal", eadb=False, lp=None):
671 """Create a new smb.conf file based on a couple of basic settings.
673 assert smbconf is not None
675 hostname = socket.gethostname().split(".")[0]
676 netbiosname = hostname.upper()
677 # remove forbidden chars
679 for x in netbiosname:
680 if x.isalnum() or x in VALID_NETBIOS_CHARS:
681 newnbname = "%s%c" % (newnbname, x)
682 #force the length to be <16
683 netbiosname = newnbname[0:15]
685 netbiosname = hostname.upper()
687 if serverrole is None:
688 serverrole = "standalone"
690 assert serverrole in ("domain controller", "member server", "standalone")
691 if serverrole == "domain controller":
693 elif serverrole == "member server":
694 smbconfsuffix = "member"
695 elif serverrole == "standalone":
696 smbconfsuffix = "standalone"
698 if sid_generator is None:
699 sid_generator = "internal"
701 assert domain is not None
702 domain = domain.upper()
704 assert realm is not None
705 realm = realm.upper()
708 lp = samba.param.LoadParm()
709 #Load non-existant file
710 if os.path.exists(smbconf):
712 if eadb and not lp.get("posix:eadb"):
713 if targetdir is not None:
714 privdir = os.path.join(targetdir, "private")
716 privdir = lp.get("private dir")
717 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
719 if targetdir is not None:
720 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
721 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
722 statedir_line = "state directory = " + os.path.abspath(targetdir)
723 cachedir_line = "cache directory = " + os.path.abspath(targetdir)
725 lp.set("lock dir", os.path.abspath(targetdir))
726 lp.set("state directory", os.path.abspath(targetdir))
727 lp.set("cache directory", os.path.abspath(targetdir))
734 sysvol = os.path.join(lp.get("state directory"), "sysvol")
735 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
737 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
739 "NETBIOS_NAME": netbiosname,
742 "SERVERROLE": serverrole,
743 "NETLOGONPATH": netlogon,
744 "SYSVOLPATH": sysvol,
745 "PRIVATEDIR_LINE": privatedir_line,
746 "LOCKDIR_LINE": lockdir_line,
747 "STATEDIR_LINE": statedir_line,
748 "CACHEDIR_LINE": cachedir_line
751 # reload the smb.conf
754 # and dump it without any values that are the default
755 # this ensures that any smb.conf parameters that were set
756 # on the provision/join command line are set in the resulting smb.conf
757 f = open(smbconf, mode='w')
763 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
764 users_gid, wheel_gid):
765 """setup reasonable name mappings for sam names to unix names.
767 :param samdb: SamDB object.
768 :param idmap: IDmap db object.
769 :param sid: The domain sid.
770 :param domaindn: The domain DN.
771 :param root_uid: uid of the UNIX root user.
772 :param nobody_uid: uid of the UNIX nobody user.
773 :param users_gid: gid of the UNIX users group.
774 :param wheel_gid: gid of the UNIX wheel group.
776 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
777 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
779 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
780 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
783 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
784 provision_backend, names, schema, serverrole,
786 """Setup the partitions for the SAM database.
788 Alternatively, provision() may call this, and then populate the database.
790 :note: This will wipe the Sam Database!
792 :note: This function always removes the local SAM LDB file. The erase
793 parameter controls whether to erase the existing data, which
794 may not be stored locally but in LDAP.
797 assert session_info is not None
799 # We use options=["modules:"] to stop the modules loading - we
800 # just want to wipe and re-initialise the database, not start it up
803 os.unlink(samdb_path)
807 samdb = Ldb(url=samdb_path, session_info=session_info,
808 lp=lp, options=["modules:"])
810 ldap_backend_line = "# No LDAP backend"
811 if provision_backend.type is not "ldb":
812 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
814 samdb.transaction_start()
816 logger.info("Setting up sam.ldb partitions and settings")
817 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
818 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
819 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
820 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
821 "LDAP_BACKEND_LINE": ldap_backend_line,
825 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
826 "BACKEND_TYPE": provision_backend.type,
827 "SERVER_ROLE": serverrole
830 logger.info("Setting up sam.ldb rootDSE")
831 setup_samdb_rootdse(samdb, names)
833 samdb.transaction_cancel()
836 samdb.transaction_commit()
839 def secretsdb_self_join(secretsdb, domain,
840 netbiosname, machinepass, domainsid=None,
841 realm=None, dnsdomain=None,
843 key_version_number=1,
844 secure_channel_type=SEC_CHAN_WKSTA):
845 """Add domain join-specific bits to a secrets database.
847 :param secretsdb: Ldb Handle to the secrets database
848 :param machinepass: Machine password
850 attrs = ["whenChanged",
857 if realm is not None:
858 if dnsdomain is None:
859 dnsdomain = realm.lower()
860 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
863 shortname = netbiosname.lower()
865 # We don't need to set msg["flatname"] here, because rdn_name will handle
866 # it, and it causes problems for modifies anyway
867 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
868 msg["secureChannelType"] = [str(secure_channel_type)]
869 msg["objectClass"] = ["top", "primaryDomain"]
870 if dnsname is not None:
871 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
872 msg["realm"] = [realm]
873 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
874 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
875 msg["privateKeytab"] = ["secrets.keytab"]
877 msg["secret"] = [machinepass]
878 msg["samAccountName"] = ["%s$" % netbiosname]
879 msg["secureChannelType"] = [str(secure_channel_type)]
880 if domainsid is not None:
881 msg["objectSid"] = [ndr_pack(domainsid)]
883 # This complex expression tries to ensure that we don't have more
884 # than one record for this SID, realm or netbios domain at a time,
885 # but we don't delete the old record that we are about to modify,
886 # because that would delete the keytab and previous password.
887 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
888 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
889 scope=ldb.SCOPE_ONELEVEL)
892 secretsdb.delete(del_msg.dn)
894 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
897 msg["priorSecret"] = [res[0]["secret"][0]]
898 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
901 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
906 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
912 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
913 secretsdb.modify(msg)
914 secretsdb.rename(res[0].dn, msg.dn)
916 spn = [ 'HOST/%s' % shortname ]
917 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
918 # we are a domain controller then we add servicePrincipalName
919 # entries for the keytab code to update.
920 spn.extend([ 'HOST/%s' % dnsname ])
921 msg["servicePrincipalName"] = spn
926 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
927 dnsdomain, dns_keytab_path, dnspass):
928 """Add DNS specific bits to a secrets database.
930 :param secretsdb: Ldb Handle to the secrets database
931 :param machinepass: Machine password
934 os.unlink(os.path.join(private_dir, dns_keytab_path))
938 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
940 "DNSDOMAIN": dnsdomain,
941 "DNS_KEYTAB": dns_keytab_path,
942 "DNSPASS_B64": b64encode(dnspass),
943 "HOSTNAME": names.hostname,
944 "DNSNAME" : '%s.%s' % (
945 names.netbiosname.lower(), names.dnsdomain.lower())
949 def setup_secretsdb(paths, session_info, backend_credentials, lp):
950 """Setup the secrets database.
952 :note: This function does not handle exceptions and transaction on purpose,
953 it's up to the caller to do this job.
955 :param path: Path to the secrets database.
956 :param session_info: Session info.
957 :param credentials: Credentials
958 :param lp: Loadparm context
959 :return: LDB handle for the created secrets database
961 if os.path.exists(paths.secrets):
962 os.unlink(paths.secrets)
964 keytab_path = os.path.join(paths.private_dir, paths.keytab)
965 if os.path.exists(keytab_path):
966 os.unlink(keytab_path)
968 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
969 if os.path.exists(dns_keytab_path):
970 os.unlink(dns_keytab_path)
974 secrets_ldb = Ldb(path, session_info=session_info,
977 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
978 secrets_ldb = Ldb(path, session_info=session_info,
980 secrets_ldb.transaction_start()
982 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
984 if (backend_credentials is not None and
985 backend_credentials.authentication_requested()):
986 if backend_credentials.get_bind_dn() is not None:
987 setup_add_ldif(secrets_ldb,
988 setup_path("secrets_simple_ldap.ldif"), {
989 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
990 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
993 setup_add_ldif(secrets_ldb,
994 setup_path("secrets_sasl_ldap.ldif"), {
995 "LDAPADMINUSER": backend_credentials.get_username(),
996 "LDAPADMINREALM": backend_credentials.get_realm(),
997 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
1002 secrets_ldb.transaction_cancel()
1006 def setup_privileges(path, session_info, lp):
1007 """Setup the privileges database.
1009 :param path: Path to the privileges database.
1010 :param session_info: Session info.
1011 :param credentials: Credentials
1012 :param lp: Loadparm context
1013 :return: LDB handle for the created secrets database
1015 if os.path.exists(path):
1017 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1018 privilege_ldb.erase()
1019 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1022 def setup_registry(path, session_info, lp):
1023 """Setup the registry.
1025 :param path: Path to the registry database
1026 :param session_info: Session information
1027 :param credentials: Credentials
1028 :param lp: Loadparm context
1030 reg = samba.registry.Registry()
1031 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1032 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1033 provision_reg = setup_path("provision.reg")
1034 assert os.path.exists(provision_reg)
1035 reg.diff_apply(provision_reg)
1038 def setup_idmapdb(path, session_info, lp):
1039 """Setup the idmap database.
1041 :param path: path to the idmap database
1042 :param session_info: Session information
1043 :param credentials: Credentials
1044 :param lp: Loadparm context
1046 if os.path.exists(path):
1049 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1051 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1055 def setup_samdb_rootdse(samdb, names):
1056 """Setup the SamDB rootdse.
1058 :param samdb: Sam Database handle
1060 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1061 "SCHEMADN": names.schemadn,
1062 "DOMAINDN": names.domaindn,
1063 "ROOTDN": names.rootdn,
1064 "CONFIGDN": names.configdn,
1065 "SERVERDN": names.serverdn,
1069 def setup_self_join(samdb, names, machinepass, dnspass,
1070 domainsid, next_rid, invocationid,
1071 policyguid, policyguid_dc, domainControllerFunctionality,
1072 ntdsguid, dc_rid=None):
1073 """Join a host to its own domain."""
1074 assert isinstance(invocationid, str)
1075 if ntdsguid is not None:
1076 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1083 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1084 "CONFIGDN": names.configdn,
1085 "SCHEMADN": names.schemadn,
1086 "DOMAINDN": names.domaindn,
1087 "SERVERDN": names.serverdn,
1088 "INVOCATIONID": invocationid,
1089 "NETBIOSNAME": names.netbiosname,
1090 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1091 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1092 "DOMAINSID": str(domainsid),
1093 "DCRID": str(dc_rid),
1094 "SAMBA_VERSION_STRING": version,
1095 "NTDSGUID": ntdsguid_line,
1096 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1097 domainControllerFunctionality)})
1099 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1100 "POLICYGUID": policyguid,
1101 "POLICYGUID_DC": policyguid_dc,
1102 "DNSDOMAIN": names.dnsdomain,
1103 "DOMAINDN": names.domaindn})
1105 # add the NTDSGUID based SPNs
1106 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1107 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1108 expression="", scope=ldb.SCOPE_BASE)
1109 assert isinstance(names.ntdsguid, str)
1111 # Setup fSMORoleOwner entries to point at the newly created DC entry
1112 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1113 "DOMAINDN": names.domaindn,
1114 "CONFIGDN": names.configdn,
1115 "SCHEMADN": names.schemadn,
1116 "DEFAULTSITE": names.sitename,
1117 "SERVERDN": names.serverdn,
1118 "NETBIOSNAME": names.netbiosname,
1119 "RIDALLOCATIONSTART": str(next_rid + 100),
1120 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1123 # This is Samba4 specific and should be replaced by the correct
1124 # DNS AD-style setup
1125 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1126 "DNSDOMAIN": names.dnsdomain,
1127 "DOMAINDN": names.domaindn,
1128 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1129 "HOSTNAME" : names.hostname,
1130 "DNSNAME" : '%s.%s' % (
1131 names.netbiosname.lower(), names.dnsdomain.lower())
1135 def getpolicypath(sysvolpath, dnsdomain, guid):
1136 """Return the physical path of policy given its guid.
1138 :param sysvolpath: Path to the sysvol folder
1139 :param dnsdomain: DNS name of the AD domain
1140 :param guid: The GUID of the policy
1141 :return: A string with the complete path to the policy folder
1145 guid = "{%s}" % guid
1146 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1150 def create_gpo_struct(policy_path):
1151 if not os.path.exists(policy_path):
1152 os.makedirs(policy_path, 0775)
1153 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1154 "[General]\r\nVersion=0")
1155 p = os.path.join(policy_path, "MACHINE")
1156 if not os.path.exists(p):
1157 os.makedirs(p, 0775)
1158 p = os.path.join(policy_path, "USER")
1159 if not os.path.exists(p):
1160 os.makedirs(p, 0775)
1163 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1164 """Create the default GPO for a domain
1166 :param sysvolpath: Physical path for the sysvol folder
1167 :param dnsdomain: DNS domain name of the AD domain
1168 :param policyguid: GUID of the default domain policy
1169 :param policyguid_dc: GUID of the default domain controler policy
1171 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1172 create_gpo_struct(policy_path)
1174 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1175 create_gpo_struct(policy_path)
1178 def setup_samdb(path, session_info, provision_backend, lp, names,
1179 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1180 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1181 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1182 next_rid=None, dc_rid=None):
1183 """Setup a complete SAM Database.
1185 :note: This will wipe the main SAM database file!
1188 if next_rid is None:
1191 # Provision does not make much sense values larger than 1000000000
1192 # as the upper range of the rIDAvailablePool is 1073741823 and
1193 # we don't want to create a domain that cannot allocate rids.
1194 if next_rid < 1000 or next_rid > 1000000000:
1195 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1196 error += "the valid range is %u-%u. The default is %u." % (
1197 1000, 1000000000, 1000)
1198 raise ProvisioningError(error)
1200 # ATTENTION: Do NOT change these default values without discussion with the
1201 # team and/or release manager. They have a big impact on the whole program!
1202 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1204 if dom_for_fun_level is None:
1205 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1207 if dom_for_fun_level > domainControllerFunctionality:
1208 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!")
1210 domainFunctionality = dom_for_fun_level
1211 forestFunctionality = dom_for_fun_level
1213 # Also wipes the database
1214 setup_samdb_partitions(path, logger=logger, lp=lp,
1215 provision_backend=provision_backend, session_info=session_info,
1216 names=names, serverrole=serverrole, schema=schema)
1219 schema = Schema(domainsid, schemadn=names.schemadn)
1221 # Load the database, but don's load the global schema and don't connect
1223 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1224 credentials=provision_backend.credentials, lp=lp,
1225 global_schema=False, am_rodc=am_rodc)
1227 logger.info("Pre-loading the Samba 4 and AD schema")
1229 # Load the schema from the one we computed earlier
1230 samdb.set_schema(schema)
1232 # Set the NTDS settings DN manually - in order to have it already around
1233 # before the provisioned tree exists and we connect
1234 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1236 # And now we can connect to the DB - the schema won't be loaded from the
1240 if fill == FILL_DRS:
1243 samdb.transaction_start()
1245 # Set the domain functionality levels onto the database.
1246 # Various module (the password_hash module in particular) need
1247 # to know what level of AD we are emulating.
1249 # These will be fixed into the database via the database
1250 # modifictions below, but we need them set from the start.
1251 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1252 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1253 samdb.set_opaque_integer("domainControllerFunctionality",
1254 domainControllerFunctionality)
1256 samdb.set_domain_sid(str(domainsid))
1257 samdb.set_invocation_id(invocationid)
1259 logger.info("Adding DomainDN: %s" % names.domaindn)
1261 # impersonate domain admin
1262 admin_session_info = admin_session(lp, str(domainsid))
1263 samdb.set_session_info(admin_session_info)
1264 if domainguid is not None:
1265 domainguid_line = "objectGUID: %s\n-" % domainguid
1267 domainguid_line = ""
1269 descr = b64encode(get_domain_descriptor(domainsid))
1270 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1271 "DOMAINDN": names.domaindn,
1272 "DOMAINSID": str(domainsid),
1273 "DESCRIPTOR": descr,
1274 "DOMAINGUID": domainguid_line
1277 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1278 "DOMAINDN": names.domaindn,
1279 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1280 "NEXTRID": str(next_rid),
1281 "DEFAULTSITE": names.sitename,
1282 "CONFIGDN": names.configdn,
1283 "POLICYGUID": policyguid,
1284 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1285 "SAMBA_VERSION_STRING": version
1288 logger.info("Adding configuration container")
1289 descr = b64encode(get_config_descriptor(domainsid))
1290 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1291 "CONFIGDN": names.configdn,
1292 "DESCRIPTOR": descr,
1295 # Now register this container in the root of the forest
1296 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1297 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1300 # The LDIF here was created when the Schema object was constructed
1301 logger.info("Setting up sam.ldb schema")
1302 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1303 samdb.modify_ldif(schema.schema_dn_modify)
1304 samdb.write_prefixes_from_schema()
1305 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1306 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1307 {"SCHEMADN": names.schemadn})
1309 logger.info("Reopening sam.ldb with new schema")
1311 samdb.transaction_cancel()
1314 samdb.transaction_commit()
1316 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1317 credentials=provision_backend.credentials, lp=lp,
1318 global_schema=False, am_rodc=am_rodc)
1320 # Set the NTDS settings DN manually - in order to have it already around
1321 # before the provisioned tree exists and we connect
1322 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1325 samdb.transaction_start()
1327 samdb.invocation_id = invocationid
1329 logger.info("Setting up sam.ldb configuration data")
1330 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1331 "CONFIGDN": names.configdn,
1332 "NETBIOSNAME": names.netbiosname,
1333 "DEFAULTSITE": names.sitename,
1334 "DNSDOMAIN": names.dnsdomain,
1335 "DOMAIN": names.domain,
1336 "SCHEMADN": names.schemadn,
1337 "DOMAINDN": names.domaindn,
1338 "SERVERDN": names.serverdn,
1339 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1340 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1343 logger.info("Setting up display specifiers")
1344 display_specifiers_ldif = read_ms_ldif(
1345 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1346 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1347 {"CONFIGDN": names.configdn})
1348 check_all_substituted(display_specifiers_ldif)
1349 samdb.add_ldif(display_specifiers_ldif)
1351 logger.info("Adding users container")
1352 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1353 "DOMAINDN": names.domaindn})
1354 logger.info("Modifying users container")
1355 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1356 "DOMAINDN": names.domaindn})
1357 logger.info("Adding computers container")
1358 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1359 "DOMAINDN": names.domaindn})
1360 logger.info("Modifying computers container")
1361 setup_modify_ldif(samdb,
1362 setup_path("provision_computers_modify.ldif"), {
1363 "DOMAINDN": names.domaindn})
1364 logger.info("Setting up sam.ldb data")
1365 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1366 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1367 "DOMAINDN": names.domaindn,
1368 "NETBIOSNAME": names.netbiosname,
1369 "DEFAULTSITE": names.sitename,
1370 "CONFIGDN": names.configdn,
1371 "SERVERDN": names.serverdn,
1372 "RIDAVAILABLESTART": str(next_rid + 600),
1373 "POLICYGUID_DC": policyguid_dc
1376 setup_modify_ldif(samdb,
1377 setup_path("provision_basedn_references.ldif"), {
1378 "DOMAINDN": names.domaindn})
1380 setup_modify_ldif(samdb,
1381 setup_path("provision_configuration_references.ldif"), {
1382 "CONFIGDN": names.configdn,
1383 "SCHEMADN": names.schemadn})
1384 if fill == FILL_FULL:
1385 logger.info("Setting up sam.ldb users and groups")
1386 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1387 "DOMAINDN": names.domaindn,
1388 "DOMAINSID": str(domainsid),
1389 "CONFIGDN": names.configdn,
1390 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1391 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1394 logger.info("Setting up self join")
1395 setup_self_join(samdb, names=names, invocationid=invocationid,
1397 machinepass=machinepass,
1398 domainsid=domainsid,
1401 policyguid=policyguid,
1402 policyguid_dc=policyguid_dc,
1403 domainControllerFunctionality=domainControllerFunctionality,
1406 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1407 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1408 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1409 assert isinstance(names.ntdsguid, str)
1411 samdb.transaction_cancel()
1414 samdb.transaction_commit()
1419 FILL_NT4SYNC = "NT4SYNC"
1421 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1422 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)"
1425 def set_dir_acl(path, acl, lp, domsid):
1426 setntacl(lp, path, acl, domsid)
1427 for root, dirs, files in os.walk(path, topdown=False):
1429 setntacl(lp, os.path.join(root, name), acl, domsid)
1431 setntacl(lp, os.path.join(root, name), acl, domsid)
1434 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1435 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1438 :param sysvol: Physical path for the sysvol folder
1439 :param dnsdomain: The DNS name of the domain
1440 :param domainsid: The SID of the domain
1441 :param domaindn: The DN of the domain (ie. DC=...)
1442 :param samdb: An LDB object on the SAM db
1443 :param lp: an LP object
1446 # Set ACL for GPO root folder
1447 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1448 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1450 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1451 attrs=["cn", "nTSecurityDescriptor"],
1452 expression="", scope=ldb.SCOPE_ONELEVEL)
1455 acl = ndr_unpack(security.descriptor,
1456 str(policy["nTSecurityDescriptor"])).as_sddl()
1457 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1458 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1462 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1464 """Set the ACL for the sysvol share and the subfolders
1466 :param samdb: An LDB object on the SAM db
1467 :param netlogon: Physical path for the netlogon folder
1468 :param sysvol: Physical path for the sysvol folder
1469 :param gid: The GID of the "Domain adminstrators" group
1470 :param domainsid: The SID of the domain
1471 :param dnsdomain: The DNS name of the domain
1472 :param domaindn: The DN of the domain (ie. DC=...)
1476 os.chown(sysvol, -1, gid)
1482 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1483 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1484 for root, dirs, files in os.walk(sysvol, topdown=False):
1487 os.chown(os.path.join(root, name), -1, gid)
1488 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1491 os.chown(os.path.join(root, name), -1, gid)
1492 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1494 # Set acls on Policy folder and policies folders
1495 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1498 def interface_ips_v4(lp):
1499 '''return only IPv4 IPs'''
1500 ips = samba.interface_ips(lp, False)
1503 if i.find(':') == -1:
1507 def interface_ips_v6(lp, linklocal=False):
1508 '''return only IPv6 IPs'''
1509 ips = samba.interface_ips(lp, False)
1512 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1517 def provision(logger, session_info, credentials, smbconf=None,
1518 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1519 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1520 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1521 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1522 domainguid=None, policyguid=None, policyguid_dc=None,
1523 invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1524 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1525 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1526 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1527 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1528 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1532 :note: caution, this wipes all existing data!
1535 if domainsid is None:
1536 domainsid = security.random_sid()
1538 domainsid = security.dom_sid(domainsid)
1540 # create/adapt the group policy GUIDs
1541 # Default GUID for default policy are described at
1542 # "How Core Group Policy Works"
1543 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1544 if policyguid is None:
1545 policyguid = DEFAULT_POLICY_GUID
1546 policyguid = policyguid.upper()
1547 if policyguid_dc is None:
1548 policyguid_dc = DEFAULT_DC_POLICY_GUID
1549 policyguid_dc = policyguid_dc.upper()
1551 if adminpass is None:
1552 adminpass = samba.generate_random_password(12, 32)
1553 if krbtgtpass is None:
1554 krbtgtpass = samba.generate_random_password(128, 255)
1555 if machinepass is None:
1556 machinepass = samba.generate_random_password(128, 255)
1558 dnspass = samba.generate_random_password(128, 255)
1559 if ldapadminpass is None:
1560 # Make a new, random password between Samba and it's LDAP server
1561 ldapadminpass=samba.generate_random_password(128, 255)
1563 if backend_type is None:
1564 backend_type = "ldb"
1566 sid_generator = "internal"
1567 if backend_type == "fedora-ds":
1568 sid_generator = "backend"
1570 root_uid = findnss_uid([root or "root"])
1571 nobody_uid = findnss_uid([nobody or "nobody"])
1572 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1574 wheel_gid = findnss_gid(["wheel", "adm"])
1576 wheel_gid = findnss_gid([wheel])
1578 bind_gid = findnss_gid(["bind", "named"])
1582 if targetdir is not None:
1583 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1584 elif smbconf is None:
1585 smbconf = samba.param.default_path()
1586 if not os.path.exists(os.path.dirname(smbconf)):
1587 os.makedirs(os.path.dirname(smbconf))
1589 # only install a new smb.conf if there isn't one there already
1590 if os.path.exists(smbconf):
1591 # if Samba Team members can't figure out the weird errors
1592 # loading an empty smb.conf gives, then we need to be smarter.
1593 # Pretend it just didn't exist --abartlet
1594 data = open(smbconf, 'r').read()
1595 data = data.lstrip()
1596 if data is None or data == "":
1597 make_smbconf(smbconf, hostname, domain, realm,
1598 serverrole, targetdir, sid_generator, useeadb,
1601 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1602 targetdir, sid_generator, useeadb, lp=lp)
1605 lp = samba.param.LoadParm()
1607 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1608 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1609 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1611 paths = provision_paths_from_lp(lp, names.dnsdomain)
1613 paths.bind_gid = bind_gid
1616 logger.info("Looking up IPv4 addresses")
1617 hostips = interface_ips_v4(lp)
1618 if len(hostips) > 0:
1620 if len(hostips) > 1:
1621 logger.warning("More than one IPv4 address found. Using %s",
1623 if hostip == "127.0.0.1":
1626 logger.warning("No IPv4 address will be assigned")
1629 logger.info("Looking up IPv6 addresses")
1630 hostips = interface_ips_v6(lp, linklocal=False)
1632 hostip6 = hostips[0]
1633 if len(hostips) > 1:
1634 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1636 logger.warning("No IPv6 address will be assigned")
1638 if serverrole is None:
1639 serverrole = lp.get("server role")
1641 assert serverrole in ("domain controller", "member server", "standalone")
1642 if invocationid is None:
1643 invocationid = str(uuid.uuid4())
1645 if not os.path.exists(paths.private_dir):
1646 os.mkdir(paths.private_dir)
1647 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1648 os.mkdir(os.path.join(paths.private_dir, "tls"))
1650 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1652 schema = Schema(domainsid, invocationid=invocationid,
1653 schemadn=names.schemadn)
1655 if backend_type == "ldb":
1656 provision_backend = LDBBackend(backend_type, paths=paths,
1657 lp=lp, credentials=credentials,
1658 names=names, logger=logger)
1659 elif backend_type == "existing":
1660 provision_backend = ExistingBackend(backend_type, paths=paths,
1661 lp=lp, credentials=credentials,
1662 names=names, logger=logger,
1663 ldap_backend_forced_uri=ldap_backend_forced_uri)
1664 elif backend_type == "fedora-ds":
1665 provision_backend = FDSBackend(backend_type, paths=paths,
1666 lp=lp, credentials=credentials,
1667 names=names, logger=logger, domainsid=domainsid,
1668 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1669 slapd_path=slapd_path,
1670 ldap_backend_extra_port=ldap_backend_extra_port,
1671 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1672 setup_ds_path=setup_ds_path,
1673 ldap_backend_forced_uri=ldap_backend_forced_uri)
1674 elif backend_type == "openldap":
1675 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1676 lp=lp, credentials=credentials,
1677 names=names, logger=logger, domainsid=domainsid,
1678 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1679 slapd_path=slapd_path,
1680 ldap_backend_extra_port=ldap_backend_extra_port,
1681 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1683 ldap_backend_forced_uri=ldap_backend_forced_uri)
1685 raise ValueError("Unknown LDAP backend type selected")
1687 provision_backend.init()
1688 provision_backend.start()
1690 # only install a new shares config db if there is none
1691 if not os.path.exists(paths.shareconf):
1692 logger.info("Setting up share.ldb")
1693 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1695 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1697 logger.info("Setting up secrets.ldb")
1698 secrets_ldb = setup_secretsdb(paths,
1699 session_info=session_info,
1700 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1703 logger.info("Setting up the registry")
1704 setup_registry(paths.hklm, session_info,
1707 logger.info("Setting up the privileges database")
1708 setup_privileges(paths.privilege, session_info, lp=lp)
1710 logger.info("Setting up idmap db")
1711 idmap = setup_idmapdb(paths.idmapdb,
1712 session_info=session_info, lp=lp)
1714 logger.info("Setting up SAM db")
1715 samdb = setup_samdb(paths.samdb, session_info,
1716 provision_backend, lp, names, logger=logger,
1717 domainsid=domainsid, schema=schema, domainguid=domainguid,
1718 policyguid=policyguid, policyguid_dc=policyguid_dc,
1719 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1720 invocationid=invocationid, machinepass=machinepass,
1721 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1722 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1723 next_rid=next_rid, dc_rid=dc_rid)
1725 if serverrole == "domain controller":
1726 if paths.netlogon is None:
1727 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1728 logger.info("Please either remove %s or see the template at %s" %
1729 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1730 assert paths.netlogon is not None
1732 if paths.sysvol is None:
1733 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1734 " are configuring a DC.")
1735 logger.info("Please either remove %s or see the template at %s" %
1736 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1737 assert paths.sysvol is not None
1739 if not os.path.isdir(paths.netlogon):
1740 os.makedirs(paths.netlogon, 0755)
1742 if samdb_fill == FILL_FULL:
1743 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1744 root_uid=root_uid, nobody_uid=nobody_uid,
1745 users_gid=users_gid, wheel_gid=wheel_gid)
1747 if serverrole == "domain controller":
1748 # Set up group policies (domain policy and domain controller
1750 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1752 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1753 domainsid, names.dnsdomain, names.domaindn, lp)
1755 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1756 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1757 { 'NTDSGUID' : names.ntdsguid })
1759 secretsdb_self_join(secrets_ldb, domain=names.domain,
1760 realm=names.realm, dnsdomain=names.dnsdomain,
1761 netbiosname=names.netbiosname, domainsid=domainsid,
1762 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1764 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1765 # In future, this might be determined from some configuration
1766 kerberos_enctypes = str(ENC_ALL_TYPES)
1769 msg = ldb.Message(ldb.Dn(samdb,
1770 samdb.searchone("distinguishedName",
1771 expression="samAccountName=%s$" % names.netbiosname,
1772 scope=ldb.SCOPE_SUBTREE)))
1773 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1774 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1775 name="msDS-SupportedEncryptionTypes")
1777 except ldb.LdbError, (enum, estr):
1778 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1779 # It might be that this attribute does not exist in this schema
1782 if serverrole == "domain controller":
1783 secretsdb_setup_dns(secrets_ldb, names,
1784 paths.private_dir, realm=names.realm,
1785 dnsdomain=names.dnsdomain,
1786 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1788 setup_ad_dns(samdb, names=names, hostip=hostip, hostip6=hostip6)
1790 domainguid = samdb.searchone(basedn=domaindn,
1791 attribute="objectGUID")
1792 assert isinstance(domainguid, str)
1794 # Only make a zone file on the first DC, it should be
1795 # replicated with DNS replication
1796 create_zone_file(lp, logger, paths, targetdir,
1797 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1798 hostname=names.hostname, realm=names.realm,
1799 domainguid=domainguid, ntdsguid=names.ntdsguid)
1801 create_named_conf(paths, realm=names.realm,
1802 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1804 create_named_txt(paths.namedtxt,
1805 realm=names.realm, dnsdomain=names.dnsdomain,
1806 dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1807 private_dir=paths.private_dir,
1808 keytab_name=paths.dns_keytab)
1809 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1810 logger.info("and %s for further documentation required for secure DNS "
1811 "updates", paths.namedtxt)
1813 lastProvisionUSNs = get_last_provision_usn(samdb)
1814 maxUSN = get_max_usn(samdb, str(names.rootdn))
1815 if lastProvisionUSNs is not None:
1816 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1818 set_provision_usn(samdb, 0, maxUSN, invocationid)
1820 create_krb5_conf(paths.krb5conf,
1821 dnsdomain=names.dnsdomain, hostname=names.hostname,
1823 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1824 "generated at %s", paths.krb5conf)
1826 if serverrole == "domain controller":
1827 create_dns_update_list(lp, logger, paths)
1829 provision_backend.post_setup()
1830 provision_backend.shutdown()
1832 create_phpldapadmin_config(paths.phpldapadminconfig,
1835 secrets_ldb.transaction_cancel()
1838 # Now commit the secrets.ldb to disk
1839 secrets_ldb.transaction_commit()
1841 # the commit creates the dns.keytab, now chown it
1842 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1843 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1845 os.chmod(dns_keytab_path, 0640)
1846 os.chown(dns_keytab_path, -1, paths.bind_gid)
1848 if not os.environ.has_key('SAMBA_SELFTEST'):
1849 logger.info("Failed to chown %s to bind gid %u",
1850 dns_keytab_path, paths.bind_gid)
1852 if samdb_fill != FILL_DRS:
1853 # fix any dangling GUIDs from the provision
1854 logger.info("Fixing provision GUIDs")
1855 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, quiet=True)
1856 samdb.transaction_start()
1857 # a small number of GUIDs are missing because of ordering issues in the
1859 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1860 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1861 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1862 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1863 scope=ldb.SCOPE_ONELEVEL,
1864 attrs=['ipsecOwnersReference',
1865 'ipsecFilterReference',
1866 'ipsecISAKMPReference',
1867 'ipsecNegotiationPolicyReference',
1868 'ipsecNFAReference'])
1869 samdb.transaction_commit()
1872 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1873 paths.phpldapadminconfig)
1875 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1876 logger.info("Server Role: %s" % serverrole)
1877 logger.info("Hostname: %s" % names.hostname)
1878 logger.info("NetBIOS Domain: %s" % names.domain)
1879 logger.info("DNS Domain: %s" % names.dnsdomain)
1880 logger.info("DOMAIN SID: %s" % str(domainsid))
1881 if samdb_fill == FILL_FULL:
1882 logger.info("Admin password: %s" % adminpass)
1883 if provision_backend.type is not "ldb":
1884 if provision_backend.credentials.get_bind_dn() is not None:
1885 logger.info("LDAP Backend Admin DN: %s" %
1886 provision_backend.credentials.get_bind_dn())
1888 logger.info("LDAP Admin User: %s" %
1889 provision_backend.credentials.get_username())
1891 logger.info("LDAP Admin Password: %s" %
1892 provision_backend.credentials.get_password())
1894 if provision_backend.slapd_command_escaped is not None:
1895 # now display slapd_command_file.txt to show how slapd must be
1897 logger.info("Use later the following commandline to start slapd, then Samba:")
1898 logger.info(provision_backend.slapd_command_escaped)
1899 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1900 provision_backend.ldapdir)
1902 result = ProvisionResult()
1903 result.domaindn = domaindn
1904 result.paths = paths
1906 result.samdb = samdb
1910 def provision_become_dc(smbconf=None, targetdir=None,
1911 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1912 serverdn=None, domain=None, hostname=None, domainsid=None,
1913 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1914 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1915 root=None, nobody=None, users=None, wheel=None, backup=None,
1916 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1917 sitename=None, debuglevel=1):
1919 logger = logging.getLogger("provision")
1920 samba.set_debug_level(debuglevel)
1922 res = provision(logger, system_session(), None,
1923 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1924 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1925 configdn=configdn, serverdn=serverdn, domain=domain,
1926 hostname=hostname, hostip=None, domainsid=domainsid,
1927 machinepass=machinepass, serverrole="domain controller",
1929 res.lp.set("debuglevel", str(debuglevel))
1933 def create_phpldapadmin_config(path, ldapi_uri):
1934 """Create a PHP LDAP admin configuration file.
1936 :param path: Path to write the configuration to.
1938 setup_file(setup_path("phpldapadmin-config.php"), path,
1939 {"S4_LDAPI_URI": ldapi_uri})
1942 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1943 hostip, hostip6, hostname, realm, domainguid,
1945 """Write out a DNS zone file, from the info in the current database.
1947 :param paths: paths object
1948 :param dnsdomain: DNS Domain name
1949 :param domaindn: DN of the Domain
1950 :param hostip: Local IPv4 IP
1951 :param hostip6: Local IPv6 IP
1952 :param hostname: Local hostname
1953 :param realm: Realm name
1954 :param domainguid: GUID of the domain.
1955 :param ntdsguid: GUID of the hosts nTDSDSA record.
1957 assert isinstance(domainguid, str)
1959 if hostip6 is not None:
1960 hostip6_base_line = " IN AAAA " + hostip6
1961 hostip6_host_line = hostname + " IN AAAA " + hostip6
1962 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1964 hostip6_base_line = ""
1965 hostip6_host_line = ""
1966 gc_msdcs_ip6_line = ""
1968 if hostip is not None:
1969 hostip_base_line = " IN A " + hostip
1970 hostip_host_line = hostname + " IN A " + hostip
1971 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1973 hostip_base_line = ""
1974 hostip_host_line = ""
1975 gc_msdcs_ip_line = ""
1977 dns_dir = os.path.dirname(paths.dns)
1980 shutil.rmtree(dns_dir, True)
1984 os.mkdir(dns_dir, 0775)
1986 # we need to freeze the zone while we update the contents
1987 if targetdir is None:
1988 rndc = ' '.join(lp.get("rndc command"))
1989 os.system(rndc + " freeze " + lp.get("realm"))
1991 setup_file(setup_path("provision.zone"), paths.dns, {
1992 "HOSTNAME": hostname,
1993 "DNSDOMAIN": dnsdomain,
1995 "HOSTIP_BASE_LINE": hostip_base_line,
1996 "HOSTIP_HOST_LINE": hostip_host_line,
1997 "DOMAINGUID": domainguid,
1998 "DATESTRING": time.strftime("%Y%m%d%H"),
1999 "DEFAULTSITE": DEFAULTSITE,
2000 "NTDSGUID": ntdsguid,
2001 "HOSTIP6_BASE_LINE": hostip6_base_line,
2002 "HOSTIP6_HOST_LINE": hostip6_host_line,
2003 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
2004 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
2007 # note that we use no variable substitution on this file
2008 # the substitution is done at runtime by samba_dnsupdate
2009 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2011 # and the SPN update list
2012 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2014 if paths.bind_gid is not None:
2016 os.chown(dns_dir, -1, paths.bind_gid)
2017 os.chown(paths.dns, -1, paths.bind_gid)
2018 # chmod needed to cope with umask
2019 os.chmod(dns_dir, 0775)
2020 os.chmod(paths.dns, 0664)
2022 if not os.environ.has_key('SAMBA_SELFTEST'):
2023 logger.error("Failed to chown %s to bind gid %u" % (
2024 dns_dir, paths.bind_gid))
2026 if targetdir is None:
2027 os.system(rndc + " unfreeze " + lp.get("realm"))
2030 def create_dns_update_list(lp, logger, paths):
2031 """Write out a dns_update_list file"""
2032 # note that we use no variable substitution on this file
2033 # the substitution is done at runtime by samba_dnsupdate
2034 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2035 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2038 def create_named_conf(paths, realm, dnsdomain,
2040 """Write out a file containing zone statements suitable for inclusion in a
2041 named.conf file (including GSS-TSIG configuration).
2043 :param paths: all paths
2044 :param realm: Realm name
2045 :param dnsdomain: DNS Domain name
2046 :param private_dir: Path to private directory
2047 :param keytab_name: File name of DNS keytab file
2050 setup_file(setup_path("named.conf"), paths.namedconf, {
2051 "DNSDOMAIN": dnsdomain,
2053 "ZONE_FILE": paths.dns,
2054 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2055 "NAMED_CONF": paths.namedconf,
2056 "NAMED_CONF_UPDATE": paths.namedconf_update
2059 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2062 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2064 """Write out a file containing zone statements suitable for inclusion in a
2065 named.conf file (including GSS-TSIG configuration).
2067 :param path: Path of the new named.conf file.
2068 :param realm: Realm name
2069 :param dnsdomain: DNS Domain name
2070 :param private_dir: Path to private directory
2071 :param keytab_name: File name of DNS keytab file
2073 setup_file(setup_path("named.txt"), path, {
2074 "DNSDOMAIN": dnsdomain,
2075 "DNSNAME" : dnsname,
2077 "DNS_KEYTAB": keytab_name,
2078 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2079 "PRIVATE_DIR": private_dir
2083 def create_krb5_conf(path, dnsdomain, hostname, realm):
2084 """Write out a file containing zone statements suitable for inclusion in a
2085 named.conf file (including GSS-TSIG configuration).
2087 :param path: Path of the new named.conf file.
2088 :param dnsdomain: DNS Domain name
2089 :param hostname: Local hostname
2090 :param realm: Realm name
2092 setup_file(setup_path("krb5.conf"), path, {
2093 "DNSDOMAIN": dnsdomain,
2094 "HOSTNAME": hostname,
2099 class ProvisioningError(Exception):
2100 """A generic provision error."""
2102 def __init__(self, value):
2106 return "ProvisioningError: " + self.value
2109 class InvalidNetbiosName(Exception):
2110 """A specified name was not a valid NetBIOS name."""
2111 def __init__(self, name):
2112 super(InvalidNetbiosName, self).__init__(
2113 "The name '%r' is not a valid NetBIOS name" % name)