s4 provision: DNS backend should be set by caller
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision / __init__.py
blob5e7e374ab9e0da0296bbd1b791eaa692c2d78743
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
31 import os
32 import re
33 import pwd
34 import grp
35 import logging
36 import time
37 import uuid
38 import socket
39 import urllib
40 import shutil
41 import string
43 import ldb
45 from samba.auth import system_session, admin_session
46 import samba
47 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
48 from samba import (
49 Ldb,
50 check_all_substituted,
51 read_and_sub_file,
52 setup_file,
53 substitute_var,
54 valid_netbios_name,
55 version,
57 from samba.dcerpc import security, misc
58 from samba.dcerpc.misc import (
59 SEC_CHAN_BDC,
60 SEC_CHAN_WKSTA,
62 from samba.dsdb import (
63 DS_DOMAIN_FUNCTION_2003,
64 DS_DOMAIN_FUNCTION_2008_R2,
65 ENC_ALL_TYPES,
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 (
72 ExistingBackend,
73 FDSBackend,
74 LDBBackend,
75 OpenLDAPBackend,
77 from samba.provision.sambadns import setup_ad_dns
79 import samba.param
80 import samba.registry
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"
93 def setup_path(file):
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)
118 return ndr_pack(sec)
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)" \
161 "(A;;RPRC;;;RU)" \
162 "(A;CI;LC;;;RU)" \
163 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
164 "(A;;RP;;;WD)" \
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)
172 return ndr_pack(sec)
175 class ProvisionPaths(object):
177 def __init__(self):
178 self.shareconf = None
179 self.hklm = None
180 self.hkcu = None
181 self.hkcr = None
182 self.hku = None
183 self.hkpd = None
184 self.hkpt = None
185 self.samdb = None
186 self.idmapdb = None
187 self.secrets = None
188 self.keytab = None
189 self.dns_keytab = None
190 self.dns = None
191 self.winsdb = None
192 self.private_dir = None
195 class ProvisionNames(object):
197 def __init__(self):
198 self.rootdn = None
199 self.domaindn = None
200 self.configdn = None
201 self.schemadn = None
202 self.ldapmanagerdn = None
203 self.dnsdomain = None
204 self.realm = None
205 self.netbiosname = None
206 self.domain = None
207 self.hostname = None
208 self.sitename = None
209 self.smbconf = 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)
231 # netbiosname
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
241 # partitions
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"]
259 # default site name
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]))
281 # domain guid/sid
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
290 else:
291 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
293 # policy guid
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("}","")
298 # dc policy guid
299 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
300 " Policy)",
301 base="CN=Policies,CN=System," + basedn,
302 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
303 if len(res8) == 1:
304 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
305 else:
306 names.policyid_dc = None
307 res9 = idmapdb.search(expression="(cn=%s)" %
308 (security.SID_BUILTIN_ADMINISTRATORS),
309 attrs=["xidNumber"])
310 if len(res9) == 1:
311 names.wheel_gid = res9[0]["xidNumber"]
312 else:
313 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
314 return names
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
320 upgradeprovision.
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)
332 tab = []
333 if not replace:
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)
340 tab.append(str(e))
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")
352 samdb.modify(delta)
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
358 upgradeprovision.
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"""
367 tab = []
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)
374 samdb.add(delta)
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
382 (ie. DC=foo, DC=bar)
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
400 try:
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:
406 return None
407 raise
408 if len(entry):
409 myids = []
410 range = {}
411 p = re.compile(r'-')
412 if entry[0].get("provisionnerID"):
413 for e in entry[0]["provisionnerID"]:
414 myids.append(str(e))
415 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
416 tab1 = str(r).split(';')
417 if len(tab1) == 2:
418 id = tab1[1]
419 else:
420 id = "default"
421 if (len(myids) > 0 and id not in myids):
422 continue
423 tab2 = p.split(tab1[0])
424 if range.get(id) == None:
425 range[id] = []
426 range[id].append(tab2[0])
427 range[id].append(tab2[1])
428 return range
429 else:
430 return None
433 class ProvisionResult(object):
435 def __init__(self):
436 self.paths = None
437 self.domaindn = None
438 self.lp = None
439 self.samdb = None
440 self.idmap = None
441 self.names = None
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.
466 for name in names:
467 try:
468 return nssfn(name)
469 except KeyError:
470 pass
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
504 variables.
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()
514 try:
515 setup_add_ldif(ldb, ldif_path, subst_vars)
516 except Exception:
517 ldb.transaction_cancel()
518 raise
519 else:
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
562 return paths
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."""
570 if hostname is None:
571 hostname = socket.gethostname().split(".")[0]
573 netbiosname = lp.get("netbios name")
574 if netbiosname is None:
575 netbiosname = hostname
576 # remove forbidden chars
577 newnbname = ""
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":
614 if domain is None:
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))
622 if domaindn is None:
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))
627 else:
628 domain = netbiosname
629 if domaindn is None:
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))
639 if domain == realm:
640 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
642 if rootdn is None:
643 rootdn = domaindn
645 if configdn is None:
646 configdn = "CN=Configuration," + rootdn
647 if schemadn is None:
648 schemadn = "CN=Schema," + configdn
650 if sitename is None:
651 sitename=DEFAULTSITE
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
661 names.realm = realm
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)
668 return names
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
676 if hostname is None:
677 hostname = socket.gethostname().split(".")[0]
678 netbiosname = hostname.upper()
679 # remove forbidden chars
680 newnbname = ""
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]
686 else:
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":
694 smbconfsuffix = "dc"
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()
709 if lp is None:
710 lp = samba.param.LoadParm()
711 #Load non-existant file
712 if os.path.exists(smbconf):
713 lp.load(smbconf)
714 if eadb and not lp.get("posix:eadb"):
715 if targetdir is not None:
716 privdir = os.path.join(targetdir, "private")
717 else:
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))
730 else:
731 privatedir_line = ""
732 lockdir_line = ""
733 statedir_line = ""
734 cachedir_line = ""
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),
740 smbconf, {
741 "NETBIOS_NAME": netbiosname,
742 "DOMAIN": domain,
743 "REALM": realm,
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
754 lp.load(smbconf)
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')
760 lp.dump(f, False)
761 f.close()
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,
787 erase=False):
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
804 try:
805 os.unlink(samdb_path)
806 except OSError:
807 pass
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()
817 try:
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)
831 except Exception:
832 samdb.transaction_cancel()
833 raise
834 else:
835 samdb.transaction_commit()
838 def secretsdb_self_join(secretsdb, domain,
839 netbiosname, machinepass, domainsid=None,
840 realm=None, dnsdomain=None,
841 keytab_path=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",
850 "secret",
851 "priorSecret",
852 "priorChanged",
853 "krb5Keytab",
854 "privateKeytab"]
856 if realm is not None:
857 if dnsdomain is None:
858 dnsdomain = realm.lower()
859 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
860 else:
861 dnsname = None
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)
890 for del_msg in res:
891 secretsdb.delete(del_msg.dn)
893 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
895 if len(res) == 1:
896 msg["priorSecret"] = [res[0]["secret"][0]]
897 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
899 try:
900 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
901 except KeyError:
902 pass
904 try:
905 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
906 except KeyError:
907 pass
909 for el in msg:
910 if el != 'dn':
911 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
912 secretsdb.modify(msg)
913 secretsdb.rename(res[0].dn, msg.dn)
914 else:
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
922 secretsdb.add(msg)
925 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
926 dnsdomain, dns_keytab_path, dnspass):
927 """Add DNS specific bits to a secrets database.
929 :param secretsdb: Ldb Handle to the secrets database
930 :param machinepass: Machine password
932 try:
933 os.unlink(os.path.join(private_dir, dns_keytab_path))
934 except OSError:
935 pass
937 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
938 "REALM": realm,
939 "DNSDOMAIN": dnsdomain,
940 "DNS_KEYTAB": dns_keytab_path,
941 "DNSPASS_B64": b64encode(dnspass),
942 "HOSTNAME": names.hostname,
943 "DNSNAME" : '%s.%s' % (
944 names.netbiosname.lower(), names.dnsdomain.lower())
948 def setup_secretsdb(paths, session_info, backend_credentials, lp):
949 """Setup the secrets database.
951 :note: This function does not handle exceptions and transaction on purpose,
952 it's up to the caller to do this job.
954 :param path: Path to the secrets database.
955 :param session_info: Session info.
956 :param credentials: Credentials
957 :param lp: Loadparm context
958 :return: LDB handle for the created secrets database
960 if os.path.exists(paths.secrets):
961 os.unlink(paths.secrets)
963 keytab_path = os.path.join(paths.private_dir, paths.keytab)
964 if os.path.exists(keytab_path):
965 os.unlink(keytab_path)
967 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
968 if os.path.exists(dns_keytab_path):
969 os.unlink(dns_keytab_path)
971 path = paths.secrets
973 secrets_ldb = Ldb(path, session_info=session_info,
974 lp=lp)
975 secrets_ldb.erase()
976 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
977 secrets_ldb = Ldb(path, session_info=session_info,
978 lp=lp)
979 secrets_ldb.transaction_start()
980 try:
981 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
983 if (backend_credentials is not None and
984 backend_credentials.authentication_requested()):
985 if backend_credentials.get_bind_dn() is not None:
986 setup_add_ldif(secrets_ldb,
987 setup_path("secrets_simple_ldap.ldif"), {
988 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
989 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
991 else:
992 setup_add_ldif(secrets_ldb,
993 setup_path("secrets_sasl_ldap.ldif"), {
994 "LDAPADMINUSER": backend_credentials.get_username(),
995 "LDAPADMINREALM": backend_credentials.get_realm(),
996 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
998 except Exception:
999 secrets_ldb.transaction_cancel()
1000 raise
1001 return secrets_ldb
1005 def setup_privileges(path, session_info, lp):
1006 """Setup the privileges database.
1008 :param path: Path to the privileges database.
1009 :param session_info: Session info.
1010 :param credentials: Credentials
1011 :param lp: Loadparm context
1012 :return: LDB handle for the created secrets database
1014 if os.path.exists(path):
1015 os.unlink(path)
1016 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1017 privilege_ldb.erase()
1018 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1021 def setup_registry(path, session_info, lp):
1022 """Setup the registry.
1024 :param path: Path to the registry database
1025 :param session_info: Session information
1026 :param credentials: Credentials
1027 :param lp: Loadparm context
1029 reg = samba.registry.Registry()
1030 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1031 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1032 provision_reg = setup_path("provision.reg")
1033 assert os.path.exists(provision_reg)
1034 reg.diff_apply(provision_reg)
1037 def setup_idmapdb(path, session_info, lp):
1038 """Setup the idmap database.
1040 :param path: path to the idmap database
1041 :param session_info: Session information
1042 :param credentials: Credentials
1043 :param lp: Loadparm context
1045 if os.path.exists(path):
1046 os.unlink(path)
1048 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1049 idmap_ldb.erase()
1050 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1051 return idmap_ldb
1054 def setup_samdb_rootdse(samdb, names):
1055 """Setup the SamDB rootdse.
1057 :param samdb: Sam Database handle
1059 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1060 "SCHEMADN": names.schemadn,
1061 "DOMAINDN": names.domaindn,
1062 "ROOTDN" : names.rootdn,
1063 "CONFIGDN": names.configdn,
1064 "SERVERDN": names.serverdn,
1068 def setup_self_join(samdb, admin_session_info, names, fill, machinepass, dnspass,
1069 domainsid, next_rid, invocationid,
1070 policyguid, policyguid_dc, domainControllerFunctionality,
1071 ntdsguid, dc_rid=None):
1072 """Join a host to its own domain."""
1073 assert isinstance(invocationid, str)
1074 if ntdsguid is not None:
1075 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1076 else:
1077 ntdsguid_line = ""
1079 if dc_rid is None:
1080 dc_rid = next_rid
1082 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1083 "CONFIGDN": names.configdn,
1084 "SCHEMADN": names.schemadn,
1085 "DOMAINDN": names.domaindn,
1086 "SERVERDN": names.serverdn,
1087 "INVOCATIONID": invocationid,
1088 "NETBIOSNAME": names.netbiosname,
1089 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1090 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1091 "DOMAINSID": str(domainsid),
1092 "DCRID": str(dc_rid),
1093 "SAMBA_VERSION_STRING": version,
1094 "NTDSGUID": ntdsguid_line,
1095 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1096 domainControllerFunctionality),
1097 "RIDALLOCATIONSTART": str(next_rid + 100),
1098 "RIDALLOCATIONEND": str(next_rid + 100 + 499)})
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 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1107 if fill == FILL_FULL:
1108 setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
1109 "CONFIGDN": names.configdn,
1110 "SCHEMADN": names.schemadn,
1111 "DOMAINDN": names.domaindn,
1112 "SERVERDN": names.serverdn,
1113 "INVOCATIONID": invocationid,
1114 "NETBIOSNAME": names.netbiosname,
1115 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1116 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1117 "DOMAINSID": str(domainsid),
1118 "DCRID": str(dc_rid),
1119 "SAMBA_VERSION_STRING": version,
1120 "NTDSGUID": ntdsguid_line,
1121 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1122 domainControllerFunctionality)})
1124 # Setup fSMORoleOwner entries to point at the newly created DC entry
1125 setup_modify_ldif(samdb, setup_path("provision_self_join_modify_config.ldif"), {
1126 "CONFIGDN": names.configdn,
1127 "SCHEMADN": names.schemadn,
1128 "DEFAULTSITE": names.sitename,
1129 "NETBIOSNAME": names.netbiosname,
1130 "SERVERDN": names.serverdn,
1133 system_session_info = system_session()
1134 samdb.set_session_info(system_session_info)
1135 # Setup fSMORoleOwner entries to point at the newly created DC entry
1137 # to modify a serverReference under cn=config when we are a subdomain, we must
1138 # be system due to ACLs
1139 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1140 "DOMAINDN": names.domaindn,
1141 "SERVERDN": names.serverdn,
1142 "NETBIOSNAME": names.netbiosname,
1145 samdb.set_session_info(admin_session_info)
1147 # This is Samba4 specific and should be replaced by the correct
1148 # DNS AD-style setup
1149 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1150 "DNSDOMAIN": names.dnsdomain,
1151 "DOMAINDN": names.domaindn,
1152 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1153 "HOSTNAME" : names.hostname,
1154 "DNSNAME" : '%s.%s' % (
1155 names.netbiosname.lower(), names.dnsdomain.lower())
1159 def getpolicypath(sysvolpath, dnsdomain, guid):
1160 """Return the physical path of policy given its guid.
1162 :param sysvolpath: Path to the sysvol folder
1163 :param dnsdomain: DNS name of the AD domain
1164 :param guid: The GUID of the policy
1165 :return: A string with the complete path to the policy folder
1168 if guid[0] != "{":
1169 guid = "{%s}" % guid
1170 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1171 return policy_path
1174 def create_gpo_struct(policy_path):
1175 if not os.path.exists(policy_path):
1176 os.makedirs(policy_path, 0775)
1177 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1178 "[General]\r\nVersion=0")
1179 p = os.path.join(policy_path, "MACHINE")
1180 if not os.path.exists(p):
1181 os.makedirs(p, 0775)
1182 p = os.path.join(policy_path, "USER")
1183 if not os.path.exists(p):
1184 os.makedirs(p, 0775)
1187 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1188 """Create the default GPO for a domain
1190 :param sysvolpath: Physical path for the sysvol folder
1191 :param dnsdomain: DNS domain name of the AD domain
1192 :param policyguid: GUID of the default domain policy
1193 :param policyguid_dc: GUID of the default domain controler policy
1195 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1196 create_gpo_struct(policy_path)
1198 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1199 create_gpo_struct(policy_path)
1202 def setup_samdb(path, session_info, provision_backend, lp, names,
1203 logger, fill, serverrole,
1204 am_rodc=False, schema=None):
1205 """Setup a complete SAM Database.
1207 :note: This will wipe the main SAM database file!
1210 # Also wipes the database
1211 setup_samdb_partitions(path, logger=logger, lp=lp,
1212 provision_backend=provision_backend, session_info=session_info,
1213 names=names, serverrole=serverrole, schema=schema)
1215 if schema is None:
1216 schema = Schema(domainsid, schemadn=names.schemadn)
1218 # Load the database, but don's load the global schema and don't connect
1219 # quite yet
1220 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1221 credentials=provision_backend.credentials, lp=lp,
1222 global_schema=False, am_rodc=am_rodc)
1224 logger.info("Pre-loading the Samba 4 and AD schema")
1226 # Load the schema from the one we computed earlier
1227 samdb.set_schema(schema)
1229 # Set the NTDS settings DN manually - in order to have it already around
1230 # before the provisioned tree exists and we connect
1231 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1233 # And now we can connect to the DB - the schema won't be loaded from the
1234 # DB
1235 samdb.connect(path)
1237 return samdb
1239 def fill_samdb(samdb, lp, names,
1240 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1241 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1242 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1243 next_rid=None, dc_rid=None):
1245 if next_rid is None:
1246 next_rid = 1000
1248 # Provision does not make much sense values larger than 1000000000
1249 # as the upper range of the rIDAvailablePool is 1073741823 and
1250 # we don't want to create a domain that cannot allocate rids.
1251 if next_rid < 1000 or next_rid > 1000000000:
1252 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1253 error += "the valid range is %u-%u. The default is %u." % (
1254 1000, 1000000000, 1000)
1255 raise ProvisioningError(error)
1257 # ATTENTION: Do NOT change these default values without discussion with the
1258 # team and/or release manager. They have a big impact on the whole program!
1259 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1261 if dom_for_fun_level is None:
1262 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1264 if dom_for_fun_level > domainControllerFunctionality:
1265 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!")
1267 domainFunctionality = dom_for_fun_level
1268 forestFunctionality = dom_for_fun_level
1270 # Set the NTDS settings DN manually - in order to have it already around
1271 # before the provisioned tree exists and we connect
1272 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1274 samdb.transaction_start()
1275 try:
1276 # Set the domain functionality levels onto the database.
1277 # Various module (the password_hash module in particular) need
1278 # to know what level of AD we are emulating.
1280 # These will be fixed into the database via the database
1281 # modifictions below, but we need them set from the start.
1282 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1283 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1284 samdb.set_opaque_integer("domainControllerFunctionality",
1285 domainControllerFunctionality)
1287 samdb.set_domain_sid(str(domainsid))
1288 samdb.set_invocation_id(invocationid)
1290 logger.info("Adding DomainDN: %s" % names.domaindn)
1292 # impersonate domain admin
1293 admin_session_info = admin_session(lp, str(domainsid))
1294 samdb.set_session_info(admin_session_info)
1295 if domainguid is not None:
1296 domainguid_line = "objectGUID: %s\n-" % domainguid
1297 else:
1298 domainguid_line = ""
1300 descr = b64encode(get_domain_descriptor(domainsid))
1301 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1302 "DOMAINDN": names.domaindn,
1303 "DOMAINSID": str(domainsid),
1304 "DESCRIPTOR": descr,
1305 "DOMAINGUID": domainguid_line
1308 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1309 "DOMAINDN": names.domaindn,
1310 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1311 "NEXTRID": str(next_rid),
1312 "DEFAULTSITE": names.sitename,
1313 "CONFIGDN": names.configdn,
1314 "POLICYGUID": policyguid,
1315 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1316 "SAMBA_VERSION_STRING": version
1319 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1320 if fill == FILL_FULL:
1321 logger.info("Adding configuration container")
1322 descr = b64encode(get_config_descriptor(domainsid))
1323 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1324 "CONFIGDN": names.configdn,
1325 "DESCRIPTOR": descr,
1328 # The LDIF here was created when the Schema object was constructed
1329 logger.info("Setting up sam.ldb schema")
1330 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1331 samdb.modify_ldif(schema.schema_dn_modify)
1332 samdb.write_prefixes_from_schema()
1333 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1334 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1335 {"SCHEMADN": names.schemadn})
1337 # Now register this container in the root of the forest
1338 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1339 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1340 "subRefs")
1342 except Exception:
1343 samdb.transaction_cancel()
1344 raise
1345 else:
1346 samdb.transaction_commit()
1348 samdb.transaction_start()
1349 try:
1350 samdb.invocation_id = invocationid
1352 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1353 if fill == FILL_FULL:
1354 logger.info("Setting up sam.ldb configuration data")
1355 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1356 "CONFIGDN": names.configdn,
1357 "NETBIOSNAME": names.netbiosname,
1358 "DEFAULTSITE": names.sitename,
1359 "DNSDOMAIN": names.dnsdomain,
1360 "DOMAIN": names.domain,
1361 "SCHEMADN": names.schemadn,
1362 "DOMAINDN": names.domaindn,
1363 "SERVERDN": names.serverdn,
1364 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1365 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1368 logger.info("Setting up display specifiers")
1369 display_specifiers_ldif = read_ms_ldif(
1370 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1371 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1372 {"CONFIGDN": names.configdn})
1373 check_all_substituted(display_specifiers_ldif)
1374 samdb.add_ldif(display_specifiers_ldif)
1376 logger.info("Adding users container")
1377 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1378 "DOMAINDN": names.domaindn})
1379 logger.info("Modifying users container")
1380 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1381 "DOMAINDN": names.domaindn})
1382 logger.info("Adding computers container")
1383 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1384 "DOMAINDN": names.domaindn})
1385 logger.info("Modifying computers container")
1386 setup_modify_ldif(samdb,
1387 setup_path("provision_computers_modify.ldif"), {
1388 "DOMAINDN": names.domaindn})
1389 logger.info("Setting up sam.ldb data")
1390 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1391 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1392 "DOMAINDN": names.domaindn,
1393 "NETBIOSNAME": names.netbiosname,
1394 "DEFAULTSITE": names.sitename,
1395 "CONFIGDN": names.configdn,
1396 "SERVERDN": names.serverdn,
1397 "RIDAVAILABLESTART": str(next_rid + 600),
1398 "POLICYGUID_DC": policyguid_dc
1401 # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
1402 if fill == FILL_FULL:
1403 setup_modify_ldif(samdb,
1404 setup_path("provision_configuration_references.ldif"), {
1405 "CONFIGDN": names.configdn,
1406 "SCHEMADN": names.schemadn})
1408 logger.info("Setting up well known security principals")
1409 setup_add_ldif(samdb, setup_path("provision_well_known_sec_princ.ldif"), {
1410 "CONFIGDN": names.configdn,
1413 if fill == FILL_FULL or fill == FILL_SUBDOMAIN:
1414 setup_modify_ldif(samdb,
1415 setup_path("provision_basedn_references.ldif"),
1416 {"DOMAINDN": names.domaindn})
1418 logger.info("Setting up sam.ldb users and groups")
1419 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1420 "DOMAINDN": names.domaindn,
1421 "DOMAINSID": str(domainsid),
1422 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1423 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1426 logger.info("Setting up self join")
1427 setup_self_join(samdb, admin_session_info, names=names, fill=fill, invocationid=invocationid,
1428 dnspass=dnspass,
1429 machinepass=machinepass,
1430 domainsid=domainsid,
1431 next_rid=next_rid,
1432 dc_rid=dc_rid,
1433 policyguid=policyguid,
1434 policyguid_dc=policyguid_dc,
1435 domainControllerFunctionality=domainControllerFunctionality,
1436 ntdsguid=ntdsguid)
1438 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1439 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1440 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1441 assert isinstance(names.ntdsguid, str)
1442 except Exception:
1443 samdb.transaction_cancel()
1444 raise
1445 else:
1446 samdb.transaction_commit()
1447 return samdb
1450 FILL_FULL = "FULL"
1451 FILL_SUBDOMAIN = "SUBDOMAIN"
1452 FILL_NT4SYNC = "NT4SYNC"
1453 FILL_DRS = "DRS"
1454 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1455 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)"
1458 def set_dir_acl(path, acl, lp, domsid):
1459 setntacl(lp, path, acl, domsid)
1460 for root, dirs, files in os.walk(path, topdown=False):
1461 for name in files:
1462 setntacl(lp, os.path.join(root, name), acl, domsid)
1463 for name in dirs:
1464 setntacl(lp, os.path.join(root, name), acl, domsid)
1467 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1468 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1469 folders beneath.
1471 :param sysvol: Physical path for the sysvol folder
1472 :param dnsdomain: The DNS name of the domain
1473 :param domainsid: The SID of the domain
1474 :param domaindn: The DN of the domain (ie. DC=...)
1475 :param samdb: An LDB object on the SAM db
1476 :param lp: an LP object
1479 # Set ACL for GPO root folder
1480 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1481 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1483 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1484 attrs=["cn", "nTSecurityDescriptor"],
1485 expression="", scope=ldb.SCOPE_ONELEVEL)
1487 for policy in res:
1488 acl = ndr_unpack(security.descriptor,
1489 str(policy["nTSecurityDescriptor"])).as_sddl()
1490 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1491 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1492 str(domainsid))
1495 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1496 lp):
1497 """Set the ACL for the sysvol share and the subfolders
1499 :param samdb: An LDB object on the SAM db
1500 :param netlogon: Physical path for the netlogon folder
1501 :param sysvol: Physical path for the sysvol folder
1502 :param gid: The GID of the "Domain adminstrators" group
1503 :param domainsid: The SID of the domain
1504 :param dnsdomain: The DNS name of the domain
1505 :param domaindn: The DN of the domain (ie. DC=...)
1508 try:
1509 os.chown(sysvol, -1, gid)
1510 except OSError:
1511 canchown = False
1512 else:
1513 canchown = True
1515 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1516 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1517 for root, dirs, files in os.walk(sysvol, topdown=False):
1518 for name in files:
1519 if canchown:
1520 os.chown(os.path.join(root, name), -1, gid)
1521 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1522 for name in dirs:
1523 if canchown:
1524 os.chown(os.path.join(root, name), -1, gid)
1525 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1527 # Set acls on Policy folder and policies folders
1528 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1531 def interface_ips_v4(lp):
1532 '''return only IPv4 IPs'''
1533 ips = samba.interface_ips(lp, False)
1534 ret = []
1535 for i in ips:
1536 if i.find(':') == -1:
1537 ret.append(i)
1538 return ret
1540 def interface_ips_v6(lp, linklocal=False):
1541 '''return only IPv6 IPs'''
1542 ips = samba.interface_ips(lp, False)
1543 ret = []
1544 for i in ips:
1545 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1546 ret.append(i)
1547 return ret
1550 def provision_fill(samdb, secrets_ldb, logger, names, paths,
1551 domainsid, schema=None,
1552 targetdir=None, samdb_fill=FILL_FULL,
1553 hostip=None, hostip6=None,
1554 next_rid=1000, dc_rid=None, adminpass=None, krbtgtpass=None,
1555 domainguid=None, policyguid=None, policyguid_dc=None,
1556 invocationid=None, machinepass=None, ntdsguid=None,
1557 dns_backend=None, dnspass=None,
1558 serverrole=None, dom_for_fun_level=None,
1559 am_rodc=False, lp=None):
1560 # create/adapt the group policy GUIDs
1561 # Default GUID for default policy are described at
1562 # "How Core Group Policy Works"
1563 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1564 if policyguid is None:
1565 policyguid = DEFAULT_POLICY_GUID
1566 policyguid = policyguid.upper()
1567 if policyguid_dc is None:
1568 policyguid_dc = DEFAULT_DC_POLICY_GUID
1569 policyguid_dc = policyguid_dc.upper()
1571 if invocationid is None:
1572 invocationid = str(uuid.uuid4())
1574 if adminpass is None:
1575 adminpass = samba.generate_random_password(12, 32)
1576 if krbtgtpass is None:
1577 krbtgtpass = samba.generate_random_password(128, 255)
1578 if machinepass is None:
1579 machinepass = samba.generate_random_password(128, 255)
1580 if dnspass is None:
1581 dnspass = samba.generate_random_password(128, 255)
1583 samdb = fill_samdb(samdb, lp, names, logger=logger,
1584 domainsid=domainsid, schema=schema, domainguid=domainguid,
1585 policyguid=policyguid, policyguid_dc=policyguid_dc,
1586 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1587 invocationid=invocationid, machinepass=machinepass,
1588 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1589 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1590 next_rid=next_rid, dc_rid=dc_rid)
1592 if serverrole == "domain controller":
1593 # Set up group policies (domain policy and domain controller
1594 # policy)
1595 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1596 policyguid_dc)
1597 setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.wheel_gid,
1598 domainsid, names.dnsdomain, names.domaindn, lp)
1600 secretsdb_self_join(secrets_ldb, domain=names.domain,
1601 realm=names.realm, dnsdomain=names.dnsdomain,
1602 netbiosname=names.netbiosname, domainsid=domainsid,
1603 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1605 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1606 # In future, this might be determined from some configuration
1607 kerberos_enctypes = str(ENC_ALL_TYPES)
1609 try:
1610 msg = ldb.Message(ldb.Dn(samdb,
1611 samdb.searchone("distinguishedName",
1612 expression="samAccountName=%s$" % names.netbiosname,
1613 scope=ldb.SCOPE_SUBTREE)))
1614 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1615 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1616 name="msDS-SupportedEncryptionTypes")
1617 samdb.modify(msg)
1618 except ldb.LdbError, (enum, estr):
1619 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1620 # It might be that this attribute does not exist in this schema
1621 raise
1623 secretsdb_setup_dns(secrets_ldb, names,
1624 paths.private_dir, realm=names.realm,
1625 dnsdomain=names.dnsdomain,
1626 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1628 setup_ad_dns(samdb, names, logger, hostip=hostip, hostip6=hostip6,
1629 dns_backend=dns_backend, os_level=dom_for_fun_level)
1631 domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
1632 attribute="objectGUID")
1633 assert isinstance(domainguid, str)
1635 create_dns_dir(logger, paths)
1637 # Only make a zone file on the first DC, it should be
1638 # replicated with DNS replication
1639 if dns_backend == "BIND9_FLATFILE":
1640 create_zone_file(lp, logger, paths, targetdir,
1641 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1642 hostname=names.hostname, realm=names.realm,
1643 domainguid=domainguid, ntdsguid=names.ntdsguid)
1645 create_named_conf(paths, realm=names.realm,
1646 dnsdomain=names.dnsdomain, dns_backend=dns_backend)
1648 create_named_txt(paths.namedtxt,
1649 realm=names.realm, dnsdomain=names.dnsdomain,
1650 dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1651 private_dir=paths.private_dir,
1652 keytab_name=paths.dns_keytab)
1653 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1654 logger.info("and %s for further documentation required for secure DNS "
1655 "updates", paths.namedtxt)
1657 lastProvisionUSNs = get_last_provision_usn(samdb)
1658 maxUSN = get_max_usn(samdb, str(names.rootdn))
1659 if lastProvisionUSNs is not None:
1660 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1661 else:
1662 set_provision_usn(samdb, 0, maxUSN, invocationid)
1664 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1665 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1666 { 'NTDSGUID' : names.ntdsguid })
1668 # fix any dangling GUIDs from the provision
1669 logger.info("Fixing provision GUIDs")
1670 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, quiet=True)
1671 samdb.transaction_start()
1672 # a small number of GUIDs are missing because of ordering issues in the
1673 # provision code
1674 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1675 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1676 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1677 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1678 scope=ldb.SCOPE_ONELEVEL,
1679 attrs=['ipsecOwnersReference',
1680 'ipsecFilterReference',
1681 'ipsecISAKMPReference',
1682 'ipsecNegotiationPolicyReference',
1683 'ipsecNFAReference'])
1684 samdb.transaction_commit()
1687 def provision(logger, session_info, credentials, smbconf=None,
1688 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1689 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1690 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1691 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1692 domainguid=None, policyguid=None, policyguid_dc=None,
1693 dns_backend=None, dnspass=None,
1694 invocationid=None, machinepass=None, ntdsguid=None,
1695 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1696 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1697 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1698 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1699 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1700 lp=None):
1701 """Provision samba4
1703 :note: caution, this wipes all existing data!
1706 if ldapadminpass is None:
1707 # Make a new, random password between Samba and it's LDAP server
1708 ldapadminpass=samba.generate_random_password(128, 255)
1710 if backend_type is None:
1711 backend_type = "ldb"
1713 if domainsid is None:
1714 domainsid = security.random_sid()
1715 else:
1716 domainsid = security.dom_sid(domainsid)
1718 sid_generator = "internal"
1719 if backend_type == "fedora-ds":
1720 sid_generator = "backend"
1722 root_uid = findnss_uid([root or "root"])
1723 nobody_uid = findnss_uid([nobody or "nobody"])
1724 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1725 if wheel is None:
1726 wheel_gid = findnss_gid(["wheel", "adm"])
1727 else:
1728 wheel_gid = findnss_gid([wheel])
1729 try:
1730 bind_gid = findnss_gid(["bind", "named"])
1731 except KeyError:
1732 bind_gid = None
1734 if targetdir is not None:
1735 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1736 elif smbconf is None:
1737 smbconf = samba.param.default_path()
1738 if not os.path.exists(os.path.dirname(smbconf)):
1739 os.makedirs(os.path.dirname(smbconf))
1741 # only install a new smb.conf if there isn't one there already
1742 if os.path.exists(smbconf):
1743 # if Samba Team members can't figure out the weird errors
1744 # loading an empty smb.conf gives, then we need to be smarter.
1745 # Pretend it just didn't exist --abartlet
1746 data = open(smbconf, 'r').read()
1747 data = data.lstrip()
1748 if data is None or data == "":
1749 make_smbconf(smbconf, hostname, domain, realm,
1750 serverrole, targetdir, sid_generator, useeadb,
1751 lp=lp)
1752 else:
1753 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1754 targetdir, sid_generator, useeadb, lp=lp)
1756 if lp is None:
1757 lp = samba.param.LoadParm()
1758 lp.load(smbconf)
1759 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1760 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1761 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1762 sitename=sitename, rootdn=rootdn)
1763 paths = provision_paths_from_lp(lp, names.dnsdomain)
1765 paths.bind_gid = bind_gid
1766 paths.wheel_gid = wheel_gid
1768 if hostip is None:
1769 logger.info("Looking up IPv4 addresses")
1770 hostips = interface_ips_v4(lp)
1771 if len(hostips) > 0:
1772 hostip = hostips[0]
1773 if len(hostips) > 1:
1774 logger.warning("More than one IPv4 address found. Using %s",
1775 hostip)
1776 if hostip == "127.0.0.1":
1777 hostip = None
1778 if hostip is None:
1779 logger.warning("No IPv4 address will be assigned")
1781 if hostip6 is None:
1782 logger.info("Looking up IPv6 addresses")
1783 hostips = interface_ips_v6(lp, linklocal=False)
1784 if hostips:
1785 hostip6 = hostips[0]
1786 if len(hostips) > 1:
1787 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1788 if hostip6 is None:
1789 logger.warning("No IPv6 address will be assigned")
1791 names.hostip = hostip
1792 names.hostip6 = hostip6
1794 if serverrole is None:
1795 serverrole = lp.get("server role")
1797 assert serverrole in ("domain controller", "member server", "standalone")
1799 if not os.path.exists(paths.private_dir):
1800 os.mkdir(paths.private_dir)
1801 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1802 os.mkdir(os.path.join(paths.private_dir, "tls"))
1804 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1806 schema = Schema(domainsid, invocationid=invocationid,
1807 schemadn=names.schemadn)
1809 if backend_type == "ldb":
1810 provision_backend = LDBBackend(backend_type, paths=paths,
1811 lp=lp, credentials=credentials,
1812 names=names, logger=logger)
1813 elif backend_type == "existing":
1814 provision_backend = ExistingBackend(backend_type, paths=paths,
1815 lp=lp, credentials=credentials,
1816 names=names, logger=logger,
1817 ldap_backend_forced_uri=ldap_backend_forced_uri)
1818 elif backend_type == "fedora-ds":
1819 provision_backend = FDSBackend(backend_type, paths=paths,
1820 lp=lp, credentials=credentials,
1821 names=names, logger=logger, domainsid=domainsid,
1822 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1823 slapd_path=slapd_path,
1824 ldap_backend_extra_port=ldap_backend_extra_port,
1825 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1826 setup_ds_path=setup_ds_path,
1827 ldap_backend_forced_uri=ldap_backend_forced_uri)
1828 elif backend_type == "openldap":
1829 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1830 lp=lp, credentials=credentials,
1831 names=names, logger=logger, domainsid=domainsid,
1832 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1833 slapd_path=slapd_path,
1834 ldap_backend_extra_port=ldap_backend_extra_port,
1835 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1836 nosync=nosync,
1837 ldap_backend_forced_uri=ldap_backend_forced_uri)
1838 else:
1839 raise ValueError("Unknown LDAP backend type selected")
1841 provision_backend.init()
1842 provision_backend.start()
1844 # only install a new shares config db if there is none
1845 if not os.path.exists(paths.shareconf):
1846 logger.info("Setting up share.ldb")
1847 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1848 lp=lp)
1849 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1851 logger.info("Setting up secrets.ldb")
1852 secrets_ldb = setup_secretsdb(paths,
1853 session_info=session_info,
1854 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1856 try:
1857 logger.info("Setting up the registry")
1858 setup_registry(paths.hklm, session_info,
1859 lp=lp)
1861 logger.info("Setting up the privileges database")
1862 setup_privileges(paths.privilege, session_info, lp=lp)
1864 logger.info("Setting up idmap db")
1865 idmap = setup_idmapdb(paths.idmapdb,
1866 session_info=session_info, lp=lp)
1868 setup_name_mappings(idmap, sid=str(domainsid),
1869 root_uid=root_uid, nobody_uid=nobody_uid,
1870 users_gid=users_gid, wheel_gid=wheel_gid)
1872 logger.info("Setting up SAM db")
1873 samdb = setup_samdb(paths.samdb, session_info,
1874 provision_backend, lp, names, logger=logger,
1875 serverrole=serverrole,
1876 schema=schema, fill=samdb_fill, am_rodc=am_rodc)
1878 if serverrole == "domain controller":
1879 if paths.netlogon is None:
1880 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1881 logger.info("Please either remove %s or see the template at %s" %
1882 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1883 assert paths.netlogon is not None
1885 if paths.sysvol is None:
1886 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1887 " are configuring a DC.")
1888 logger.info("Please either remove %s or see the template at %s" %
1889 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1890 assert paths.sysvol is not None
1892 if not os.path.isdir(paths.netlogon):
1893 os.makedirs(paths.netlogon, 0755)
1895 if samdb_fill == FILL_FULL:
1896 provision_fill(samdb, secrets_ldb, logger,
1897 names, paths, schema=schema, targetdir=targetdir,
1898 samdb_fill=samdb_fill, hostip=hostip, hostip6=hostip6, domainsid=domainsid,
1899 next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
1900 krbtgtpass=krbtgtpass, domainguid=domainguid,
1901 policyguid=policyguid, policyguid_dc=policyguid_dc,
1902 invocationid=invocationid, machinepass=machinepass,
1903 ntdsguid=ntdsguid, dns_backend=dns_backend, dnspass=dnspass,
1904 serverrole=serverrole, dom_for_fun_level=dom_for_fun_level,
1905 am_rodc=am_rodc, lp=lp)
1907 create_krb5_conf(paths.krb5conf,
1908 dnsdomain=names.dnsdomain, hostname=names.hostname,
1909 realm=names.realm)
1910 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1911 "generated at %s", paths.krb5conf)
1913 if serverrole == "domain controller":
1914 create_dns_update_list(lp, logger, paths)
1916 provision_backend.post_setup()
1917 provision_backend.shutdown()
1919 create_phpldapadmin_config(paths.phpldapadminconfig,
1920 ldapi_url)
1921 except Exception:
1922 secrets_ldb.transaction_cancel()
1923 raise
1925 # Now commit the secrets.ldb to disk
1926 secrets_ldb.transaction_commit()
1928 # the commit creates the dns.keytab, now chown it
1929 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1930 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1931 try:
1932 os.chmod(dns_keytab_path, 0640)
1933 os.chown(dns_keytab_path, -1, paths.bind_gid)
1934 except OSError:
1935 if not os.environ.has_key('SAMBA_SELFTEST'):
1936 logger.info("Failed to chown %s to bind gid %u",
1937 dns_keytab_path, paths.bind_gid)
1939 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1940 paths.phpldapadminconfig)
1942 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1943 logger.info("Server Role: %s" % serverrole)
1944 logger.info("Hostname: %s" % names.hostname)
1945 logger.info("NetBIOS Domain: %s" % names.domain)
1946 logger.info("DNS Domain: %s" % names.dnsdomain)
1947 logger.info("DOMAIN SID: %s" % str(domainsid))
1948 if samdb_fill == FILL_FULL:
1949 logger.info("Admin password: %s" % adminpass)
1950 if provision_backend.type is not "ldb":
1951 if provision_backend.credentials.get_bind_dn() is not None:
1952 logger.info("LDAP Backend Admin DN: %s" %
1953 provision_backend.credentials.get_bind_dn())
1954 else:
1955 logger.info("LDAP Admin User: %s" %
1956 provision_backend.credentials.get_username())
1958 logger.info("LDAP Admin Password: %s" %
1959 provision_backend.credentials.get_password())
1961 if provision_backend.slapd_command_escaped is not None:
1962 # now display slapd_command_file.txt to show how slapd must be
1963 # started next time
1964 logger.info("Use later the following commandline to start slapd, then Samba:")
1965 logger.info(provision_backend.slapd_command_escaped)
1966 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1967 provision_backend.ldapdir)
1969 result = ProvisionResult()
1970 result.domaindn = domaindn
1971 result.paths = paths
1972 result.names = names
1973 result.lp = lp
1974 result.samdb = samdb
1975 result.idmap = idmap
1976 return result
1979 def provision_become_dc(smbconf=None, targetdir=None,
1980 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1981 serverdn=None, domain=None, hostname=None, domainsid=None,
1982 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1983 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1984 dns_backend=None, root=None, nobody=None, users=None, wheel=None, backup=None,
1985 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1986 sitename=None, debuglevel=1):
1988 logger = logging.getLogger("provision")
1989 samba.set_debug_level(debuglevel)
1991 res = provision(logger, system_session(), None,
1992 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1993 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1994 configdn=configdn, serverdn=serverdn, domain=domain,
1995 hostname=hostname, hostip=None, domainsid=domainsid,
1996 machinepass=machinepass, serverrole="domain controller",
1997 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1998 res.lp.set("debuglevel", str(debuglevel))
1999 return res
2002 def create_phpldapadmin_config(path, ldapi_uri):
2003 """Create a PHP LDAP admin configuration file.
2005 :param path: Path to write the configuration to.
2007 setup_file(setup_path("phpldapadmin-config.php"), path,
2008 {"S4_LDAPI_URI": ldapi_uri})
2011 def create_dns_dir(logger, paths):
2012 """Write out a DNS zone file, from the info in the current database.
2014 :param logger: Logger object
2015 :param paths: paths object
2017 dns_dir = os.path.dirname(paths.dns)
2019 try:
2020 shutil.rmtree(dns_dir, True)
2021 except OSError:
2022 pass
2024 os.mkdir(dns_dir, 0770)
2026 if paths.bind_gid is not None:
2027 try:
2028 os.chown(dns_dir, -1, paths.bind_gid)
2029 # chmod needed to cope with umask
2030 os.chmod(dns_dir, 0770)
2031 except OSError:
2032 if not os.environ.has_key('SAMBA_SELFTEST'):
2033 logger.error("Failed to chown %s to bind gid %u" % (
2034 dns_dir, paths.bind_gid))
2037 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
2038 hostip, hostip6, hostname, realm, domainguid,
2039 ntdsguid):
2040 """Write out a DNS zone file, from the info in the current database.
2042 :param paths: paths object
2043 :param dnsdomain: DNS Domain name
2044 :param domaindn: DN of the Domain
2045 :param hostip: Local IPv4 IP
2046 :param hostip6: Local IPv6 IP
2047 :param hostname: Local hostname
2048 :param realm: Realm name
2049 :param domainguid: GUID of the domain.
2050 :param ntdsguid: GUID of the hosts nTDSDSA record.
2052 assert isinstance(domainguid, str)
2054 if hostip6 is not None:
2055 hostip6_base_line = " IN AAAA " + hostip6
2056 hostip6_host_line = hostname + " IN AAAA " + hostip6
2057 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
2058 else:
2059 hostip6_base_line = ""
2060 hostip6_host_line = ""
2061 gc_msdcs_ip6_line = ""
2063 if hostip is not None:
2064 hostip_base_line = " IN A " + hostip
2065 hostip_host_line = hostname + " IN A " + hostip
2066 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
2067 else:
2068 hostip_base_line = ""
2069 hostip_host_line = ""
2070 gc_msdcs_ip_line = ""
2072 # we need to freeze the zone while we update the contents
2073 if targetdir is None:
2074 rndc = ' '.join(lp.get("rndc command"))
2075 os.system(rndc + " freeze " + lp.get("realm"))
2077 setup_file(setup_path("provision.zone"), paths.dns, {
2078 "HOSTNAME": hostname,
2079 "DNSDOMAIN": dnsdomain,
2080 "REALM": realm,
2081 "HOSTIP_BASE_LINE": hostip_base_line,
2082 "HOSTIP_HOST_LINE": hostip_host_line,
2083 "DOMAINGUID": domainguid,
2084 "DATESTRING": time.strftime("%Y%m%d%H"),
2085 "DEFAULTSITE": DEFAULTSITE,
2086 "NTDSGUID": ntdsguid,
2087 "HOSTIP6_BASE_LINE": hostip6_base_line,
2088 "HOSTIP6_HOST_LINE": hostip6_host_line,
2089 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
2090 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
2093 if paths.bind_gid is not None:
2094 try:
2095 os.chown(paths.dns, -1, paths.bind_gid)
2096 # chmod needed to cope with umask
2097 os.chmod(paths.dns, 0664)
2098 except OSError:
2099 if not os.environ.has_key('SAMBA_SELFTEST'):
2100 logger.error("Failed to chown %s to bind gid %u" % (
2101 paths.dns, paths.bind_gid))
2103 if targetdir is None:
2104 os.system(rndc + " unfreeze " + lp.get("realm"))
2107 def create_dns_update_list(lp, logger, paths):
2108 """Write out a dns_update_list file"""
2109 # note that we use no variable substitution on this file
2110 # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
2111 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2112 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2115 def create_named_conf(paths, realm, dnsdomain, dns_backend):
2116 """Write out a file containing zone statements suitable for inclusion in a
2117 named.conf file (including GSS-TSIG configuration).
2119 :param paths: all paths
2120 :param realm: Realm name
2121 :param dnsdomain: DNS Domain name
2122 :param dns_backend: DNS backend type
2123 :param keytab_name: File name of DNS keytab file
2126 if dns_backend == "BIND9_FLATFILE":
2127 setup_file(setup_path("named.conf"), paths.namedconf, {
2128 "DNSDOMAIN": dnsdomain,
2129 "REALM": realm,
2130 "ZONE_FILE": paths.dns,
2131 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2132 "NAMED_CONF": paths.namedconf,
2133 "NAMED_CONF_UPDATE": paths.namedconf_update
2136 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2138 elif dns_backend == "BIND9_DLZ":
2139 dlz_module_path = os.path.join(samba.param.modules_dir(),
2140 "bind9/dlz_bind9.so")
2141 setup_file(setup_path("named.conf.dlz"), paths.namedconf, {
2142 "NAMED_CONF": paths.namedconf,
2143 "BIND9_DLZ_MODULE": dlz_module_path,
2148 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2149 keytab_name):
2150 """Write out a file containing zone statements suitable for inclusion in a
2151 named.conf file (including GSS-TSIG configuration).
2153 :param path: Path of the new named.conf file.
2154 :param realm: Realm name
2155 :param dnsdomain: DNS Domain name
2156 :param private_dir: Path to private directory
2157 :param keytab_name: File name of DNS keytab file
2159 setup_file(setup_path("named.txt"), path, {
2160 "DNSDOMAIN": dnsdomain,
2161 "DNSNAME" : dnsname,
2162 "REALM": realm,
2163 "DNS_KEYTAB": keytab_name,
2164 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2165 "PRIVATE_DIR": private_dir
2169 def create_krb5_conf(path, dnsdomain, hostname, realm):
2170 """Write out a file containing zone statements suitable for inclusion in a
2171 named.conf file (including GSS-TSIG configuration).
2173 :param path: Path of the new named.conf file.
2174 :param dnsdomain: DNS Domain name
2175 :param hostname: Local hostname
2176 :param realm: Realm name
2178 setup_file(setup_path("krb5.conf"), path, {
2179 "DNSDOMAIN": dnsdomain,
2180 "HOSTNAME": hostname,
2181 "REALM": realm,
2185 class ProvisioningError(Exception):
2186 """A generic provision error."""
2188 def __init__(self, value):
2189 self.value = value
2191 def __str__(self):
2192 return "ProvisioningError: " + self.value
2195 class InvalidNetbiosName(Exception):
2196 """A specified name was not a valid NetBIOS name."""
2197 def __init__(self, name):
2198 super(InvalidNetbiosName, self).__init__(
2199 "The name '%r' is not a valid NetBIOS name" % name)