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):
443 def check_install(lp, session_info, credentials):
444 """Check whether the current install seems ok.
446 :param lp: Loadparm context
447 :param session_info: Session information
448 :param credentials: Credentials
450 if lp.get("realm") == "":
451 raise Exception("Realm empty")
452 samdb = Ldb(lp.samdb_url(), session_info=session_info,
453 credentials=credentials, lp=lp)
454 if len(samdb.search("(cn=Administrator)")) != 1:
455 raise ProvisioningError("No administrator account found")
458 def findnss(nssfn, names):
459 """Find a user or group from a list of possibilities.
461 :param nssfn: NSS Function to try (should raise KeyError if not found)
462 :param names: Names to check.
463 :return: Value return by first names list.
470 raise KeyError("Unable to find user/group in %r" % names)
473 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
474 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
477 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
478 """Setup a ldb in the private dir.
480 :param ldb: LDB file to import data into
481 :param ldif_path: Path of the LDIF file to load
482 :param subst_vars: Optional variables to subsitute in LDIF.
483 :param nocontrols: Optional list of controls, can be None for no controls
485 assert isinstance(ldif_path, str)
486 data = read_and_sub_file(ldif_path, subst_vars)
487 ldb.add_ldif(data, controls)
490 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
491 """Modify a ldb in the private dir.
493 :param ldb: LDB object.
494 :param ldif_path: LDIF file path.
495 :param subst_vars: Optional dictionary with substitution variables.
497 data = read_and_sub_file(ldif_path, subst_vars)
498 ldb.modify_ldif(data, controls)
501 def setup_ldb(ldb, ldif_path, subst_vars):
502 """Import a LDIF a file into a LDB handle, optionally substituting
505 :note: Either all LDIF data will be added or none (using transactions).
507 :param ldb: LDB file to import into.
508 :param ldif_path: Path to the LDIF file.
509 :param subst_vars: Dictionary with substitution variables.
511 assert ldb is not None
512 ldb.transaction_start()
514 setup_add_ldif(ldb, ldif_path, subst_vars)
516 ldb.transaction_cancel()
519 ldb.transaction_commit()
522 def provision_paths_from_lp(lp, dnsdomain):
523 """Set the default paths for provisioning.
525 :param lp: Loadparm context.
526 :param dnsdomain: DNS Domain name
528 paths = ProvisionPaths()
529 paths.private_dir = lp.get("private dir")
531 # This is stored without path prefix for the "privateKeytab" attribute in
532 # "secrets_dns.ldif".
533 paths.dns_keytab = "dns.keytab"
534 paths.keytab = "secrets.keytab"
536 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
537 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
538 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
539 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
540 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
541 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
542 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
543 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
544 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
545 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
546 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
547 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
548 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
549 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
550 paths.phpldapadminconfig = os.path.join(paths.private_dir,
551 "phpldapadmin-config.php")
552 paths.hklm = "hklm.ldb"
553 paths.hkcr = "hkcr.ldb"
554 paths.hkcu = "hkcu.ldb"
555 paths.hku = "hku.ldb"
556 paths.hkpd = "hkpd.ldb"
557 paths.hkpt = "hkpt.ldb"
558 paths.sysvol = lp.get("path", "sysvol")
559 paths.netlogon = lp.get("path", "netlogon")
560 paths.smbconf = lp.configfile
564 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
565 serverrole=None, rootdn=None, domaindn=None, configdn=None,
566 schemadn=None, serverdn=None, sitename=None):
567 """Guess configuration settings to use."""
570 hostname = socket.gethostname().split(".")[0]
572 netbiosname = lp.get("netbios name")
573 if netbiosname is None:
574 netbiosname = hostname
575 # remove forbidden chars
577 for x in netbiosname:
578 if x.isalnum() or x in VALID_NETBIOS_CHARS:
579 newnbname = "%s%c" % (newnbname, x)
580 # force the length to be <16
581 netbiosname = newnbname[0:15]
582 assert netbiosname is not None
583 netbiosname = netbiosname.upper()
584 if not valid_netbios_name(netbiosname):
585 raise InvalidNetbiosName(netbiosname)
587 if dnsdomain is None:
588 dnsdomain = lp.get("realm")
589 if dnsdomain is None or dnsdomain == "":
590 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
592 dnsdomain = dnsdomain.lower()
594 if serverrole is None:
595 serverrole = lp.get("server role")
596 if serverrole is None:
597 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
599 serverrole = serverrole.lower()
601 realm = dnsdomain.upper()
603 if lp.get("realm") == "":
604 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
606 if lp.get("realm").upper() != realm:
607 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))
609 if lp.get("server role").lower() != serverrole:
610 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))
612 if serverrole == "domain controller":
614 # This will, for better or worse, default to 'WORKGROUP'
615 domain = lp.get("workgroup")
616 domain = domain.upper()
618 if lp.get("workgroup").upper() != domain:
619 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))
622 domaindn = samba.dn_from_dns_name(dnsdomain)
624 if domain == netbiosname:
625 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
629 domaindn = "DC=" + netbiosname
631 if not valid_netbios_name(domain):
632 raise InvalidNetbiosName(domain)
634 if hostname.upper() == realm:
635 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
636 if netbiosname.upper() == realm:
637 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
639 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
645 configdn = "CN=Configuration," + rootdn
647 schemadn = "CN=Schema," + configdn
652 names = ProvisionNames()
653 names.rootdn = rootdn
654 names.domaindn = domaindn
655 names.configdn = configdn
656 names.schemadn = schemadn
657 names.ldapmanagerdn = "CN=Manager," + rootdn
658 names.dnsdomain = dnsdomain
659 names.domain = domain
661 names.netbiosname = netbiosname
662 names.hostname = hostname
663 names.sitename = sitename
664 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
665 netbiosname, sitename, configdn)
670 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
671 targetdir, sid_generator="internal", eadb=False, lp=None):
672 """Create a new smb.conf file based on a couple of basic settings.
674 assert smbconf is not None
676 hostname = socket.gethostname().split(".")[0]
677 netbiosname = hostname.upper()
678 # remove forbidden chars
680 for x in netbiosname:
681 if x.isalnum() or x in VALID_NETBIOS_CHARS:
682 newnbname = "%s%c" % (newnbname, x)
683 #force the length to be <16
684 netbiosname = newnbname[0:15]
686 netbiosname = hostname.upper()
688 if serverrole is None:
689 serverrole = "standalone"
691 assert serverrole in ("domain controller", "member server", "standalone")
692 if serverrole == "domain controller":
694 elif serverrole == "member server":
695 smbconfsuffix = "member"
696 elif serverrole == "standalone":
697 smbconfsuffix = "standalone"
699 if sid_generator is None:
700 sid_generator = "internal"
702 assert domain is not None
703 domain = domain.upper()
705 assert realm is not None
706 realm = realm.upper()
709 lp = samba.param.LoadParm()
710 #Load non-existant file
711 if os.path.exists(smbconf):
713 if eadb and not lp.get("posix:eadb"):
714 if targetdir is not None:
715 privdir = os.path.join(targetdir, "private")
717 privdir = lp.get("private dir")
718 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
720 if targetdir is not None:
721 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
722 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
723 statedir_line = "state directory = " + os.path.abspath(targetdir)
724 cachedir_line = "cache directory = " + os.path.abspath(targetdir)
726 lp.set("lock dir", os.path.abspath(targetdir))
727 lp.set("state directory", os.path.abspath(targetdir))
728 lp.set("cache directory", os.path.abspath(targetdir))
735 sysvol = os.path.join(lp.get("state directory"), "sysvol")
736 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
738 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
740 "NETBIOS_NAME": netbiosname,
743 "SERVERROLE": serverrole,
744 "NETLOGONPATH": netlogon,
745 "SYSVOLPATH": sysvol,
746 "PRIVATEDIR_LINE": privatedir_line,
747 "LOCKDIR_LINE": lockdir_line,
748 "STATEDIR_LINE": statedir_line,
749 "CACHEDIR_LINE": cachedir_line
752 # reload the smb.conf
755 # and dump it without any values that are the default
756 # this ensures that any smb.conf parameters that were set
757 # on the provision/join command line are set in the resulting smb.conf
758 f = open(smbconf, mode='w')
764 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
765 users_gid, wheel_gid):
766 """setup reasonable name mappings for sam names to unix names.
768 :param samdb: SamDB object.
769 :param idmap: IDmap db object.
770 :param sid: The domain sid.
771 :param domaindn: The domain DN.
772 :param root_uid: uid of the UNIX root user.
773 :param nobody_uid: uid of the UNIX nobody user.
774 :param users_gid: gid of the UNIX users group.
775 :param wheel_gid: gid of the UNIX wheel group.
777 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
778 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
780 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
781 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
784 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
785 provision_backend, names, schema, serverrole,
787 """Setup the partitions for the SAM database.
789 Alternatively, provision() may call this, and then populate the database.
791 :note: This will wipe the Sam Database!
793 :note: This function always removes the local SAM LDB file. The erase
794 parameter controls whether to erase the existing data, which
795 may not be stored locally but in LDAP.
798 assert session_info is not None
800 # We use options=["modules:"] to stop the modules loading - we
801 # just want to wipe and re-initialise the database, not start it up
804 os.unlink(samdb_path)
808 samdb = Ldb(url=samdb_path, session_info=session_info,
809 lp=lp, options=["modules:"])
811 ldap_backend_line = "# No LDAP backend"
812 if provision_backend.type is not "ldb":
813 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
815 samdb.transaction_start()
817 logger.info("Setting up sam.ldb partitions and settings")
818 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
819 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
820 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
821 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
822 "LDAP_BACKEND_LINE": ldap_backend_line,
826 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
827 "BACKEND_TYPE": provision_backend.type,
828 "SERVER_ROLE": serverrole
831 logger.info("Setting up sam.ldb rootDSE")
832 setup_samdb_rootdse(samdb, names)
834 samdb.transaction_cancel()
837 samdb.transaction_commit()
840 def secretsdb_self_join(secretsdb, domain,
841 netbiosname, machinepass, domainsid=None,
842 realm=None, dnsdomain=None,
844 key_version_number=1,
845 secure_channel_type=SEC_CHAN_WKSTA):
846 """Add domain join-specific bits to a secrets database.
848 :param secretsdb: Ldb Handle to the secrets database
849 :param machinepass: Machine password
851 attrs = ["whenChanged",
858 if realm is not None:
859 if dnsdomain is None:
860 dnsdomain = realm.lower()
861 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
864 shortname = netbiosname.lower()
866 # We don't need to set msg["flatname"] here, because rdn_name will handle
867 # it, and it causes problems for modifies anyway
868 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
869 msg["secureChannelType"] = [str(secure_channel_type)]
870 msg["objectClass"] = ["top", "primaryDomain"]
871 if dnsname is not None:
872 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
873 msg["realm"] = [realm]
874 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
875 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
876 msg["privateKeytab"] = ["secrets.keytab"]
878 msg["secret"] = [machinepass]
879 msg["samAccountName"] = ["%s$" % netbiosname]
880 msg["secureChannelType"] = [str(secure_channel_type)]
881 if domainsid is not None:
882 msg["objectSid"] = [ndr_pack(domainsid)]
884 # This complex expression tries to ensure that we don't have more
885 # than one record for this SID, realm or netbios domain at a time,
886 # but we don't delete the old record that we are about to modify,
887 # because that would delete the keytab and previous password.
888 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
889 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
890 scope=ldb.SCOPE_ONELEVEL)
893 secretsdb.delete(del_msg.dn)
895 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
898 msg["priorSecret"] = [res[0]["secret"][0]]
899 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
902 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
907 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
913 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
914 secretsdb.modify(msg)
915 secretsdb.rename(res[0].dn, msg.dn)
917 spn = [ 'HOST/%s' % shortname ]
918 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
919 # we are a domain controller then we add servicePrincipalName
920 # entries for the keytab code to update.
921 spn.extend([ 'HOST/%s' % dnsname ])
922 msg["servicePrincipalName"] = spn
927 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
928 dnsdomain, dns_keytab_path, dnspass):
929 """Add DNS specific bits to a secrets database.
931 :param secretsdb: Ldb Handle to the secrets database
932 :param machinepass: Machine password
935 os.unlink(os.path.join(private_dir, dns_keytab_path))
939 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
941 "DNSDOMAIN": dnsdomain,
942 "DNS_KEYTAB": dns_keytab_path,
943 "DNSPASS_B64": b64encode(dnspass),
944 "HOSTNAME": names.hostname,
945 "DNSNAME" : '%s.%s' % (
946 names.netbiosname.lower(), names.dnsdomain.lower())
950 def setup_secretsdb(paths, session_info, backend_credentials, lp):
951 """Setup the secrets database.
953 :note: This function does not handle exceptions and transaction on purpose,
954 it's up to the caller to do this job.
956 :param path: Path to the secrets database.
957 :param session_info: Session info.
958 :param credentials: Credentials
959 :param lp: Loadparm context
960 :return: LDB handle for the created secrets database
962 if os.path.exists(paths.secrets):
963 os.unlink(paths.secrets)
965 keytab_path = os.path.join(paths.private_dir, paths.keytab)
966 if os.path.exists(keytab_path):
967 os.unlink(keytab_path)
969 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
970 if os.path.exists(dns_keytab_path):
971 os.unlink(dns_keytab_path)
975 secrets_ldb = Ldb(path, session_info=session_info,
978 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
979 secrets_ldb = Ldb(path, session_info=session_info,
981 secrets_ldb.transaction_start()
983 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
985 if (backend_credentials is not None and
986 backend_credentials.authentication_requested()):
987 if backend_credentials.get_bind_dn() is not None:
988 setup_add_ldif(secrets_ldb,
989 setup_path("secrets_simple_ldap.ldif"), {
990 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
991 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
994 setup_add_ldif(secrets_ldb,
995 setup_path("secrets_sasl_ldap.ldif"), {
996 "LDAPADMINUSER": backend_credentials.get_username(),
997 "LDAPADMINREALM": backend_credentials.get_realm(),
998 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
1003 secrets_ldb.transaction_cancel()
1007 def setup_privileges(path, session_info, lp):
1008 """Setup the privileges database.
1010 :param path: Path to the privileges database.
1011 :param session_info: Session info.
1012 :param credentials: Credentials
1013 :param lp: Loadparm context
1014 :return: LDB handle for the created secrets database
1016 if os.path.exists(path):
1018 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1019 privilege_ldb.erase()
1020 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1023 def setup_registry(path, session_info, lp):
1024 """Setup the registry.
1026 :param path: Path to the registry database
1027 :param session_info: Session information
1028 :param credentials: Credentials
1029 :param lp: Loadparm context
1031 reg = samba.registry.Registry()
1032 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1033 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1034 provision_reg = setup_path("provision.reg")
1035 assert os.path.exists(provision_reg)
1036 reg.diff_apply(provision_reg)
1039 def setup_idmapdb(path, session_info, lp):
1040 """Setup the idmap database.
1042 :param path: path to the idmap database
1043 :param session_info: Session information
1044 :param credentials: Credentials
1045 :param lp: Loadparm context
1047 if os.path.exists(path):
1050 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1052 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1056 def setup_samdb_rootdse(samdb, names):
1057 """Setup the SamDB rootdse.
1059 :param samdb: Sam Database handle
1061 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1062 "SCHEMADN": names.schemadn,
1063 "DOMAINDN": names.domaindn,
1064 "ROOTDN" : names.rootdn,
1065 "CONFIGDN": names.configdn,
1066 "SERVERDN": names.serverdn,
1070 def setup_self_join(samdb, names, machinepass, dnspass,
1071 domainsid, next_rid, invocationid,
1072 policyguid, policyguid_dc, domainControllerFunctionality,
1073 ntdsguid, dc_rid=None):
1074 """Join a host to its own domain."""
1075 assert isinstance(invocationid, str)
1076 if ntdsguid is not None:
1077 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1084 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1085 "CONFIGDN": names.configdn,
1086 "SCHEMADN": names.schemadn,
1087 "DOMAINDN": names.domaindn,
1088 "SERVERDN": names.serverdn,
1089 "INVOCATIONID": invocationid,
1090 "NETBIOSNAME": names.netbiosname,
1091 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1092 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1093 "DOMAINSID": str(domainsid),
1094 "DCRID": str(dc_rid),
1095 "SAMBA_VERSION_STRING": version,
1096 "NTDSGUID": ntdsguid_line,
1097 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1098 domainControllerFunctionality)})
1100 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1101 "POLICYGUID": policyguid,
1102 "POLICYGUID_DC": policyguid_dc,
1103 "DNSDOMAIN": names.dnsdomain,
1104 "DOMAINDN": names.domaindn})
1106 # add the NTDSGUID based SPNs
1107 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1108 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1109 expression="", scope=ldb.SCOPE_BASE)
1110 assert isinstance(names.ntdsguid, str)
1112 # Setup fSMORoleOwner entries to point at the newly created DC entry
1113 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1114 "DOMAINDN": names.domaindn,
1115 "CONFIGDN": names.configdn,
1116 "SCHEMADN": names.schemadn,
1117 "DEFAULTSITE": names.sitename,
1118 "SERVERDN": names.serverdn,
1119 "NETBIOSNAME": names.netbiosname,
1120 "RIDALLOCATIONSTART": str(next_rid + 100),
1121 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1124 # This is Samba4 specific and should be replaced by the correct
1125 # DNS AD-style setup
1126 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1127 "DNSDOMAIN": names.dnsdomain,
1128 "DOMAINDN": names.domaindn,
1129 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1130 "HOSTNAME" : names.hostname,
1131 "DNSNAME" : '%s.%s' % (
1132 names.netbiosname.lower(), names.dnsdomain.lower())
1136 def getpolicypath(sysvolpath, dnsdomain, guid):
1137 """Return the physical path of policy given its guid.
1139 :param sysvolpath: Path to the sysvol folder
1140 :param dnsdomain: DNS name of the AD domain
1141 :param guid: The GUID of the policy
1142 :return: A string with the complete path to the policy folder
1146 guid = "{%s}" % guid
1147 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1151 def create_gpo_struct(policy_path):
1152 if not os.path.exists(policy_path):
1153 os.makedirs(policy_path, 0775)
1154 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1155 "[General]\r\nVersion=0")
1156 p = os.path.join(policy_path, "MACHINE")
1157 if not os.path.exists(p):
1158 os.makedirs(p, 0775)
1159 p = os.path.join(policy_path, "USER")
1160 if not os.path.exists(p):
1161 os.makedirs(p, 0775)
1164 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1165 """Create the default GPO for a domain
1167 :param sysvolpath: Physical path for the sysvol folder
1168 :param dnsdomain: DNS domain name of the AD domain
1169 :param policyguid: GUID of the default domain policy
1170 :param policyguid_dc: GUID of the default domain controler policy
1172 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1173 create_gpo_struct(policy_path)
1175 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1176 create_gpo_struct(policy_path)
1179 def setup_samdb(path, session_info, provision_backend, lp, names,
1180 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1181 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1182 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1183 next_rid=None, dc_rid=None):
1184 """Setup a complete SAM Database.
1186 :note: This will wipe the main SAM database file!
1189 if next_rid is None:
1192 # Provision does not make much sense values larger than 1000000000
1193 # as the upper range of the rIDAvailablePool is 1073741823 and
1194 # we don't want to create a domain that cannot allocate rids.
1195 if next_rid < 1000 or next_rid > 1000000000:
1196 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1197 error += "the valid range is %u-%u. The default is %u." % (
1198 1000, 1000000000, 1000)
1199 raise ProvisioningError(error)
1201 # ATTENTION: Do NOT change these default values without discussion with the
1202 # team and/or release manager. They have a big impact on the whole program!
1203 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1205 if dom_for_fun_level is None:
1206 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1208 if dom_for_fun_level > domainControllerFunctionality:
1209 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!")
1211 domainFunctionality = dom_for_fun_level
1212 forestFunctionality = dom_for_fun_level
1214 # Also wipes the database
1215 setup_samdb_partitions(path, logger=logger, lp=lp,
1216 provision_backend=provision_backend, session_info=session_info,
1217 names=names, serverrole=serverrole, schema=schema)
1220 schema = Schema(domainsid, schemadn=names.schemadn)
1222 # Load the database, but don's load the global schema and don't connect
1224 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1225 credentials=provision_backend.credentials, lp=lp,
1226 global_schema=False, am_rodc=am_rodc)
1228 logger.info("Pre-loading the Samba 4 and AD schema")
1230 # Load the schema from the one we computed earlier
1231 samdb.set_schema(schema)
1233 # Set the NTDS settings DN manually - in order to have it already around
1234 # before the provisioned tree exists and we connect
1235 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1237 # And now we can connect to the DB - the schema won't be loaded from the
1241 if fill == FILL_DRS:
1244 samdb.transaction_start()
1246 # Set the domain functionality levels onto the database.
1247 # Various module (the password_hash module in particular) need
1248 # to know what level of AD we are emulating.
1250 # These will be fixed into the database via the database
1251 # modifictions below, but we need them set from the start.
1252 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1253 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1254 samdb.set_opaque_integer("domainControllerFunctionality",
1255 domainControllerFunctionality)
1257 samdb.set_domain_sid(str(domainsid))
1258 samdb.set_invocation_id(invocationid)
1260 logger.info("Adding DomainDN: %s" % names.domaindn)
1262 # impersonate domain admin
1263 admin_session_info = admin_session(lp, str(domainsid))
1264 samdb.set_session_info(admin_session_info)
1265 if domainguid is not None:
1266 domainguid_line = "objectGUID: %s\n-" % domainguid
1268 domainguid_line = ""
1270 descr = b64encode(get_domain_descriptor(domainsid))
1271 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1272 "DOMAINDN": names.domaindn,
1273 "DOMAINSID": str(domainsid),
1274 "DESCRIPTOR": descr,
1275 "DOMAINGUID": domainguid_line
1278 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1279 "DOMAINDN": names.domaindn,
1280 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1281 "NEXTRID": str(next_rid),
1282 "DEFAULTSITE": names.sitename,
1283 "CONFIGDN": names.configdn,
1284 "POLICYGUID": policyguid,
1285 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1286 "SAMBA_VERSION_STRING": version
1289 logger.info("Adding configuration container")
1290 descr = b64encode(get_config_descriptor(domainsid))
1291 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1292 "CONFIGDN": names.configdn,
1293 "DESCRIPTOR": descr,
1296 # Now register this container in the root of the forest
1297 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1298 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1301 # The LDIF here was created when the Schema object was constructed
1302 logger.info("Setting up sam.ldb schema")
1303 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1304 samdb.modify_ldif(schema.schema_dn_modify)
1305 samdb.write_prefixes_from_schema()
1306 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1307 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1308 {"SCHEMADN": names.schemadn})
1310 logger.info("Reopening sam.ldb with new schema")
1312 samdb.transaction_cancel()
1315 samdb.transaction_commit()
1317 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1318 credentials=provision_backend.credentials, lp=lp,
1319 global_schema=False, am_rodc=am_rodc)
1321 # Set the NTDS settings DN manually - in order to have it already around
1322 # before the provisioned tree exists and we connect
1323 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1326 samdb.transaction_start()
1328 samdb.invocation_id = invocationid
1330 logger.info("Setting up sam.ldb configuration data")
1331 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1332 "CONFIGDN": names.configdn,
1333 "NETBIOSNAME": names.netbiosname,
1334 "DEFAULTSITE": names.sitename,
1335 "DNSDOMAIN": names.dnsdomain,
1336 "DOMAIN": names.domain,
1337 "SCHEMADN": names.schemadn,
1338 "DOMAINDN": names.domaindn,
1339 "SERVERDN": names.serverdn,
1340 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1341 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1344 logger.info("Setting up display specifiers")
1345 display_specifiers_ldif = read_ms_ldif(
1346 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1347 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1348 {"CONFIGDN": names.configdn})
1349 check_all_substituted(display_specifiers_ldif)
1350 samdb.add_ldif(display_specifiers_ldif)
1352 logger.info("Adding users container")
1353 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1354 "DOMAINDN": names.domaindn})
1355 logger.info("Modifying users container")
1356 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1357 "DOMAINDN": names.domaindn})
1358 logger.info("Adding computers container")
1359 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1360 "DOMAINDN": names.domaindn})
1361 logger.info("Modifying computers container")
1362 setup_modify_ldif(samdb,
1363 setup_path("provision_computers_modify.ldif"), {
1364 "DOMAINDN": names.domaindn})
1365 logger.info("Setting up sam.ldb data")
1366 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1367 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1368 "DOMAINDN": names.domaindn,
1369 "NETBIOSNAME": names.netbiosname,
1370 "DEFAULTSITE": names.sitename,
1371 "CONFIGDN": names.configdn,
1372 "SERVERDN": names.serverdn,
1373 "RIDAVAILABLESTART": str(next_rid + 600),
1374 "POLICYGUID_DC": policyguid_dc
1377 setup_modify_ldif(samdb,
1378 setup_path("provision_basedn_references.ldif"), {
1379 "DOMAINDN": names.domaindn})
1381 setup_modify_ldif(samdb,
1382 setup_path("provision_configuration_references.ldif"), {
1383 "CONFIGDN": names.configdn,
1384 "SCHEMADN": names.schemadn})
1385 if fill == FILL_FULL:
1386 logger.info("Setting up sam.ldb users and groups")
1387 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1388 "DOMAINDN": names.domaindn,
1389 "DOMAINSID": str(domainsid),
1390 "CONFIGDN": names.configdn,
1391 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1392 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1395 logger.info("Setting up self join")
1396 setup_self_join(samdb, names=names, invocationid=invocationid,
1398 machinepass=machinepass,
1399 domainsid=domainsid,
1402 policyguid=policyguid,
1403 policyguid_dc=policyguid_dc,
1404 domainControllerFunctionality=domainControllerFunctionality,
1407 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1408 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1409 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1410 assert isinstance(names.ntdsguid, str)
1412 samdb.transaction_cancel()
1415 samdb.transaction_commit()
1420 FILL_NT4SYNC = "NT4SYNC"
1422 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1423 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)"
1426 def set_dir_acl(path, acl, lp, domsid):
1427 setntacl(lp, path, acl, domsid)
1428 for root, dirs, files in os.walk(path, topdown=False):
1430 setntacl(lp, os.path.join(root, name), acl, domsid)
1432 setntacl(lp, os.path.join(root, name), acl, domsid)
1435 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1436 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1439 :param sysvol: Physical path for the sysvol folder
1440 :param dnsdomain: The DNS name of the domain
1441 :param domainsid: The SID of the domain
1442 :param domaindn: The DN of the domain (ie. DC=...)
1443 :param samdb: An LDB object on the SAM db
1444 :param lp: an LP object
1447 # Set ACL for GPO root folder
1448 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1449 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1451 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1452 attrs=["cn", "nTSecurityDescriptor"],
1453 expression="", scope=ldb.SCOPE_ONELEVEL)
1456 acl = ndr_unpack(security.descriptor,
1457 str(policy["nTSecurityDescriptor"])).as_sddl()
1458 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1459 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1463 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1465 """Set the ACL for the sysvol share and the subfolders
1467 :param samdb: An LDB object on the SAM db
1468 :param netlogon: Physical path for the netlogon folder
1469 :param sysvol: Physical path for the sysvol folder
1470 :param gid: The GID of the "Domain adminstrators" group
1471 :param domainsid: The SID of the domain
1472 :param dnsdomain: The DNS name of the domain
1473 :param domaindn: The DN of the domain (ie. DC=...)
1477 os.chown(sysvol, -1, gid)
1483 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1484 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1485 for root, dirs, files in os.walk(sysvol, topdown=False):
1488 os.chown(os.path.join(root, name), -1, gid)
1489 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1492 os.chown(os.path.join(root, name), -1, gid)
1493 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1495 # Set acls on Policy folder and policies folders
1496 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1499 def interface_ips_v4(lp):
1500 '''return only IPv4 IPs'''
1501 ips = samba.interface_ips(lp, False)
1504 if i.find(':') == -1:
1508 def interface_ips_v6(lp, linklocal=False):
1509 '''return only IPv6 IPs'''
1510 ips = samba.interface_ips(lp, False)
1513 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1518 def provision(logger, session_info, credentials, smbconf=None,
1519 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1520 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1521 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1522 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1523 domainguid=None, policyguid=None, policyguid_dc=None,
1524 invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1525 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1526 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1527 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1528 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1529 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1533 :note: caution, this wipes all existing data!
1536 if domainsid is None:
1537 domainsid = security.random_sid()
1539 domainsid = security.dom_sid(domainsid)
1541 # create/adapt the group policy GUIDs
1542 # Default GUID for default policy are described at
1543 # "How Core Group Policy Works"
1544 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1545 if policyguid is None:
1546 policyguid = DEFAULT_POLICY_GUID
1547 policyguid = policyguid.upper()
1548 if policyguid_dc is None:
1549 policyguid_dc = DEFAULT_DC_POLICY_GUID
1550 policyguid_dc = policyguid_dc.upper()
1552 if adminpass is None:
1553 adminpass = samba.generate_random_password(12, 32)
1554 if krbtgtpass is None:
1555 krbtgtpass = samba.generate_random_password(128, 255)
1556 if machinepass is None:
1557 machinepass = samba.generate_random_password(128, 255)
1559 dnspass = samba.generate_random_password(128, 255)
1560 if ldapadminpass is None:
1561 # Make a new, random password between Samba and it's LDAP server
1562 ldapadminpass=samba.generate_random_password(128, 255)
1564 if backend_type is None:
1565 backend_type = "ldb"
1567 sid_generator = "internal"
1568 if backend_type == "fedora-ds":
1569 sid_generator = "backend"
1571 root_uid = findnss_uid([root or "root"])
1572 nobody_uid = findnss_uid([nobody or "nobody"])
1573 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1575 wheel_gid = findnss_gid(["wheel", "adm"])
1577 wheel_gid = findnss_gid([wheel])
1579 bind_gid = findnss_gid(["bind", "named"])
1583 if targetdir is not None:
1584 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1585 elif smbconf is None:
1586 smbconf = samba.param.default_path()
1587 if not os.path.exists(os.path.dirname(smbconf)):
1588 os.makedirs(os.path.dirname(smbconf))
1590 # only install a new smb.conf if there isn't one there already
1591 if os.path.exists(smbconf):
1592 # if Samba Team members can't figure out the weird errors
1593 # loading an empty smb.conf gives, then we need to be smarter.
1594 # Pretend it just didn't exist --abartlet
1595 data = open(smbconf, 'r').read()
1596 data = data.lstrip()
1597 if data is None or data == "":
1598 make_smbconf(smbconf, hostname, domain, realm,
1599 serverrole, targetdir, sid_generator, useeadb,
1602 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1603 targetdir, sid_generator, useeadb, lp=lp)
1606 lp = samba.param.LoadParm()
1608 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1609 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1610 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1611 sitename=sitename, rootdn=rootdn)
1612 paths = provision_paths_from_lp(lp, names.dnsdomain)
1614 paths.bind_gid = bind_gid
1617 logger.info("Looking up IPv4 addresses")
1618 hostips = interface_ips_v4(lp)
1619 if len(hostips) > 0:
1621 if len(hostips) > 1:
1622 logger.warning("More than one IPv4 address found. Using %s",
1624 if hostip == "127.0.0.1":
1627 logger.warning("No IPv4 address will be assigned")
1630 logger.info("Looking up IPv6 addresses")
1631 hostips = interface_ips_v6(lp, linklocal=False)
1633 hostip6 = hostips[0]
1634 if len(hostips) > 1:
1635 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1637 logger.warning("No IPv6 address will be assigned")
1639 if serverrole is None:
1640 serverrole = lp.get("server role")
1642 assert serverrole in ("domain controller", "member server", "standalone")
1643 if invocationid is None:
1644 invocationid = str(uuid.uuid4())
1646 if not os.path.exists(paths.private_dir):
1647 os.mkdir(paths.private_dir)
1648 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1649 os.mkdir(os.path.join(paths.private_dir, "tls"))
1651 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1653 schema = Schema(domainsid, invocationid=invocationid,
1654 schemadn=names.schemadn)
1656 if backend_type == "ldb":
1657 provision_backend = LDBBackend(backend_type, paths=paths,
1658 lp=lp, credentials=credentials,
1659 names=names, logger=logger)
1660 elif backend_type == "existing":
1661 provision_backend = ExistingBackend(backend_type, paths=paths,
1662 lp=lp, credentials=credentials,
1663 names=names, logger=logger,
1664 ldap_backend_forced_uri=ldap_backend_forced_uri)
1665 elif backend_type == "fedora-ds":
1666 provision_backend = FDSBackend(backend_type, paths=paths,
1667 lp=lp, credentials=credentials,
1668 names=names, logger=logger, domainsid=domainsid,
1669 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1670 slapd_path=slapd_path,
1671 ldap_backend_extra_port=ldap_backend_extra_port,
1672 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1673 setup_ds_path=setup_ds_path,
1674 ldap_backend_forced_uri=ldap_backend_forced_uri)
1675 elif backend_type == "openldap":
1676 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1677 lp=lp, credentials=credentials,
1678 names=names, logger=logger, domainsid=domainsid,
1679 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1680 slapd_path=slapd_path,
1681 ldap_backend_extra_port=ldap_backend_extra_port,
1682 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1684 ldap_backend_forced_uri=ldap_backend_forced_uri)
1686 raise ValueError("Unknown LDAP backend type selected")
1688 provision_backend.init()
1689 provision_backend.start()
1691 # only install a new shares config db if there is none
1692 if not os.path.exists(paths.shareconf):
1693 logger.info("Setting up share.ldb")
1694 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1696 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1698 logger.info("Setting up secrets.ldb")
1699 secrets_ldb = setup_secretsdb(paths,
1700 session_info=session_info,
1701 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1704 logger.info("Setting up the registry")
1705 setup_registry(paths.hklm, session_info,
1708 logger.info("Setting up the privileges database")
1709 setup_privileges(paths.privilege, session_info, lp=lp)
1711 logger.info("Setting up idmap db")
1712 idmap = setup_idmapdb(paths.idmapdb,
1713 session_info=session_info, lp=lp)
1715 logger.info("Setting up SAM db")
1716 samdb = setup_samdb(paths.samdb, session_info,
1717 provision_backend, lp, names, logger=logger,
1718 domainsid=domainsid, schema=schema, domainguid=domainguid,
1719 policyguid=policyguid, policyguid_dc=policyguid_dc,
1720 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1721 invocationid=invocationid, machinepass=machinepass,
1722 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1723 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1724 next_rid=next_rid, dc_rid=dc_rid)
1726 if serverrole == "domain controller":
1727 if paths.netlogon is None:
1728 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1729 logger.info("Please either remove %s or see the template at %s" %
1730 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1731 assert paths.netlogon is not None
1733 if paths.sysvol is None:
1734 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1735 " are configuring a DC.")
1736 logger.info("Please either remove %s or see the template at %s" %
1737 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1738 assert paths.sysvol is not None
1740 if not os.path.isdir(paths.netlogon):
1741 os.makedirs(paths.netlogon, 0755)
1743 if samdb_fill == FILL_FULL:
1744 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1745 root_uid=root_uid, nobody_uid=nobody_uid,
1746 users_gid=users_gid, wheel_gid=wheel_gid)
1748 if serverrole == "domain controller":
1749 # Set up group policies (domain policy and domain controller
1751 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1753 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1754 domainsid, names.dnsdomain, names.domaindn, lp)
1756 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1757 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1758 { 'NTDSGUID' : names.ntdsguid })
1760 secretsdb_self_join(secrets_ldb, domain=names.domain,
1761 realm=names.realm, dnsdomain=names.dnsdomain,
1762 netbiosname=names.netbiosname, domainsid=domainsid,
1763 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1765 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1766 # In future, this might be determined from some configuration
1767 kerberos_enctypes = str(ENC_ALL_TYPES)
1770 msg = ldb.Message(ldb.Dn(samdb,
1771 samdb.searchone("distinguishedName",
1772 expression="samAccountName=%s$" % names.netbiosname,
1773 scope=ldb.SCOPE_SUBTREE)))
1774 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1775 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1776 name="msDS-SupportedEncryptionTypes")
1778 except ldb.LdbError, (enum, estr):
1779 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1780 # It might be that this attribute does not exist in this schema
1783 if serverrole == "domain controller":
1784 secretsdb_setup_dns(secrets_ldb, names,
1785 paths.private_dir, realm=names.realm,
1786 dnsdomain=names.dnsdomain,
1787 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1789 setup_ad_dns(samdb, names=names, hostip=hostip, hostip6=hostip6)
1791 domainguid = samdb.searchone(basedn=domaindn,
1792 attribute="objectGUID")
1793 assert isinstance(domainguid, str)
1795 # Only make a zone file on the first DC, it should be
1796 # replicated with DNS replication
1797 create_zone_file(lp, logger, paths, targetdir,
1798 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1799 hostname=names.hostname, realm=names.realm,
1800 domainguid=domainguid, ntdsguid=names.ntdsguid)
1802 create_named_conf(paths, realm=names.realm,
1803 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1805 create_named_txt(paths.namedtxt,
1806 realm=names.realm, dnsdomain=names.dnsdomain,
1807 dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1808 private_dir=paths.private_dir,
1809 keytab_name=paths.dns_keytab)
1810 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1811 logger.info("and %s for further documentation required for secure DNS "
1812 "updates", paths.namedtxt)
1814 lastProvisionUSNs = get_last_provision_usn(samdb)
1815 maxUSN = get_max_usn(samdb, str(names.rootdn))
1816 if lastProvisionUSNs is not None:
1817 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1819 set_provision_usn(samdb, 0, maxUSN, invocationid)
1821 create_krb5_conf(paths.krb5conf,
1822 dnsdomain=names.dnsdomain, hostname=names.hostname,
1824 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1825 "generated at %s", paths.krb5conf)
1827 if serverrole == "domain controller":
1828 create_dns_update_list(lp, logger, paths)
1830 provision_backend.post_setup()
1831 provision_backend.shutdown()
1833 create_phpldapadmin_config(paths.phpldapadminconfig,
1836 secrets_ldb.transaction_cancel()
1839 # Now commit the secrets.ldb to disk
1840 secrets_ldb.transaction_commit()
1842 # the commit creates the dns.keytab, now chown it
1843 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1844 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1846 os.chmod(dns_keytab_path, 0640)
1847 os.chown(dns_keytab_path, -1, paths.bind_gid)
1849 if not os.environ.has_key('SAMBA_SELFTEST'):
1850 logger.info("Failed to chown %s to bind gid %u",
1851 dns_keytab_path, paths.bind_gid)
1853 if samdb_fill != FILL_DRS:
1854 # fix any dangling GUIDs from the provision
1855 logger.info("Fixing provision GUIDs")
1856 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, quiet=True)
1857 samdb.transaction_start()
1858 # a small number of GUIDs are missing because of ordering issues in the
1860 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1861 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1862 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1863 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1864 scope=ldb.SCOPE_ONELEVEL,
1865 attrs=['ipsecOwnersReference',
1866 'ipsecFilterReference',
1867 'ipsecISAKMPReference',
1868 'ipsecNegotiationPolicyReference',
1869 'ipsecNFAReference'])
1870 samdb.transaction_commit()
1873 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1874 paths.phpldapadminconfig)
1876 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1877 logger.info("Server Role: %s" % serverrole)
1878 logger.info("Hostname: %s" % names.hostname)
1879 logger.info("NetBIOS Domain: %s" % names.domain)
1880 logger.info("DNS Domain: %s" % names.dnsdomain)
1881 logger.info("DOMAIN SID: %s" % str(domainsid))
1882 if samdb_fill == FILL_FULL:
1883 logger.info("Admin password: %s" % adminpass)
1884 if provision_backend.type is not "ldb":
1885 if provision_backend.credentials.get_bind_dn() is not None:
1886 logger.info("LDAP Backend Admin DN: %s" %
1887 provision_backend.credentials.get_bind_dn())
1889 logger.info("LDAP Admin User: %s" %
1890 provision_backend.credentials.get_username())
1892 logger.info("LDAP Admin Password: %s" %
1893 provision_backend.credentials.get_password())
1895 if provision_backend.slapd_command_escaped is not None:
1896 # now display slapd_command_file.txt to show how slapd must be
1898 logger.info("Use later the following commandline to start slapd, then Samba:")
1899 logger.info(provision_backend.slapd_command_escaped)
1900 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1901 provision_backend.ldapdir)
1903 result = ProvisionResult()
1904 result.domaindn = domaindn
1905 result.paths = paths
1907 result.samdb = samdb
1908 result.idmap = idmap
1912 def provision_become_dc(smbconf=None, targetdir=None,
1913 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1914 serverdn=None, domain=None, hostname=None, domainsid=None,
1915 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1916 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1917 root=None, nobody=None, users=None, wheel=None, backup=None,
1918 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1919 sitename=None, debuglevel=1):
1921 logger = logging.getLogger("provision")
1922 samba.set_debug_level(debuglevel)
1924 res = provision(logger, system_session(), None,
1925 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1926 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1927 configdn=configdn, serverdn=serverdn, domain=domain,
1928 hostname=hostname, hostip=None, domainsid=domainsid,
1929 machinepass=machinepass, serverrole="domain controller",
1931 res.lp.set("debuglevel", str(debuglevel))
1935 def create_phpldapadmin_config(path, ldapi_uri):
1936 """Create a PHP LDAP admin configuration file.
1938 :param path: Path to write the configuration to.
1940 setup_file(setup_path("phpldapadmin-config.php"), path,
1941 {"S4_LDAPI_URI": ldapi_uri})
1944 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1945 hostip, hostip6, hostname, realm, domainguid,
1947 """Write out a DNS zone file, from the info in the current database.
1949 :param paths: paths object
1950 :param dnsdomain: DNS Domain name
1951 :param domaindn: DN of the Domain
1952 :param hostip: Local IPv4 IP
1953 :param hostip6: Local IPv6 IP
1954 :param hostname: Local hostname
1955 :param realm: Realm name
1956 :param domainguid: GUID of the domain.
1957 :param ntdsguid: GUID of the hosts nTDSDSA record.
1959 assert isinstance(domainguid, str)
1961 if hostip6 is not None:
1962 hostip6_base_line = " IN AAAA " + hostip6
1963 hostip6_host_line = hostname + " IN AAAA " + hostip6
1964 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1966 hostip6_base_line = ""
1967 hostip6_host_line = ""
1968 gc_msdcs_ip6_line = ""
1970 if hostip is not None:
1971 hostip_base_line = " IN A " + hostip
1972 hostip_host_line = hostname + " IN A " + hostip
1973 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1975 hostip_base_line = ""
1976 hostip_host_line = ""
1977 gc_msdcs_ip_line = ""
1979 dns_dir = os.path.dirname(paths.dns)
1982 shutil.rmtree(dns_dir, True)
1986 os.mkdir(dns_dir, 0775)
1988 # we need to freeze the zone while we update the contents
1989 if targetdir is None:
1990 rndc = ' '.join(lp.get("rndc command"))
1991 os.system(rndc + " freeze " + lp.get("realm"))
1993 setup_file(setup_path("provision.zone"), paths.dns, {
1994 "HOSTNAME": hostname,
1995 "DNSDOMAIN": dnsdomain,
1997 "HOSTIP_BASE_LINE": hostip_base_line,
1998 "HOSTIP_HOST_LINE": hostip_host_line,
1999 "DOMAINGUID": domainguid,
2000 "DATESTRING": time.strftime("%Y%m%d%H"),
2001 "DEFAULTSITE": DEFAULTSITE,
2002 "NTDSGUID": ntdsguid,
2003 "HOSTIP6_BASE_LINE": hostip6_base_line,
2004 "HOSTIP6_HOST_LINE": hostip6_host_line,
2005 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
2006 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
2009 # note that we use no variable substitution on this file
2010 # the substitution is done at runtime by samba_dnsupdate
2011 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2013 # and the SPN update list
2014 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2016 if paths.bind_gid is not None:
2018 os.chown(dns_dir, -1, paths.bind_gid)
2019 os.chown(paths.dns, -1, paths.bind_gid)
2020 # chmod needed to cope with umask
2021 os.chmod(dns_dir, 0775)
2022 os.chmod(paths.dns, 0664)
2024 if not os.environ.has_key('SAMBA_SELFTEST'):
2025 logger.error("Failed to chown %s to bind gid %u" % (
2026 dns_dir, paths.bind_gid))
2028 if targetdir is None:
2029 os.system(rndc + " unfreeze " + lp.get("realm"))
2032 def create_dns_update_list(lp, logger, paths):
2033 """Write out a dns_update_list file"""
2034 # note that we use no variable substitution on this file
2035 # the substitution is done at runtime by samba_dnsupdate
2036 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2037 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2040 def create_named_conf(paths, realm, dnsdomain,
2042 """Write out a file containing zone statements suitable for inclusion in a
2043 named.conf file (including GSS-TSIG configuration).
2045 :param paths: all paths
2046 :param realm: Realm name
2047 :param dnsdomain: DNS Domain name
2048 :param private_dir: Path to private directory
2049 :param keytab_name: File name of DNS keytab file
2052 setup_file(setup_path("named.conf"), paths.namedconf, {
2053 "DNSDOMAIN": dnsdomain,
2055 "ZONE_FILE": paths.dns,
2056 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2057 "NAMED_CONF": paths.namedconf,
2058 "NAMED_CONF_UPDATE": paths.namedconf_update
2061 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2064 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2066 """Write out a file containing zone statements suitable for inclusion in a
2067 named.conf file (including GSS-TSIG configuration).
2069 :param path: Path of the new named.conf file.
2070 :param realm: Realm name
2071 :param dnsdomain: DNS Domain name
2072 :param private_dir: Path to private directory
2073 :param keytab_name: File name of DNS keytab file
2075 setup_file(setup_path("named.txt"), path, {
2076 "DNSDOMAIN": dnsdomain,
2077 "DNSNAME" : dnsname,
2079 "DNS_KEYTAB": keytab_name,
2080 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2081 "PRIVATE_DIR": private_dir
2085 def create_krb5_conf(path, dnsdomain, hostname, realm):
2086 """Write out a file containing zone statements suitable for inclusion in a
2087 named.conf file (including GSS-TSIG configuration).
2089 :param path: Path of the new named.conf file.
2090 :param dnsdomain: DNS Domain name
2091 :param hostname: Local hostname
2092 :param realm: Realm name
2094 setup_file(setup_path("krb5.conf"), path, {
2095 "DNSDOMAIN": dnsdomain,
2096 "HOSTNAME": hostname,
2101 class ProvisioningError(Exception):
2102 """A generic provision error."""
2104 def __init__(self, value):
2108 return "ProvisioningError: " + self.value
2111 class InvalidNetbiosName(Exception):
2112 """A specified name was not a valid NetBIOS name."""
2113 def __init__(self, name):
2114 super(InvalidNetbiosName, self).__init__(
2115 "The name '%r' is not a valid NetBIOS name" % name)