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, create_dns_update_list
81 from samba.schema import Schema
82 from samba.samdb import SamDB
83 from samba.dbchecker import dbcheck
86 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
87 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
88 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
89 DEFAULTSITE = "Default-First-Site-Name"
90 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
94 """Return an absolute path to the provision tempate file specified by file"""
95 return os.path.join(samba.param.setup_dir(), file)
97 # Descriptors of naming contexts and other important objects
99 # "get_schema_descriptor" is located in "schema.py"
101 def get_config_descriptor(domain_sid):
102 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
103 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
104 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
105 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
106 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
107 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
108 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
109 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
110 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
111 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
112 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
113 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
114 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
115 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
116 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
117 sec = security.descriptor.from_sddl(sddl, domain_sid)
121 def get_domain_descriptor(domain_sid):
122 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
129 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
130 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
131 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
132 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
133 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
134 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
135 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
136 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
137 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
138 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
140 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
141 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
142 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
143 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
144 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
145 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
146 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
147 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
148 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
149 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
150 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
151 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
152 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
153 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
154 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
155 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
156 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
157 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
158 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
159 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
160 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
163 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
165 "(A;;RPLCLORC;;;ED)" \
166 "(A;;RPLCLORC;;;AU)" \
167 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
168 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
169 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
170 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
171 sec = security.descriptor.from_sddl(sddl, domain_sid)
175 class ProvisionPaths(object):
178 self.shareconf = None
189 self.dns_keytab = None
192 self.private_dir = None
195 class ProvisionNames(object):
202 self.ldapmanagerdn = None
203 self.dnsdomain = None
205 self.netbiosname = None
211 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
212 """Get key provision parameters (realm, domain, ...) from a given provision
214 :param samdb: An LDB object connected to the sam.ldb file
215 :param secretsdb: An LDB object connected to the secrets.ldb file
216 :param idmapdb: An LDB object connected to the idmap.ldb file
217 :param paths: A list of path to provision object
218 :param smbconf: Path to the smb.conf file
219 :param lp: A LoadParm object
220 :return: A list of key provision parameters
222 names = ProvisionNames()
223 names.adminpass = None
225 # NT domain, kerberos realm, root dn, domain dn, domain dns name
226 names.domain = string.upper(lp.get("workgroup"))
227 names.realm = lp.get("realm")
228 names.dnsdomain = names.realm.lower()
229 basedn = samba.dn_from_dns_name(names.dnsdomain)
230 names.realm = string.upper(names.realm)
232 # Get the netbiosname first (could be obtained from smb.conf in theory)
233 res = secretsdb.search(expression="(flatname=%s)" %
234 names.domain,base="CN=Primary Domains",
235 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
236 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
238 names.smbconf = smbconf
240 # That's a bit simplistic but it's ok as long as we have only 3
242 current = samdb.search(expression="(objectClass=*)",
243 base="", scope=ldb.SCOPE_BASE,
244 attrs=["defaultNamingContext", "schemaNamingContext",
245 "configurationNamingContext","rootDomainNamingContext"])
247 names.configdn = current[0]["configurationNamingContext"]
248 configdn = str(names.configdn)
249 names.schemadn = current[0]["schemaNamingContext"]
250 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
251 current[0]["defaultNamingContext"][0]))):
252 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
253 "is not the same ..." % (paths.samdb,
254 str(current[0]["defaultNamingContext"][0]),
255 paths.smbconf, basedn)))
257 names.domaindn=current[0]["defaultNamingContext"]
258 names.rootdn=current[0]["rootDomainNamingContext"]
260 res3 = samdb.search(expression="(objectClass=site)",
261 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
262 names.sitename = str(res3[0]["cn"])
264 # dns hostname and server dn
265 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
266 base="OU=Domain Controllers,%s" % basedn,
267 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
268 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
270 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
271 attrs=[], base=configdn)
272 names.serverdn = server_res[0].dn
274 # invocation id/objectguid
275 res5 = samdb.search(expression="(objectClass=*)",
276 base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
277 attrs=["invocationID", "objectGUID"])
278 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
279 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
282 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
283 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
284 "objectSid","msDS-Behavior-Version" ])
285 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
286 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
287 if res6[0].get("msDS-Behavior-Version") is None or \
288 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
289 names.domainlevel = DS_DOMAIN_FUNCTION_2000
291 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
294 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
295 base="CN=Policies,CN=System," + basedn,
296 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
297 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
299 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
301 base="CN=Policies,CN=System," + basedn,
302 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
304 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
306 names.policyid_dc = None
307 res9 = idmapdb.search(expression="(cn=%s)" %
308 (security.SID_BUILTIN_ADMINISTRATORS),
311 names.wheel_gid = res9[0]["xidNumber"]
313 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
316 def update_provision_usn(samdb, low, high, id, replace=False):
317 """Update the field provisionUSN in sam.ldb
319 This field is used to track range of USN modified by provision and
321 This value is used afterward by next provision to figure out if
322 the field have been modified since last provision.
324 :param samdb: An LDB object connect to sam.ldb
325 :param low: The lowest USN modified by this upgrade
326 :param high: The highest USN modified by this upgrade
327 :param id: The invocation id of the samba's dc
328 :param replace: A boolean indicating if the range should replace any
329 existing one or appended (default)
334 entry = samdb.search(base="@PROVISION",
335 scope=ldb.SCOPE_BASE,
336 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
337 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
338 if not re.search(';', e):
339 e = "%s;%s" % (e, id)
342 tab.append("%s-%s;%s" % (low, high, id))
343 delta = ldb.Message()
344 delta.dn = ldb.Dn(samdb, "@PROVISION")
345 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
346 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
347 entry = samdb.search(expression='provisionnerID=*',
348 base="@PROVISION", scope=ldb.SCOPE_BASE,
349 attrs=["provisionnerID"])
350 if len(entry) == 0 or len(entry[0]) == 0:
351 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
355 def set_provision_usn(samdb, low, high, id):
356 """Set the field provisionUSN in sam.ldb
357 This field is used to track range of USN modified by provision and
359 This value is used afterward by next provision to figure out if
360 the field have been modified since last provision.
362 :param samdb: An LDB object connect to sam.ldb
363 :param low: The lowest USN modified by this upgrade
364 :param high: The highest USN modified by this upgrade
365 :param id: The invocationId of the provision"""
368 tab.append("%s-%s;%s" % (low, high, id))
370 delta = ldb.Message()
371 delta.dn = ldb.Dn(samdb, "@PROVISION")
372 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
373 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
377 def get_max_usn(samdb,basedn):
378 """ This function return the biggest USN present in the provision
380 :param samdb: A LDB object pointing to the sam.ldb
381 :param basedn: A string containing the base DN of the provision
383 :return: The biggest USN in the provision"""
385 res = samdb.search(expression="objectClass=*",base=basedn,
386 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
387 controls=["search_options:1:2",
388 "server_sort:1:1:uSNChanged",
389 "paged_results:1:1"])
390 return res[0]["uSNChanged"]
393 def get_last_provision_usn(sam):
394 """Get USNs ranges modified by a provision or an upgradeprovision
396 :param sam: An LDB object pointing to the sam.ldb
397 :return: a dictionnary which keys are invocation id and values are an array
398 of integer representing the different ranges
401 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
402 base="@PROVISION", scope=ldb.SCOPE_BASE,
403 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
404 except ldb.LdbError, (ecode, emsg):
405 if ecode == ldb.ERR_NO_SUCH_OBJECT:
412 if entry[0].get("provisionnerID"):
413 for e in entry[0]["provisionnerID"]:
415 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
416 tab1 = str(r).split(';')
421 if (len(myids) > 0 and id not in myids):
423 tab2 = p.split(tab1[0])
424 if range.get(id) == None:
426 range[id].append(tab2[0])
427 range[id].append(tab2[1])
433 class ProvisionResult(object):
444 def check_install(lp, session_info, credentials):
445 """Check whether the current install seems ok.
447 :param lp: Loadparm context
448 :param session_info: Session information
449 :param credentials: Credentials
451 if lp.get("realm") == "":
452 raise Exception("Realm empty")
453 samdb = Ldb(lp.samdb_url(), session_info=session_info,
454 credentials=credentials, lp=lp)
455 if len(samdb.search("(cn=Administrator)")) != 1:
456 raise ProvisioningError("No administrator account found")
459 def findnss(nssfn, names):
460 """Find a user or group from a list of possibilities.
462 :param nssfn: NSS Function to try (should raise KeyError if not found)
463 :param names: Names to check.
464 :return: Value return by first names list.
471 raise KeyError("Unable to find user/group in %r" % names)
474 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
475 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
478 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
479 """Setup a ldb in the private dir.
481 :param ldb: LDB file to import data into
482 :param ldif_path: Path of the LDIF file to load
483 :param subst_vars: Optional variables to subsitute in LDIF.
484 :param nocontrols: Optional list of controls, can be None for no controls
486 assert isinstance(ldif_path, str)
487 data = read_and_sub_file(ldif_path, subst_vars)
488 ldb.add_ldif(data, controls)
491 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
492 """Modify a ldb in the private dir.
494 :param ldb: LDB object.
495 :param ldif_path: LDIF file path.
496 :param subst_vars: Optional dictionary with substitution variables.
498 data = read_and_sub_file(ldif_path, subst_vars)
499 ldb.modify_ldif(data, controls)
502 def setup_ldb(ldb, ldif_path, subst_vars):
503 """Import a LDIF a file into a LDB handle, optionally substituting
506 :note: Either all LDIF data will be added or none (using transactions).
508 :param ldb: LDB file to import into.
509 :param ldif_path: Path to the LDIF file.
510 :param subst_vars: Dictionary with substitution variables.
512 assert ldb is not None
513 ldb.transaction_start()
515 setup_add_ldif(ldb, ldif_path, subst_vars)
517 ldb.transaction_cancel()
520 ldb.transaction_commit()
523 def provision_paths_from_lp(lp, dnsdomain):
524 """Set the default paths for provisioning.
526 :param lp: Loadparm context.
527 :param dnsdomain: DNS Domain name
529 paths = ProvisionPaths()
530 paths.private_dir = lp.get("private dir")
532 # This is stored without path prefix for the "privateKeytab" attribute in
533 # "secrets_dns.ldif".
534 paths.dns_keytab = "dns.keytab"
535 paths.keytab = "secrets.keytab"
537 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
538 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
539 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
540 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
541 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
542 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
543 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
544 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
545 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
546 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
547 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
548 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
549 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
550 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
551 paths.phpldapadminconfig = os.path.join(paths.private_dir,
552 "phpldapadmin-config.php")
553 paths.hklm = "hklm.ldb"
554 paths.hkcr = "hkcr.ldb"
555 paths.hkcu = "hkcu.ldb"
556 paths.hku = "hku.ldb"
557 paths.hkpd = "hkpd.ldb"
558 paths.hkpt = "hkpt.ldb"
559 paths.sysvol = lp.get("path", "sysvol")
560 paths.netlogon = lp.get("path", "netlogon")
561 paths.smbconf = lp.configfile
565 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
566 serverrole=None, rootdn=None, domaindn=None, configdn=None,
567 schemadn=None, serverdn=None, sitename=None):
568 """Guess configuration settings to use."""
571 hostname = socket.gethostname().split(".")[0]
573 netbiosname = lp.get("netbios name")
574 if netbiosname is None:
575 netbiosname = hostname
576 # remove forbidden chars
578 for x in netbiosname:
579 if x.isalnum() or x in VALID_NETBIOS_CHARS:
580 newnbname = "%s%c" % (newnbname, x)
581 # force the length to be <16
582 netbiosname = newnbname[0:15]
583 assert netbiosname is not None
584 netbiosname = netbiosname.upper()
585 if not valid_netbios_name(netbiosname):
586 raise InvalidNetbiosName(netbiosname)
588 if dnsdomain is None:
589 dnsdomain = lp.get("realm")
590 if dnsdomain is None or dnsdomain == "":
591 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
593 dnsdomain = dnsdomain.lower()
595 if serverrole is None:
596 serverrole = lp.get("server role")
597 if serverrole is None:
598 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
600 serverrole = serverrole.lower()
602 realm = dnsdomain.upper()
604 if lp.get("realm") == "":
605 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
607 if lp.get("realm").upper() != realm:
608 raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
610 if lp.get("server role").lower() != serverrole:
611 raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), serverrole, lp.configfile))
613 if serverrole == "domain controller":
615 # This will, for better or worse, default to 'WORKGROUP'
616 domain = lp.get("workgroup")
617 domain = domain.upper()
619 if lp.get("workgroup").upper() != domain:
620 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
623 domaindn = samba.dn_from_dns_name(dnsdomain)
625 if domain == netbiosname:
626 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
630 domaindn = "DC=" + netbiosname
632 if not valid_netbios_name(domain):
633 raise InvalidNetbiosName(domain)
635 if hostname.upper() == realm:
636 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
637 if netbiosname.upper() == realm:
638 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
640 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
646 configdn = "CN=Configuration," + rootdn
648 schemadn = "CN=Schema," + configdn
653 names = ProvisionNames()
654 names.rootdn = rootdn
655 names.domaindn = domaindn
656 names.configdn = configdn
657 names.schemadn = schemadn
658 names.ldapmanagerdn = "CN=Manager," + rootdn
659 names.dnsdomain = dnsdomain
660 names.domain = domain
662 names.netbiosname = netbiosname
663 names.hostname = hostname
664 names.sitename = sitename
665 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
666 netbiosname, sitename, configdn)
671 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
672 targetdir, sid_generator="internal", eadb=False, lp=None):
673 """Create a new smb.conf file based on a couple of basic settings.
675 assert smbconf is not None
677 hostname = socket.gethostname().split(".")[0]
678 netbiosname = hostname.upper()
679 # remove forbidden chars
681 for x in netbiosname:
682 if x.isalnum() or x in VALID_NETBIOS_CHARS:
683 newnbname = "%s%c" % (newnbname, x)
684 #force the length to be <16
685 netbiosname = newnbname[0:15]
687 netbiosname = hostname.upper()
689 if serverrole is None:
690 serverrole = "standalone"
692 assert serverrole in ("domain controller", "member server", "standalone")
693 if serverrole == "domain controller":
695 elif serverrole == "member server":
696 smbconfsuffix = "member"
697 elif serverrole == "standalone":
698 smbconfsuffix = "standalone"
700 if sid_generator is None:
701 sid_generator = "internal"
703 assert domain is not None
704 domain = domain.upper()
706 assert realm is not None
707 realm = realm.upper()
710 lp = samba.param.LoadParm()
711 #Load non-existant file
712 if os.path.exists(smbconf):
714 if eadb and not lp.get("posix:eadb"):
715 if targetdir is not None:
716 privdir = os.path.join(targetdir, "private")
718 privdir = lp.get("private dir")
719 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
721 if targetdir is not None:
722 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
723 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
724 statedir_line = "state directory = " + os.path.abspath(targetdir)
725 cachedir_line = "cache directory = " + os.path.abspath(targetdir)
727 lp.set("lock dir", os.path.abspath(targetdir))
728 lp.set("state directory", os.path.abspath(targetdir))
729 lp.set("cache directory", os.path.abspath(targetdir))
736 sysvol = os.path.join(lp.get("state directory"), "sysvol")
737 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
739 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
741 "NETBIOS_NAME": netbiosname,
744 "SERVERROLE": serverrole,
745 "NETLOGONPATH": netlogon,
746 "SYSVOLPATH": sysvol,
747 "PRIVATEDIR_LINE": privatedir_line,
748 "LOCKDIR_LINE": lockdir_line,
749 "STATEDIR_LINE": statedir_line,
750 "CACHEDIR_LINE": cachedir_line
753 # reload the smb.conf
756 # and dump it without any values that are the default
757 # this ensures that any smb.conf parameters that were set
758 # on the provision/join command line are set in the resulting smb.conf
759 f = open(smbconf, mode='w')
765 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
766 users_gid, wheel_gid):
767 """setup reasonable name mappings for sam names to unix names.
769 :param samdb: SamDB object.
770 :param idmap: IDmap db object.
771 :param sid: The domain sid.
772 :param domaindn: The domain DN.
773 :param root_uid: uid of the UNIX root user.
774 :param nobody_uid: uid of the UNIX nobody user.
775 :param users_gid: gid of the UNIX users group.
776 :param wheel_gid: gid of the UNIX wheel group.
778 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
779 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
781 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
782 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
785 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
786 provision_backend, names, schema, serverrole,
788 """Setup the partitions for the SAM database.
790 Alternatively, provision() may call this, and then populate the database.
792 :note: This will wipe the Sam Database!
794 :note: This function always removes the local SAM LDB file. The erase
795 parameter controls whether to erase the existing data, which
796 may not be stored locally but in LDAP.
799 assert session_info is not None
801 # We use options=["modules:"] to stop the modules loading - we
802 # just want to wipe and re-initialise the database, not start it up
805 os.unlink(samdb_path)
809 samdb = Ldb(url=samdb_path, session_info=session_info,
810 lp=lp, options=["modules:"])
812 ldap_backend_line = "# No LDAP backend"
813 if provision_backend.type is not "ldb":
814 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
816 samdb.transaction_start()
818 logger.info("Setting up sam.ldb partitions and settings")
819 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
820 "LDAP_BACKEND_LINE": ldap_backend_line
824 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
825 "BACKEND_TYPE": provision_backend.type,
826 "SERVER_ROLE": serverrole
829 logger.info("Setting up sam.ldb rootDSE")
830 setup_samdb_rootdse(samdb, names)
832 samdb.transaction_cancel()
835 samdb.transaction_commit()
838 def secretsdb_self_join(secretsdb, domain,
839 netbiosname, machinepass, domainsid=None,
840 realm=None, dnsdomain=None,
842 key_version_number=1,
843 secure_channel_type=SEC_CHAN_WKSTA):
844 """Add domain join-specific bits to a secrets database.
846 :param secretsdb: Ldb Handle to the secrets database
847 :param machinepass: Machine password
849 attrs = ["whenChanged",
856 if realm is not None:
857 if dnsdomain is None:
858 dnsdomain = realm.lower()
859 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
862 shortname = netbiosname.lower()
864 # We don't need to set msg["flatname"] here, because rdn_name will handle
865 # it, and it causes problems for modifies anyway
866 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
867 msg["secureChannelType"] = [str(secure_channel_type)]
868 msg["objectClass"] = ["top", "primaryDomain"]
869 if dnsname is not None:
870 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
871 msg["realm"] = [realm]
872 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
873 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
874 msg["privateKeytab"] = ["secrets.keytab"]
876 msg["secret"] = [machinepass]
877 msg["samAccountName"] = ["%s$" % netbiosname]
878 msg["secureChannelType"] = [str(secure_channel_type)]
879 if domainsid is not None:
880 msg["objectSid"] = [ndr_pack(domainsid)]
882 # This complex expression tries to ensure that we don't have more
883 # than one record for this SID, realm or netbios domain at a time,
884 # but we don't delete the old record that we are about to modify,
885 # because that would delete the keytab and previous password.
886 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
887 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
888 scope=ldb.SCOPE_ONELEVEL)
891 secretsdb.delete(del_msg.dn)
893 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
896 msg["priorSecret"] = [res[0]["secret"][0]]
897 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
900 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
905 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
911 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
912 secretsdb.modify(msg)
913 secretsdb.rename(res[0].dn, msg.dn)
915 spn = [ 'HOST/%s' % shortname ]
916 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
917 # we are a domain controller then we add servicePrincipalName
918 # entries for the keytab code to update.
919 spn.extend([ 'HOST/%s' % dnsname ])
920 msg["servicePrincipalName"] = spn
925 def setup_secretsdb(paths, session_info, backend_credentials, lp):
926 """Setup the secrets database.
928 :note: This function does not handle exceptions and transaction on purpose,
929 it's up to the caller to do this job.
931 :param path: Path to the secrets database.
932 :param session_info: Session info.
933 :param credentials: Credentials
934 :param lp: Loadparm context
935 :return: LDB handle for the created secrets database
937 if os.path.exists(paths.secrets):
938 os.unlink(paths.secrets)
940 keytab_path = os.path.join(paths.private_dir, paths.keytab)
941 if os.path.exists(keytab_path):
942 os.unlink(keytab_path)
944 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
945 if os.path.exists(dns_keytab_path):
946 os.unlink(dns_keytab_path)
950 secrets_ldb = Ldb(path, session_info=session_info,
953 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
954 secrets_ldb = Ldb(path, session_info=session_info,
956 secrets_ldb.transaction_start()
958 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
960 if (backend_credentials is not None and
961 backend_credentials.authentication_requested()):
962 if backend_credentials.get_bind_dn() is not None:
963 setup_add_ldif(secrets_ldb,
964 setup_path("secrets_simple_ldap.ldif"), {
965 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
966 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
969 setup_add_ldif(secrets_ldb,
970 setup_path("secrets_sasl_ldap.ldif"), {
971 "LDAPADMINUSER": backend_credentials.get_username(),
972 "LDAPADMINREALM": backend_credentials.get_realm(),
973 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
976 secrets_ldb.transaction_cancel()
982 def setup_privileges(path, session_info, lp):
983 """Setup the privileges database.
985 :param path: Path to the privileges database.
986 :param session_info: Session info.
987 :param credentials: Credentials
988 :param lp: Loadparm context
989 :return: LDB handle for the created secrets database
991 if os.path.exists(path):
993 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
994 privilege_ldb.erase()
995 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
998 def setup_registry(path, session_info, lp):
999 """Setup the registry.
1001 :param path: Path to the registry database
1002 :param session_info: Session information
1003 :param credentials: Credentials
1004 :param lp: Loadparm context
1006 reg = samba.registry.Registry()
1007 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1008 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1009 provision_reg = setup_path("provision.reg")
1010 assert os.path.exists(provision_reg)
1011 reg.diff_apply(provision_reg)
1014 def setup_idmapdb(path, session_info, lp):
1015 """Setup the idmap database.
1017 :param path: path to the idmap database
1018 :param session_info: Session information
1019 :param credentials: Credentials
1020 :param lp: Loadparm context
1022 if os.path.exists(path):
1025 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1027 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1031 def setup_samdb_rootdse(samdb, names):
1032 """Setup the SamDB rootdse.
1034 :param samdb: Sam Database handle
1036 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1037 "SCHEMADN": names.schemadn,
1038 "DOMAINDN": names.domaindn,
1039 "ROOTDN" : names.rootdn,
1040 "CONFIGDN": names.configdn,
1041 "SERVERDN": names.serverdn,
1045 def setup_self_join(samdb, admin_session_info, names, fill, machinepass, dnspass,
1046 domainsid, next_rid, invocationid,
1047 policyguid, policyguid_dc, domainControllerFunctionality,
1048 ntdsguid, dc_rid=None):
1049 """Join a host to its own domain."""
1050 assert isinstance(invocationid, str)
1051 if ntdsguid is not None:
1052 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1059 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1060 "CONFIGDN": names.configdn,
1061 "SCHEMADN": names.schemadn,
1062 "DOMAINDN": names.domaindn,
1063 "SERVERDN": names.serverdn,
1064 "INVOCATIONID": invocationid,
1065 "NETBIOSNAME": names.netbiosname,
1066 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1067 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1068 "DOMAINSID": str(domainsid),
1069 "DCRID": str(dc_rid),
1070 "SAMBA_VERSION_STRING": version,
1071 "NTDSGUID": ntdsguid_line,
1072 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1073 domainControllerFunctionality),
1074 "RIDALLOCATIONSTART": str(next_rid + 100),
1075 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
1077 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1078 "POLICYGUID": policyguid,
1079 "POLICYGUID_DC": policyguid_dc,
1080 "DNSDOMAIN": names.dnsdomain,
1081 "DOMAINDN": names.domaindn})
1083 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1084 if fill == FILL_FULL:
1085 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1086 "CONFIGDN": names.configdn,
1087 "SCHEMADN": names.schemadn,
1088 "DOMAINDN": names.domaindn,
1089 "SERVERDN": names.serverdn,
1090 "INVOCATIONID": invocationid,
1091 "NETBIOSNAME": names.netbiosname,
1092 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1093 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1094 "DOMAINSID": str(domainsid),
1095 "DCRID": str(dc_rid),
1096 "SAMBA_VERSION_STRING": version,
1097 "NTDSGUID": ntdsguid_line,
1098 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1099 domainControllerFunctionality)})
1101 # Setup fSMORoleOwner entries to point at the newly created DC entry
1102 setup_modify_ldif(samdb, setup_path("provision_self_join_modify_config.ldif"), {
1103 "CONFIGDN": names.configdn,
1104 "SCHEMADN": names.schemadn,
1105 "DEFAULTSITE": names.sitename,
1106 "NETBIOSNAME": names.netbiosname,
1107 "SERVERDN": names.serverdn,
1110 system_session_info = system_session()
1111 samdb.set_session_info(system_session_info)
1112 # Setup fSMORoleOwner entries to point at the newly created DC entry
1114 # to modify a serverReference under cn=config when we are a subdomain, we must
1115 # be system due to ACLs
1116 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1117 "DOMAINDN": names.domaindn,
1118 "SERVERDN": names.serverdn,
1119 "NETBIOSNAME": names.netbiosname,
1122 samdb.set_session_info(admin_session_info)
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, fill, serverrole, schema, am_rodc=False):
1181 """Setup a complete SAM Database.
1183 :note: This will wipe the main SAM database file!
1186 # Also wipes the database
1187 setup_samdb_partitions(path, logger=logger, lp=lp,
1188 provision_backend=provision_backend, session_info=session_info,
1189 names=names, serverrole=serverrole, schema=schema)
1191 # Load the database, but don's load the global schema and don't connect
1193 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1194 credentials=provision_backend.credentials, lp=lp,
1195 global_schema=False, am_rodc=am_rodc)
1197 logger.info("Pre-loading the Samba 4 and AD schema")
1199 # Load the schema from the one we computed earlier
1200 samdb.set_schema(schema)
1202 # Set the NTDS settings DN manually - in order to have it already around
1203 # before the provisioned tree exists and we connect
1204 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1206 # And now we can connect to the DB - the schema won't be loaded from the
1212 def fill_samdb(samdb, lp, names,
1213 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1214 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1215 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1216 next_rid=None, dc_rid=None):
1218 if next_rid is None:
1221 # Provision does not make much sense values larger than 1000000000
1222 # as the upper range of the rIDAvailablePool is 1073741823 and
1223 # we don't want to create a domain that cannot allocate rids.
1224 if next_rid < 1000 or next_rid > 1000000000:
1225 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1226 error += "the valid range is %u-%u. The default is %u." % (
1227 1000, 1000000000, 1000)
1228 raise ProvisioningError(error)
1230 # ATTENTION: Do NOT change these default values without discussion with the
1231 # team and/or release manager. They have a big impact on the whole program!
1232 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1234 if dom_for_fun_level is None:
1235 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1237 if dom_for_fun_level > domainControllerFunctionality:
1238 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!")
1240 domainFunctionality = dom_for_fun_level
1241 forestFunctionality = dom_for_fun_level
1243 # Set the NTDS settings DN manually - in order to have it already around
1244 # before the provisioned tree exists and we connect
1245 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1247 samdb.transaction_start()
1249 # Set the domain functionality levels onto the database.
1250 # Various module (the password_hash module in particular) need
1251 # to know what level of AD we are emulating.
1253 # These will be fixed into the database via the database
1254 # modifictions below, but we need them set from the start.
1255 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1256 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1257 samdb.set_opaque_integer("domainControllerFunctionality",
1258 domainControllerFunctionality)
1260 samdb.set_domain_sid(str(domainsid))
1261 samdb.set_invocation_id(invocationid)
1263 logger.info("Adding DomainDN: %s" % names.domaindn)
1265 # impersonate domain admin
1266 admin_session_info = admin_session(lp, str(domainsid))
1267 samdb.set_session_info(admin_session_info)
1268 if domainguid is not None:
1269 domainguid_line = "objectGUID: %s\n-" % domainguid
1271 domainguid_line = ""
1273 descr = b64encode(get_domain_descriptor(domainsid))
1274 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1275 "DOMAINDN": names.domaindn,
1276 "DOMAINSID": str(domainsid),
1277 "DESCRIPTOR": descr,
1278 "DOMAINGUID": domainguid_line
1281 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1282 "DOMAINDN": names.domaindn,
1283 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1284 "NEXTRID": str(next_rid),
1285 "DEFAULTSITE": names.sitename,
1286 "CONFIGDN": names.configdn,
1287 "POLICYGUID": policyguid,
1288 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1289 "SAMBA_VERSION_STRING": version
1292 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1293 if fill == FILL_FULL:
1294 logger.info("Adding configuration container")
1295 descr = b64encode(get_config_descriptor(domainsid))
1296 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1297 "CONFIGDN": names.configdn,
1298 "DESCRIPTOR": descr,
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 # Now register this container in the root of the forest
1311 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1312 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1316 samdb.transaction_cancel()
1319 samdb.transaction_commit()
1321 samdb.transaction_start()
1323 samdb.invocation_id = invocationid
1325 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1326 if fill == FILL_FULL:
1327 logger.info("Setting up sam.ldb configuration data")
1328 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1329 "CONFIGDN": names.configdn,
1330 "NETBIOSNAME": names.netbiosname,
1331 "DEFAULTSITE": names.sitename,
1332 "DNSDOMAIN": names.dnsdomain,
1333 "DOMAIN": names.domain,
1334 "SCHEMADN": names.schemadn,
1335 "DOMAINDN": names.domaindn,
1336 "SERVERDN": names.serverdn,
1337 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1338 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1341 logger.info("Setting up display specifiers")
1342 display_specifiers_ldif = read_ms_ldif(
1343 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1344 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1345 {"CONFIGDN": names.configdn})
1346 check_all_substituted(display_specifiers_ldif)
1347 samdb.add_ldif(display_specifiers_ldif)
1349 logger.info("Adding users container")
1350 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1351 "DOMAINDN": names.domaindn})
1352 logger.info("Modifying users container")
1353 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1354 "DOMAINDN": names.domaindn})
1355 logger.info("Adding computers container")
1356 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1357 "DOMAINDN": names.domaindn})
1358 logger.info("Modifying computers container")
1359 setup_modify_ldif(samdb,
1360 setup_path("provision_computers_modify.ldif"), {
1361 "DOMAINDN": names.domaindn})
1362 logger.info("Setting up sam.ldb data")
1363 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1364 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1365 "DOMAINDN": names.domaindn,
1366 "NETBIOSNAME": names.netbiosname,
1367 "DEFAULTSITE": names.sitename,
1368 "CONFIGDN": names.configdn,
1369 "SERVERDN": names.serverdn,
1370 "RIDAVAILABLESTART": str(next_rid + 600),
1371 "POLICYGUID_DC": policyguid_dc
1374 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1375 if fill == FILL_FULL:
1376 setup_modify_ldif(samdb,
1377 setup_path("provision_configuration_references.ldif"), {
1378 "CONFIGDN": names.configdn,
1379 "SCHEMADN": names.schemadn})
1381 logger.info("Setting up well known security principals")
1382 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1383 "CONFIGDN": names.configdn,
1386 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1387 setup_modify_ldif(samdb,
1388 setup_path("provision_basedn_references.ldif"),
1389 {"DOMAINDN": names.domaindn})
1391 logger.info("Setting up sam.ldb users and groups")
1392 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1393 "DOMAINDN": names.domaindn,
1394 "DOMAINSID": str(domainsid),
1395 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1396 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1399 logger.info("Setting up self join")
1400 setup_self_join(samdb, admin_session_info, names=names, fill=fill, invocationid=invocationid,
1402 machinepass=machinepass,
1403 domainsid=domainsid,
1406 policyguid=policyguid,
1407 policyguid_dc=policyguid_dc,
1408 domainControllerFunctionality=domainControllerFunctionality,
1411 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1412 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1413 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1414 assert isinstance(names.ntdsguid, str)
1416 samdb.transaction_cancel()
1419 samdb.transaction_commit()
1424 FILL_SUBDOMAIN = "SUBDOMAIN"
1425 FILL_NT4SYNC = "NT4SYNC"
1427 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1428 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)"
1431 def set_dir_acl(path, acl, lp, domsid):
1432 setntacl(lp, path, acl, domsid)
1433 for root, dirs, files in os.walk(path, topdown=False):
1435 setntacl(lp, os.path.join(root, name), acl, domsid)
1437 setntacl(lp, os.path.join(root, name), acl, domsid)
1440 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1441 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1444 :param sysvol: Physical path for the sysvol folder
1445 :param dnsdomain: The DNS name of the domain
1446 :param domainsid: The SID of the domain
1447 :param domaindn: The DN of the domain (ie. DC=...)
1448 :param samdb: An LDB object on the SAM db
1449 :param lp: an LP object
1452 # Set ACL for GPO root folder
1453 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1454 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1456 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1457 attrs=["cn", "nTSecurityDescriptor"],
1458 expression="", scope=ldb.SCOPE_ONELEVEL)
1461 acl = ndr_unpack(security.descriptor,
1462 str(policy["nTSecurityDescriptor"])).as_sddl()
1463 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1464 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1468 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1470 """Set the ACL for the sysvol share and the subfolders
1472 :param samdb: An LDB object on the SAM db
1473 :param netlogon: Physical path for the netlogon folder
1474 :param sysvol: Physical path for the sysvol folder
1475 :param gid: The GID of the "Domain adminstrators" group
1476 :param domainsid: The SID of the domain
1477 :param dnsdomain: The DNS name of the domain
1478 :param domaindn: The DN of the domain (ie. DC=...)
1482 os.chown(sysvol, -1, gid)
1488 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1489 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1490 for root, dirs, files in os.walk(sysvol, topdown=False):
1493 os.chown(os.path.join(root, name), -1, gid)
1494 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1497 os.chown(os.path.join(root, name), -1, gid)
1498 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1500 # Set acls on Policy folder and policies folders
1501 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1504 def interface_ips_v4(lp):
1505 '''return only IPv4 IPs'''
1506 ips = samba.interface_ips(lp, False)
1509 if i.find(':') == -1:
1513 def interface_ips_v6(lp, linklocal=False):
1514 '''return only IPv6 IPs'''
1515 ips = samba.interface_ips(lp, False)
1518 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1523 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1524 domainsid, schema=None,
1525 targetdir=None, samdb_fill=FILL_FULL,
1526 hostip=None, hostip6=None,
1527 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1528 domainguid=None, policyguid=None, policyguid_dc=None,
1529 invocationid=None, machinepass=None, ntdsguid=None,
1530 dns_backend=None, dnspass=None,
1531 serverrole=None, dom_for_fun_level=None,
1532 am_rodc=False, lp=None):
1533 # create/adapt the group policy GUIDs
1534 # Default GUID for default policy are described at
1535 # "How Core Group Policy Works"
1536 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1537 if policyguid is None:
1538 policyguid = DEFAULT_POLICY_GUID
1539 policyguid = policyguid.upper()
1540 if policyguid_dc is None:
1541 policyguid_dc = DEFAULT_DC_POLICY_GUID
1542 policyguid_dc = policyguid_dc.upper()
1544 if invocationid is None:
1545 invocationid = str(uuid.uuid4())
1547 if adminpass is None:
1548 adminpass = samba.generate_random_password(12, 32)
1549 if krbtgtpass is None:
1550 krbtgtpass = samba.generate_random_password(128, 255)
1551 if machinepass is None:
1552 machinepass = samba.generate_random_password(128, 255)
1554 dnspass = samba.generate_random_password(128, 255)
1556 samdb = fill_samdb(samdb, lp, names, logger=logger,
1557 domainsid=domainsid, schema=schema, domainguid=domainguid,
1558 policyguid=policyguid, policyguid_dc=policyguid_dc,
1559 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1560 invocationid=invocationid, machinepass=machinepass,
1561 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1562 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1563 next_rid=next_rid, dc_rid=dc_rid)
1565 if serverrole == "domain controller":
1566 # Set up group policies (domain policy and domain controller
1568 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1570 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1571 domainsid, names.dnsdomain, names.domaindn, lp)
1573 secretsdb_self_join(secrets_ldb, domain=names.domain,
1574 realm=names.realm, dnsdomain=names.dnsdomain,
1575 netbiosname=names.netbiosname, domainsid=domainsid,
1576 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1578 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1579 # In future, this might be determined from some configuration
1580 kerberos_enctypes = str(ENC_ALL_TYPES)
1583 msg = ldb.Message(ldb.Dn(samdb,
1584 samdb.searchone("distinguishedName",
1585 expression="samAccountName=%s$" % names.netbiosname,
1586 scope=ldb.SCOPE_SUBTREE)))
1587 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1588 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1589 name="msDS-SupportedEncryptionTypes")
1591 except ldb.LdbError, (enum, estr):
1592 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1593 # It might be that this attribute does not exist in this schema
1596 setup_ad_dns(samdb, secrets_ldb, names, paths, lp, logger,
1597 hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
1598 dnspass=dnspass, os_level=dom_for_fun_level,
1599 targetdir=targetdir, site=DEFAULTSITE)
1601 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1602 attribute="objectGUID")
1603 assert isinstance(domainguid, str)
1605 lastProvisionUSNs = get_last_provision_usn(samdb)
1606 maxUSN = get_max_usn(samdb, str(names.rootdn))
1607 if lastProvisionUSNs is not None:
1608 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1610 set_provision_usn(samdb, 0, maxUSN, invocationid)
1612 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1613 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1614 { 'NTDSGUID' : names.ntdsguid })
1616 # fix any dangling GUIDs from the provision
1617 logger.info("Fixing provision GUIDs")
1618 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, quiet=True)
1619 samdb.transaction_start()
1620 # a small number of GUIDs are missing because of ordering issues in the
1622 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1623 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1624 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1625 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1626 scope=ldb.SCOPE_ONELEVEL,
1627 attrs=['ipsecOwnersReference',
1628 'ipsecFilterReference',
1629 'ipsecISAKMPReference',
1630 'ipsecNegotiationPolicyReference',
1631 'ipsecNFAReference'])
1632 samdb.transaction_commit()
1635 def provision(logger, session_info, credentials, smbconf=None,
1636 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1637 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1638 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1639 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1640 domainguid=None, policyguid=None, policyguid_dc=None,
1641 dns_backend=None, dnspass=None,
1642 invocationid=None, machinepass=None, ntdsguid=None,
1643 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1644 serverrole=None, dom_for_fun_level=None,
1645 backend_type=None, sitename=None,
1646 ol_mmr_urls=None, ol_olc=None, slapd_path=None,
1647 useeadb=False, am_rodc=False,
1651 :note: caution, this wipes all existing data!
1654 if ldapadminpass is None:
1655 # Make a new, random password between Samba and it's LDAP server
1656 ldapadminpass=samba.generate_random_password(128, 255)
1658 if backend_type is None:
1659 backend_type = "ldb"
1661 if domainsid is None:
1662 domainsid = security.random_sid()
1664 domainsid = security.dom_sid(domainsid)
1666 sid_generator = "internal"
1667 if backend_type == "fedora-ds":
1668 sid_generator = "backend"
1670 root_uid = findnss_uid([root or "root"])
1671 nobody_uid = findnss_uid([nobody or "nobody"])
1672 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1674 wheel_gid = findnss_gid(["wheel", "adm"])
1676 wheel_gid = findnss_gid([wheel])
1678 bind_gid = findnss_gid(["bind", "named"])
1682 if targetdir is not None:
1683 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1684 elif smbconf is None:
1685 smbconf = samba.param.default_path()
1686 if not os.path.exists(os.path.dirname(smbconf)):
1687 os.makedirs(os.path.dirname(smbconf))
1689 # only install a new smb.conf if there isn't one there already
1690 if os.path.exists(smbconf):
1691 # if Samba Team members can't figure out the weird errors
1692 # loading an empty smb.conf gives, then we need to be smarter.
1693 # Pretend it just didn't exist --abartlet
1694 data = open(smbconf, 'r').read()
1695 data = data.lstrip()
1696 if data is None or data == "":
1697 make_smbconf(smbconf, hostname, domain, realm,
1698 serverrole, targetdir, sid_generator, useeadb,
1701 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1702 targetdir, sid_generator, useeadb, lp=lp)
1705 lp = samba.param.LoadParm()
1707 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1708 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1709 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1710 sitename=sitename, rootdn=rootdn)
1711 paths = provision_paths_from_lp(lp, names.dnsdomain)
1713 paths.bind_gid = bind_gid
1714 paths.wheel_gid = wheel_gid
1717 logger.info("Looking up IPv4 addresses")
1718 hostips = interface_ips_v4(lp)
1719 if len(hostips) > 0:
1721 if len(hostips) > 1:
1722 logger.warning("More than one IPv4 address found. Using %s",
1724 if hostip == "127.0.0.1":
1727 logger.warning("No IPv4 address will be assigned")
1730 logger.info("Looking up IPv6 addresses")
1731 hostips = interface_ips_v6(lp, linklocal=False)
1733 hostip6 = hostips[0]
1734 if len(hostips) > 1:
1735 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1737 logger.warning("No IPv6 address will be assigned")
1739 names.hostip = hostip
1740 names.hostip6 = hostip6
1742 if serverrole is None:
1743 serverrole = lp.get("server role")
1745 assert serverrole in ("domain controller", "member server", "standalone")
1747 if not os.path.exists(paths.private_dir):
1748 os.mkdir(paths.private_dir)
1749 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1750 os.mkdir(os.path.join(paths.private_dir, "tls"))
1752 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1754 schema = Schema(domainsid, invocationid=invocationid,
1755 schemadn=names.schemadn)
1757 if backend_type == "ldb":
1758 provision_backend = LDBBackend(backend_type, paths=paths,
1759 lp=lp, credentials=credentials,
1760 names=names, logger=logger)
1761 elif backend_type == "existing":
1762 # If support for this is ever added back, then the URI will need to be specified again
1763 provision_backend = ExistingBackend(backend_type, paths=paths,
1764 lp=lp, credentials=credentials,
1765 names=names, logger=logger,
1766 ldap_backend_forced_uri=None)
1767 elif backend_type == "fedora-ds":
1768 provision_backend = FDSBackend(backend_type, paths=paths,
1769 lp=lp, credentials=credentials,
1770 names=names, logger=logger, domainsid=domainsid,
1771 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1772 slapd_path=slapd_path,
1774 elif backend_type == "openldap":
1775 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1776 lp=lp, credentials=credentials,
1777 names=names, logger=logger, domainsid=domainsid,
1778 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1779 slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
1781 raise ValueError("Unknown LDAP backend type selected")
1783 provision_backend.init()
1784 provision_backend.start()
1786 # only install a new shares config db if there is none
1787 if not os.path.exists(paths.shareconf):
1788 logger.info("Setting up share.ldb")
1789 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1791 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1793 logger.info("Setting up secrets.ldb")
1794 secrets_ldb = setup_secretsdb(paths,
1795 session_info=session_info,
1796 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1799 logger.info("Setting up the registry")
1800 setup_registry(paths.hklm, session_info,
1803 logger.info("Setting up the privileges database")
1804 setup_privileges(paths.privilege, session_info, lp=lp)
1806 logger.info("Setting up idmap db")
1807 idmap = setup_idmapdb(paths.idmapdb,
1808 session_info=session_info, lp=lp)
1810 setup_name_mappings(idmap, sid=str(domainsid),
1811 root_uid=root_uid, nobody_uid=nobody_uid,
1812 users_gid=users_gid, wheel_gid=wheel_gid)
1814 logger.info("Setting up SAM db")
1815 samdb = setup_samdb(paths.samdb, session_info,
1816 provision_backend, lp, names, logger=logger,
1817 serverrole=serverrole,
1818 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1820 if serverrole == "domain controller":
1821 if paths.netlogon is None:
1822 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1823 logger.info("Please either remove %s or see the template at %s" %
1824 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1825 assert paths.netlogon is not None
1827 if paths.sysvol is None:
1828 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1829 " are configuring a DC.")
1830 logger.info("Please either remove %s or see the template at %s" %
1831 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1832 assert paths.sysvol is not None
1834 if not os.path.isdir(paths.netlogon):
1835 os.makedirs(paths.netlogon, 0755)
1837 if samdb_fill == FILL_FULL:
1838 provision_fill(samdb, secrets_ldb, logger,
1839 names, paths, schema=schema, targetdir=targetdir,
1840 samdb_fill=samdb_fill, hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1841 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1842 krbtgtpass=krbtgtpass, domainguid=domainguid,
1843 policyguid=policyguid, policyguid_dc=policyguid_dc,
1844 invocationid=invocationid, machinepass=machinepass,
1845 ntdsguid=ntdsguid, dns_backend=dns_backend, dnspass=dnspass,
1846 serverrole=serverrole, dom_for_fun_level=dom_for_fun_level,
1847 am_rodc=am_rodc, lp=lp)
1849 create_krb5_conf(paths.krb5conf,
1850 dnsdomain=names.dnsdomain, hostname=names.hostname,
1852 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1853 "generated at %s", paths.krb5conf)
1855 if serverrole == "domain controller":
1856 create_dns_update_list(lp, logger, paths)
1858 provision_backend.post_setup()
1859 provision_backend.shutdown()
1861 create_phpldapadmin_config(paths.phpldapadminconfig,
1864 secrets_ldb.transaction_cancel()
1867 # Now commit the secrets.ldb to disk
1868 secrets_ldb.transaction_commit()
1870 # the commit creates the dns.keytab, now chown it
1871 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1872 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1874 os.chmod(dns_keytab_path, 0640)
1875 os.chown(dns_keytab_path, -1, paths.bind_gid)
1877 if not os.environ.has_key('SAMBA_SELFTEST'):
1878 logger.info("Failed to chown %s to bind gid %u",
1879 dns_keytab_path, paths.bind_gid)
1881 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1882 paths.phpldapadminconfig)
1884 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1885 logger.info("Server Role: %s" % serverrole)
1886 logger.info("Hostname: %s" % names.hostname)
1887 logger.info("NetBIOS Domain: %s" % names.domain)
1888 logger.info("DNS Domain: %s" % names.dnsdomain)
1889 logger.info("DOMAIN SID: %s" % str(domainsid))
1890 if samdb_fill == FILL_FULL:
1891 logger.info("Admin password: %s" % adminpass)
1892 if provision_backend.type is not "ldb":
1893 if provision_backend.credentials.get_bind_dn() is not None:
1894 logger.info("LDAP Backend Admin DN: %s" %
1895 provision_backend.credentials.get_bind_dn())
1897 logger.info("LDAP Admin User: %s" %
1898 provision_backend.credentials.get_username())
1900 logger.info("LDAP Admin Password: %s" %
1901 provision_backend.credentials.get_password())
1903 if provision_backend.slapd_command_escaped is not None:
1904 # now display slapd_command_file.txt to show how slapd must be
1906 logger.info("Use later the following commandline to start slapd, then Samba:")
1907 logger.info(provision_backend.slapd_command_escaped)
1908 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1909 provision_backend.ldapdir)
1911 result = ProvisionResult()
1912 result.domaindn = domaindn
1913 result.paths = paths
1914 result.names = names
1916 result.samdb = samdb
1917 result.idmap = idmap
1921 def provision_become_dc(smbconf=None, targetdir=None,
1922 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1923 serverdn=None, domain=None, hostname=None, domainsid=None,
1924 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1925 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1926 dns_backend=None, root=None, nobody=None, users=None, wheel=None, backup=None,
1927 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1928 sitename=None, debuglevel=1):
1930 logger = logging.getLogger("provision")
1931 samba.set_debug_level(debuglevel)
1933 res = provision(logger, system_session(), None,
1934 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1935 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1936 configdn=configdn, serverdn=serverdn, domain=domain,
1937 hostname=hostname, hostip=None, domainsid=domainsid,
1938 machinepass=machinepass, serverrole="domain controller",
1939 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1940 res.lp.set("debuglevel", str(debuglevel))
1944 def create_phpldapadmin_config(path, ldapi_uri):
1945 """Create a PHP LDAP admin configuration file.
1947 :param path: Path to write the configuration to.
1949 setup_file(setup_path("phpldapadmin-config.php"), path,
1950 {"S4_LDAPI_URI": ldapi_uri})
1953 def create_krb5_conf(path, dnsdomain, hostname, realm):
1954 """Write out a file containing zone statements suitable for inclusion in a
1955 named.conf file (including GSS-TSIG configuration).
1957 :param path: Path of the new named.conf file.
1958 :param dnsdomain: DNS Domain name
1959 :param hostname: Local hostname
1960 :param realm: Realm name
1962 setup_file(setup_path("krb5.conf"), path, {
1963 "DNSDOMAIN": dnsdomain,
1964 "HOSTNAME": hostname,
1969 class ProvisioningError(Exception):
1970 """A generic provision error."""
1972 def __init__(self, value):
1976 return "ProvisioningError: " + self.value
1979 class InvalidNetbiosName(Exception):
1980 """A specified name was not a valid NetBIOS name."""
1981 def __init__(self, name):
1982 super(InvalidNetbiosName, self).__init__(
1983 "The name '%r' is not a valid NetBIOS name" % name)