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