s4-provision: Remove hard coded SD for CN=Sites container
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision / __init__.py
blob5aabd36c1a8e6b6a79bef4d6ff3f0fbd7ea31bcb
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 import samba.param
78 import samba.registry
79 from samba.schema import Schema
80 from samba.samdb import SamDB
82 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
83 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
84 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
85 DEFAULTSITE = "Default-First-Site-Name"
86 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
89 def setup_path(file):
90 """Return an absolute path to the provision tempate file specified by file"""
91 return os.path.join(samba.param.setup_dir(), file)
93 # Descriptors of naming contexts and other important objects
95 # "get_schema_descriptor" is located in "schema.py"
97 def get_config_descriptor(domain_sid):
98 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
99 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
100 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
101 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
102 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
103 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
104 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
105 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
106 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
107 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
108 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
109 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
110 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
111 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
112 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
113 sec = security.descriptor.from_sddl(sddl, domain_sid)
114 return ndr_pack(sec)
117 def get_domain_descriptor(domain_sid):
118 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
119 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
120 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
121 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
122 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
129 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
130 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
131 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
132 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
133 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
134 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
135 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
136 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
137 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
138 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
140 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
141 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
142 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
143 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
144 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
145 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
146 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
147 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
148 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
149 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
150 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
151 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
152 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
153 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
154 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
155 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
156 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
157 "(A;;RPRC;;;RU)" \
158 "(A;CI;LC;;;RU)" \
159 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
160 "(A;;RP;;;WD)" \
161 "(A;;RPLCLORC;;;ED)" \
162 "(A;;RPLCLORC;;;AU)" \
163 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
164 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
165 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
166 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
167 sec = security.descriptor.from_sddl(sddl, domain_sid)
168 return ndr_pack(sec)
171 class ProvisionPaths(object):
173 def __init__(self):
174 self.shareconf = None
175 self.hklm = None
176 self.hkcu = None
177 self.hkcr = None
178 self.hku = None
179 self.hkpd = None
180 self.hkpt = None
181 self.samdb = None
182 self.idmapdb = None
183 self.secrets = None
184 self.keytab = None
185 self.dns_keytab = None
186 self.dns = None
187 self.winsdb = None
188 self.private_dir = None
191 class ProvisionNames(object):
193 def __init__(self):
194 self.rootdn = None
195 self.domaindn = None
196 self.configdn = None
197 self.schemadn = None
198 self.ldapmanagerdn = None
199 self.dnsdomain = None
200 self.realm = None
201 self.netbiosname = None
202 self.domain = None
203 self.hostname = None
204 self.sitename = None
205 self.smbconf = None
207 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
208 """Get key provision parameters (realm, domain, ...) from a given provision
210 :param samdb: An LDB object connected to the sam.ldb file
211 :param secretsdb: An LDB object connected to the secrets.ldb file
212 :param idmapdb: An LDB object connected to the idmap.ldb file
213 :param paths: A list of path to provision object
214 :param smbconf: Path to the smb.conf file
215 :param lp: A LoadParm object
216 :return: A list of key provision parameters
218 names = ProvisionNames()
219 names.adminpass = None
221 # NT domain, kerberos realm, root dn, domain dn, domain dns name
222 names.domain = string.upper(lp.get("workgroup"))
223 names.realm = lp.get("realm")
224 basedn = "DC=" + names.realm.replace(".",",DC=")
225 names.dnsdomain = names.realm.lower()
226 names.realm = string.upper(names.realm)
227 # netbiosname
228 # Get the netbiosname first (could be obtained from smb.conf in theory)
229 res = secretsdb.search(expression="(flatname=%s)" %
230 names.domain,base="CN=Primary Domains",
231 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
232 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
234 names.smbconf = smbconf
236 # That's a bit simplistic but it's ok as long as we have only 3
237 # partitions
238 current = samdb.search(expression="(objectClass=*)",
239 base="", scope=ldb.SCOPE_BASE,
240 attrs=["defaultNamingContext", "schemaNamingContext",
241 "configurationNamingContext","rootDomainNamingContext"])
243 names.configdn = current[0]["configurationNamingContext"]
244 configdn = str(names.configdn)
245 names.schemadn = current[0]["schemaNamingContext"]
246 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
247 current[0]["defaultNamingContext"][0]))):
248 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
249 "is not the same ..." % (paths.samdb,
250 str(current[0]["defaultNamingContext"][0]),
251 paths.smbconf, basedn)))
253 names.domaindn=current[0]["defaultNamingContext"]
254 names.rootdn=current[0]["rootDomainNamingContext"]
255 # default site name
256 res3 = samdb.search(expression="(objectClass=site)",
257 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
258 names.sitename = str(res3[0]["cn"])
260 # dns hostname and server dn
261 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
262 base="OU=Domain Controllers,%s" % basedn,
263 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
264 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
266 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
267 attrs=[], base=configdn)
268 names.serverdn = server_res[0].dn
270 # invocation id/objectguid
271 res5 = samdb.search(expression="(objectClass=*)",
272 base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
273 attrs=["invocationID", "objectGUID"])
274 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
275 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
277 # domain guid/sid
278 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
279 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
280 "objectSid","msDS-Behavior-Version" ])
281 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
282 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
283 if res6[0].get("msDS-Behavior-Version") is None or \
284 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
285 names.domainlevel = DS_DOMAIN_FUNCTION_2000
286 else:
287 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
289 # policy guid
290 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
291 base="CN=Policies,CN=System," + basedn,
292 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
293 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
294 # dc policy guid
295 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
296 " Policy)",
297 base="CN=Policies,CN=System," + basedn,
298 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
299 if len(res8) == 1:
300 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
301 else:
302 names.policyid_dc = None
303 res9 = idmapdb.search(expression="(cn=%s)" %
304 (security.SID_BUILTIN_ADMINISTRATORS),
305 attrs=["xidNumber"])
306 if len(res9) == 1:
307 names.wheel_gid = res9[0]["xidNumber"]
308 else:
309 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
310 return names
312 def update_provision_usn(samdb, low, high, id, replace=False):
313 """Update the field provisionUSN in sam.ldb
315 This field is used to track range of USN modified by provision and
316 upgradeprovision.
317 This value is used afterward by next provision to figure out if
318 the field have been modified since last provision.
320 :param samdb: An LDB object connect to sam.ldb
321 :param low: The lowest USN modified by this upgrade
322 :param high: The highest USN modified by this upgrade
323 :param id: The invocation id of the samba's dc
324 :param replace: A boolean indicating if the range should replace any
325 existing one or appended (default)
328 tab = []
329 if not replace:
330 entry = samdb.search(expression="(&(dn=@PROVISION)(%s=*))" %
331 LAST_PROVISION_USN_ATTRIBUTE, base="",
332 scope=ldb.SCOPE_SUBTREE,
333 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
334 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
335 if not re.search(';', e):
336 e = "%s;%s" % (e, id)
337 tab.append(str(e))
339 tab.append("%s-%s;%s" % (low, high, id))
340 delta = ldb.Message()
341 delta.dn = ldb.Dn(samdb, "@PROVISION")
342 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
343 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
344 entry = samdb.search(expression="(&(dn=@PROVISION)(provisionnerID=*))",
345 base="", scope=ldb.SCOPE_SUBTREE,
346 attrs=["provisionnerID"])
347 if len(entry) == 0 or len(entry[0]) == 0:
348 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
349 samdb.modify(delta)
352 def set_provision_usn(samdb, low, high, id):
353 """Set the field provisionUSN in sam.ldb
354 This field is used to track range of USN modified by provision and
355 upgradeprovision.
356 This value is used afterward by next provision to figure out if
357 the field have been modified since last provision.
359 :param samdb: An LDB object connect to sam.ldb
360 :param low: The lowest USN modified by this upgrade
361 :param high: The highest USN modified by this upgrade
362 :param id: The invocationId of the provision"""
364 tab = []
365 tab.append("%s-%s;%s" % (low, high, id))
367 delta = ldb.Message()
368 delta.dn = ldb.Dn(samdb, "@PROVISION")
369 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
370 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
371 samdb.add(delta)
374 def get_max_usn(samdb,basedn):
375 """ This function return the biggest USN present in the provision
377 :param samdb: A LDB object pointing to the sam.ldb
378 :param basedn: A string containing the base DN of the provision
379 (ie. DC=foo, DC=bar)
380 :return: The biggest USN in the provision"""
382 res = samdb.search(expression="objectClass=*",base=basedn,
383 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
384 controls=["search_options:1:2",
385 "server_sort:1:1:uSNChanged",
386 "paged_results:1:1"])
387 return res[0]["uSNChanged"]
390 def get_last_provision_usn(sam):
391 """Get USNs ranges modified by a provision or an upgradeprovision
393 :param sam: An LDB object pointing to the sam.ldb
394 :return: a dictionnary which keys are invocation id and values are an array
395 of integer representing the different ranges
397 entry = sam.search(expression="(&(dn=@PROVISION)(%s=*))" %
398 LAST_PROVISION_USN_ATTRIBUTE,
399 base="", scope=ldb.SCOPE_SUBTREE,
400 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
401 if len(entry):
402 myids = []
403 range = {}
404 p = re.compile(r'-')
405 if entry[0].get("provisionnerID"):
406 for e in entry[0]["provisionnerID"]:
407 myids.append(str(e))
408 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
409 tab1 = str(r).split(';')
410 if len(tab1) == 2:
411 id = tab1[1]
412 else:
413 id = "default"
414 if (len(myids) > 0 and id not in myids):
415 continue
416 tab2 = p.split(tab1[0])
417 if range.get(id) == None:
418 range[id] = []
419 range[id].append(tab2[0])
420 range[id].append(tab2[1])
421 return range
422 else:
423 return None
426 class ProvisionResult(object):
428 def __init__(self):
429 self.paths = None
430 self.domaindn = None
431 self.lp = None
432 self.samdb = None
435 def check_install(lp, session_info, credentials):
436 """Check whether the current install seems ok.
438 :param lp: Loadparm context
439 :param session_info: Session information
440 :param credentials: Credentials
442 if lp.get("realm") == "":
443 raise Exception("Realm empty")
444 samdb = Ldb(lp.samdb_url(), session_info=session_info,
445 credentials=credentials, lp=lp)
446 if len(samdb.search("(cn=Administrator)")) != 1:
447 raise ProvisioningError("No administrator account found")
450 def findnss(nssfn, names):
451 """Find a user or group from a list of possibilities.
453 :param nssfn: NSS Function to try (should raise KeyError if not found)
454 :param names: Names to check.
455 :return: Value return by first names list.
457 for name in names:
458 try:
459 return nssfn(name)
460 except KeyError:
461 pass
462 raise KeyError("Unable to find user/group in %r" % names)
465 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
466 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
469 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
470 """Setup a ldb in the private dir.
472 :param ldb: LDB file to import data into
473 :param ldif_path: Path of the LDIF file to load
474 :param subst_vars: Optional variables to subsitute in LDIF.
475 :param nocontrols: Optional list of controls, can be None for no controls
477 assert isinstance(ldif_path, str)
478 data = read_and_sub_file(ldif_path, subst_vars)
479 ldb.add_ldif(data, controls)
482 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
483 """Modify a ldb in the private dir.
485 :param ldb: LDB object.
486 :param ldif_path: LDIF file path.
487 :param subst_vars: Optional dictionary with substitution variables.
489 data = read_and_sub_file(ldif_path, subst_vars)
490 ldb.modify_ldif(data, controls)
493 def setup_ldb(ldb, ldif_path, subst_vars):
494 """Import a LDIF a file into a LDB handle, optionally substituting
495 variables.
497 :note: Either all LDIF data will be added or none (using transactions).
499 :param ldb: LDB file to import into.
500 :param ldif_path: Path to the LDIF file.
501 :param subst_vars: Dictionary with substitution variables.
503 assert ldb is not None
504 ldb.transaction_start()
505 try:
506 setup_add_ldif(ldb, ldif_path, subst_vars)
507 except Exception:
508 ldb.transaction_cancel()
509 raise
510 else:
511 ldb.transaction_commit()
514 def provision_paths_from_lp(lp, dnsdomain):
515 """Set the default paths for provisioning.
517 :param lp: Loadparm context.
518 :param dnsdomain: DNS Domain name
520 paths = ProvisionPaths()
521 paths.private_dir = lp.get("private dir")
523 # This is stored without path prefix for the "privateKeytab" attribute in
524 # "secrets_dns.ldif".
525 paths.dns_keytab = "dns.keytab"
526 paths.keytab = "secrets.keytab"
528 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
529 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
530 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
531 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
532 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
533 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
534 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
535 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
536 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
537 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
538 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
539 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
540 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
541 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
542 paths.phpldapadminconfig = os.path.join(paths.private_dir,
543 "phpldapadmin-config.php")
544 paths.hklm = "hklm.ldb"
545 paths.hkcr = "hkcr.ldb"
546 paths.hkcu = "hkcu.ldb"
547 paths.hku = "hku.ldb"
548 paths.hkpd = "hkpd.ldb"
549 paths.hkpt = "hkpt.ldb"
550 paths.sysvol = lp.get("path", "sysvol")
551 paths.netlogon = lp.get("path", "netlogon")
552 paths.smbconf = lp.configfile
553 return paths
556 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
557 serverrole=None, rootdn=None, domaindn=None, configdn=None,
558 schemadn=None, serverdn=None, sitename=None):
559 """Guess configuration settings to use."""
561 if hostname is None:
562 hostname = socket.gethostname().split(".")[0]
564 netbiosname = lp.get("netbios name")
565 if netbiosname is None:
566 netbiosname = hostname
567 # remove forbidden chars
568 newnbname = ""
569 for x in netbiosname:
570 if x.isalnum() or x in VALID_NETBIOS_CHARS:
571 newnbname = "%s%c" % (newnbname, x)
572 # force the length to be <16
573 netbiosname = newnbname[0:15]
574 assert netbiosname is not None
575 netbiosname = netbiosname.upper()
576 if not valid_netbios_name(netbiosname):
577 raise InvalidNetbiosName(netbiosname)
579 if dnsdomain is None:
580 dnsdomain = lp.get("realm")
581 if dnsdomain is None or dnsdomain == "":
582 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
584 dnsdomain = dnsdomain.lower()
586 if serverrole is None:
587 serverrole = lp.get("server role")
588 if serverrole is None:
589 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
591 serverrole = serverrole.lower()
593 realm = dnsdomain.upper()
595 if lp.get("realm") == "":
596 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
598 if lp.get("realm").upper() != realm:
599 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))
601 if lp.get("server role").lower() != serverrole:
602 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))
604 if serverrole == "domain controller":
605 if domain is None:
606 # This will, for better or worse, default to 'WORKGROUP'
607 domain = lp.get("workgroup")
608 domain = domain.upper()
610 if lp.get("workgroup").upper() != domain:
611 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))
613 if domaindn is None:
614 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
616 if domain == netbiosname:
617 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
618 else:
619 domain = netbiosname
620 if domaindn is None:
621 domaindn = "DC=" + netbiosname
623 if not valid_netbios_name(domain):
624 raise InvalidNetbiosName(domain)
626 if hostname.upper() == realm:
627 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
628 if netbiosname.upper() == realm:
629 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
630 if domain == realm:
631 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
633 if rootdn is None:
634 rootdn = domaindn
636 if configdn is None:
637 configdn = "CN=Configuration," + rootdn
638 if schemadn is None:
639 schemadn = "CN=Schema," + configdn
641 if sitename is None:
642 sitename=DEFAULTSITE
644 names = ProvisionNames()
645 names.rootdn = rootdn
646 names.domaindn = domaindn
647 names.configdn = configdn
648 names.schemadn = schemadn
649 names.ldapmanagerdn = "CN=Manager," + rootdn
650 names.dnsdomain = dnsdomain
651 names.domain = domain
652 names.realm = realm
653 names.netbiosname = netbiosname
654 names.hostname = hostname
655 names.sitename = sitename
656 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
657 netbiosname, sitename, configdn)
659 return names
662 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
663 targetdir, sid_generator="internal", eadb=False, lp=None):
664 """Create a new smb.conf file based on a couple of basic settings.
666 assert smbconf is not None
667 if hostname is None:
668 hostname = socket.gethostname().split(".")[0]
669 netbiosname = hostname.upper()
670 # remove forbidden chars
671 newnbname = ""
672 for x in netbiosname:
673 if x.isalnum() or x in VALID_NETBIOS_CHARS:
674 newnbname = "%s%c" % (newnbname, x)
675 #force the length to be <16
676 netbiosname = newnbname[0:15]
677 else:
678 netbiosname = hostname.upper()
680 if serverrole is None:
681 serverrole = "standalone"
683 assert serverrole in ("domain controller", "member server", "standalone")
684 if serverrole == "domain controller":
685 smbconfsuffix = "dc"
686 elif serverrole == "member server":
687 smbconfsuffix = "member"
688 elif serverrole == "standalone":
689 smbconfsuffix = "standalone"
691 if sid_generator is None:
692 sid_generator = "internal"
694 assert domain is not None
695 domain = domain.upper()
697 assert realm is not None
698 realm = realm.upper()
700 if lp is None:
701 lp = samba.param.LoadParm()
702 #Load non-existant file
703 if os.path.exists(smbconf):
704 lp.load(smbconf)
705 if eadb and not lp.get("posix:eadb"):
706 if targetdir is not None:
707 privdir = os.path.join(targetdir, "private")
708 else:
709 privdir = lp.get("private dir")
710 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
712 if targetdir is not None:
713 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
714 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
716 lp.set("lock dir", os.path.abspath(targetdir))
717 else:
718 privatedir_line = ""
719 lockdir_line = ""
721 sysvol = os.path.join(lp.get("lock dir"), "sysvol")
722 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
724 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
725 smbconf, {
726 "NETBIOS_NAME": netbiosname,
727 "DOMAIN": domain,
728 "REALM": realm,
729 "SERVERROLE": serverrole,
730 "NETLOGONPATH": netlogon,
731 "SYSVOLPATH": sysvol,
732 "PRIVATEDIR_LINE": privatedir_line,
733 "LOCKDIR_LINE": lockdir_line
736 # reload the smb.conf
737 lp.load(smbconf)
739 # and dump it without any values that are the default
740 # this ensures that any smb.conf parameters that were set
741 # on the provision/join command line are set in the resulting smb.conf
742 f = open(smbconf, mode='w')
743 lp.dump(f, False)
744 f.close()
748 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
749 users_gid, wheel_gid):
750 """setup reasonable name mappings for sam names to unix names.
752 :param samdb: SamDB object.
753 :param idmap: IDmap db object.
754 :param sid: The domain sid.
755 :param domaindn: The domain DN.
756 :param root_uid: uid of the UNIX root user.
757 :param nobody_uid: uid of the UNIX nobody user.
758 :param users_gid: gid of the UNIX users group.
759 :param wheel_gid: gid of the UNIX wheel group.
761 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
762 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
764 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
765 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
768 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
769 provision_backend, names, schema, serverrole,
770 erase=False):
771 """Setup the partitions for the SAM database.
773 Alternatively, provision() may call this, and then populate the database.
775 :note: This will wipe the Sam Database!
777 :note: This function always removes the local SAM LDB file. The erase
778 parameter controls whether to erase the existing data, which
779 may not be stored locally but in LDAP.
782 assert session_info is not None
784 # We use options=["modules:"] to stop the modules loading - we
785 # just want to wipe and re-initialise the database, not start it up
787 try:
788 os.unlink(samdb_path)
789 except OSError:
790 pass
792 samdb = Ldb(url=samdb_path, session_info=session_info,
793 lp=lp, options=["modules:"])
795 ldap_backend_line = "# No LDAP backend"
796 if provision_backend.type is not "ldb":
797 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
799 samdb.transaction_start()
800 try:
801 logger.info("Setting up sam.ldb partitions and settings")
802 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
803 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
804 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
805 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
806 "LDAP_BACKEND_LINE": ldap_backend_line,
810 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
811 "BACKEND_TYPE": provision_backend.type,
812 "SERVER_ROLE": serverrole
815 logger.info("Setting up sam.ldb rootDSE")
816 setup_samdb_rootdse(samdb, names)
817 except Exception:
818 samdb.transaction_cancel()
819 raise
820 else:
821 samdb.transaction_commit()
824 def secretsdb_self_join(secretsdb, domain,
825 netbiosname, machinepass, domainsid=None,
826 realm=None, dnsdomain=None,
827 keytab_path=None,
828 key_version_number=1,
829 secure_channel_type=SEC_CHAN_WKSTA):
830 """Add domain join-specific bits to a secrets database.
832 :param secretsdb: Ldb Handle to the secrets database
833 :param machinepass: Machine password
835 attrs = ["whenChanged",
836 "secret",
837 "priorSecret",
838 "priorChanged",
839 "krb5Keytab",
840 "privateKeytab"]
842 if realm is not None:
843 if dnsdomain is None:
844 dnsdomain = realm.lower()
845 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
846 else:
847 dnsname = None
848 shortname = netbiosname.lower()
850 # We don't need to set msg["flatname"] here, because rdn_name will handle
851 # it, and it causes problems for modifies anyway
852 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
853 msg["secureChannelType"] = [str(secure_channel_type)]
854 msg["objectClass"] = ["top", "primaryDomain"]
855 if dnsname is not None:
856 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
857 msg["realm"] = [realm]
858 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
859 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
860 msg["privateKeytab"] = ["secrets.keytab"]
862 msg["secret"] = [machinepass]
863 msg["samAccountName"] = ["%s$" % netbiosname]
864 msg["secureChannelType"] = [str(secure_channel_type)]
865 if domainsid is not None:
866 msg["objectSid"] = [ndr_pack(domainsid)]
868 # This complex expression tries to ensure that we don't have more
869 # than one record for this SID, realm or netbios domain at a time,
870 # but we don't delete the old record that we are about to modify,
871 # because that would delete the keytab and previous password.
872 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
873 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
874 scope=ldb.SCOPE_ONELEVEL)
876 for del_msg in res:
877 secretsdb.delete(del_msg.dn)
879 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
881 if len(res) == 1:
882 msg["priorSecret"] = [res[0]["secret"][0]]
883 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
885 try:
886 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
887 except KeyError:
888 pass
890 try:
891 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
892 except KeyError:
893 pass
895 for el in msg:
896 if el != 'dn':
897 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
898 secretsdb.modify(msg)
899 secretsdb.rename(res[0].dn, msg.dn)
900 else:
901 spn = [ 'HOST/%s' % shortname ]
902 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
903 # we are a domain controller then we add servicePrincipalName
904 # entries for the keytab code to update.
905 spn.extend([ 'HOST/%s' % dnsname ])
906 msg["servicePrincipalName"] = spn
908 secretsdb.add(msg)
911 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
912 dnsdomain, dns_keytab_path, dnspass):
913 """Add DNS specific bits to a secrets database.
915 :param secretsdb: Ldb Handle to the secrets database
916 :param machinepass: Machine password
918 try:
919 os.unlink(os.path.join(private_dir, dns_keytab_path))
920 except OSError:
921 pass
923 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
924 "REALM": realm,
925 "DNSDOMAIN": dnsdomain,
926 "DNS_KEYTAB": dns_keytab_path,
927 "DNSPASS_B64": b64encode(dnspass),
928 "HOSTNAME": names.hostname,
929 "DNSNAME" : '%s.%s' % (
930 names.netbiosname.lower(), names.dnsdomain.lower())
934 def setup_secretsdb(paths, session_info, backend_credentials, lp):
935 """Setup the secrets database.
937 :note: This function does not handle exceptions and transaction on purpose,
938 it's up to the caller to do this job.
940 :param path: Path to the secrets database.
941 :param session_info: Session info.
942 :param credentials: Credentials
943 :param lp: Loadparm context
944 :return: LDB handle for the created secrets database
946 if os.path.exists(paths.secrets):
947 os.unlink(paths.secrets)
949 keytab_path = os.path.join(paths.private_dir, paths.keytab)
950 if os.path.exists(keytab_path):
951 os.unlink(keytab_path)
953 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
954 if os.path.exists(dns_keytab_path):
955 os.unlink(dns_keytab_path)
957 path = paths.secrets
959 secrets_ldb = Ldb(path, session_info=session_info,
960 lp=lp)
961 secrets_ldb.erase()
962 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
963 secrets_ldb = Ldb(path, session_info=session_info,
964 lp=lp)
965 secrets_ldb.transaction_start()
966 try:
967 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
969 if (backend_credentials is not None and
970 backend_credentials.authentication_requested()):
971 if backend_credentials.get_bind_dn() is not None:
972 setup_add_ldif(secrets_ldb,
973 setup_path("secrets_simple_ldap.ldif"), {
974 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
975 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
977 else:
978 setup_add_ldif(secrets_ldb,
979 setup_path("secrets_sasl_ldap.ldif"), {
980 "LDAPADMINUSER": backend_credentials.get_username(),
981 "LDAPADMINREALM": backend_credentials.get_realm(),
982 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
985 return secrets_ldb
986 except Exception:
987 secrets_ldb.transaction_cancel()
988 raise
991 def setup_privileges(path, session_info, lp):
992 """Setup the privileges database.
994 :param path: Path to the privileges database.
995 :param session_info: Session info.
996 :param credentials: Credentials
997 :param lp: Loadparm context
998 :return: LDB handle for the created secrets database
1000 if os.path.exists(path):
1001 os.unlink(path)
1002 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1003 privilege_ldb.erase()
1004 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1007 def setup_registry(path, session_info, lp):
1008 """Setup the registry.
1010 :param path: Path to the registry database
1011 :param session_info: Session information
1012 :param credentials: Credentials
1013 :param lp: Loadparm context
1015 reg = samba.registry.Registry()
1016 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1017 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1018 provision_reg = setup_path("provision.reg")
1019 assert os.path.exists(provision_reg)
1020 reg.diff_apply(provision_reg)
1023 def setup_idmapdb(path, session_info, lp):
1024 """Setup the idmap database.
1026 :param path: path to the idmap database
1027 :param session_info: Session information
1028 :param credentials: Credentials
1029 :param lp: Loadparm context
1031 if os.path.exists(path):
1032 os.unlink(path)
1034 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1035 idmap_ldb.erase()
1036 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1037 return idmap_ldb
1040 def setup_samdb_rootdse(samdb, names):
1041 """Setup the SamDB rootdse.
1043 :param samdb: Sam Database handle
1045 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1046 "SCHEMADN": names.schemadn,
1047 "DOMAINDN": names.domaindn,
1048 "ROOTDN": names.rootdn,
1049 "CONFIGDN": names.configdn,
1050 "SERVERDN": names.serverdn,
1054 def setup_self_join(samdb, names, machinepass, dnspass,
1055 domainsid, next_rid, invocationid,
1056 policyguid, policyguid_dc, domainControllerFunctionality,
1057 ntdsguid):
1058 """Join a host to its own domain."""
1059 assert isinstance(invocationid, str)
1060 if ntdsguid is not None:
1061 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1062 else:
1063 ntdsguid_line = ""
1064 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1065 "CONFIGDN": names.configdn,
1066 "SCHEMADN": names.schemadn,
1067 "DOMAINDN": names.domaindn,
1068 "SERVERDN": names.serverdn,
1069 "INVOCATIONID": invocationid,
1070 "NETBIOSNAME": names.netbiosname,
1071 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1072 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1073 "DOMAINSID": str(domainsid),
1074 "DCRID": str(next_rid),
1075 "SAMBA_VERSION_STRING": version,
1076 "NTDSGUID": ntdsguid_line,
1077 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1078 domainControllerFunctionality)})
1080 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1081 "POLICYGUID": policyguid,
1082 "POLICYGUID_DC": policyguid_dc,
1083 "DNSDOMAIN": names.dnsdomain,
1084 "DOMAINDN": names.domaindn})
1086 # add the NTDSGUID based SPNs
1087 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1088 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1089 expression="", scope=ldb.SCOPE_BASE)
1090 assert isinstance(names.ntdsguid, str)
1092 # Setup fSMORoleOwner entries to point at the newly created DC entry
1093 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1094 "DOMAINDN": names.domaindn,
1095 "CONFIGDN": names.configdn,
1096 "SCHEMADN": names.schemadn,
1097 "DEFAULTSITE": names.sitename,
1098 "SERVERDN": names.serverdn,
1099 "NETBIOSNAME": names.netbiosname,
1100 "RIDALLOCATIONSTART": str(next_rid + 100),
1101 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1104 # This is partially Samba4 specific and should be replaced by the correct
1105 # DNS AD-style setup
1106 setup_add_ldif(samdb, setup_path("provision_dns_add.ldif"), {
1107 "DNSDOMAIN": names.dnsdomain,
1108 "DOMAINDN": names.domaindn,
1109 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1110 "HOSTNAME" : names.hostname,
1111 "DNSNAME" : '%s.%s' % (
1112 names.netbiosname.lower(), names.dnsdomain.lower())
1116 def getpolicypath(sysvolpath, dnsdomain, guid):
1117 """Return the physical path of policy given its guid.
1119 :param sysvolpath: Path to the sysvol folder
1120 :param dnsdomain: DNS name of the AD domain
1121 :param guid: The GUID of the policy
1122 :return: A string with the complete path to the policy folder
1125 if guid[0] != "{":
1126 guid = "{%s}" % guid
1127 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1128 return policy_path
1131 def create_gpo_struct(policy_path):
1132 if not os.path.exists(policy_path):
1133 os.makedirs(policy_path, 0775)
1134 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1135 "[General]\r\nVersion=0")
1136 p = os.path.join(policy_path, "MACHINE")
1137 if not os.path.exists(p):
1138 os.makedirs(p, 0775)
1139 p = os.path.join(policy_path, "USER")
1140 if not os.path.exists(p):
1141 os.makedirs(p, 0775)
1144 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1145 """Create the default GPO for a domain
1147 :param sysvolpath: Physical path for the sysvol folder
1148 :param dnsdomain: DNS domain name of the AD domain
1149 :param policyguid: GUID of the default domain policy
1150 :param policyguid_dc: GUID of the default domain controler policy
1152 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1153 create_gpo_struct(policy_path)
1155 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1156 create_gpo_struct(policy_path)
1159 def setup_samdb(path, session_info, provision_backend, lp, names,
1160 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1161 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1162 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1163 next_rid=1000):
1164 """Setup a complete SAM Database.
1166 :note: This will wipe the main SAM database file!
1169 # Provision does not make much sense values larger than 1000000000
1170 # as the upper range of the rIDAvailablePool is 1073741823 and
1171 # we don't want to create a domain that cannot allocate rids.
1172 if next_rid < 1000 or next_rid > 1000000000:
1173 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1174 error += "the valid range is %u-%u. The default is %u." % (
1175 1000, 1000000000, 1000)
1176 raise ProvisioningError(error)
1178 # ATTENTION: Do NOT change these default values without discussion with the
1179 # team and/or release manager. They have a big impact on the whole program!
1180 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1182 if dom_for_fun_level is None:
1183 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1185 if dom_for_fun_level > domainControllerFunctionality:
1186 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!")
1188 domainFunctionality = dom_for_fun_level
1189 forestFunctionality = dom_for_fun_level
1191 # Also wipes the database
1192 setup_samdb_partitions(path, logger=logger, lp=lp,
1193 provision_backend=provision_backend, session_info=session_info,
1194 names=names, serverrole=serverrole, schema=schema)
1196 if schema is None:
1197 schema = Schema(domainsid, schemadn=names.schemadn)
1199 # Load the database, but don's load the global schema and don't connect
1200 # quite yet
1201 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1202 credentials=provision_backend.credentials, lp=lp,
1203 global_schema=False, am_rodc=am_rodc)
1205 logger.info("Pre-loading the Samba 4 and AD schema")
1207 # Load the schema from the one we computed earlier
1208 samdb.set_schema(schema)
1210 # Set the NTDS settings DN manually - in order to have it already around
1211 # before the provisioned tree exists and we connect
1212 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1214 # And now we can connect to the DB - the schema won't be loaded from the
1215 # DB
1216 samdb.connect(path)
1218 if fill == FILL_DRS:
1219 return samdb
1221 samdb.transaction_start()
1222 try:
1223 # Set the domain functionality levels onto the database.
1224 # Various module (the password_hash module in particular) need
1225 # to know what level of AD we are emulating.
1227 # These will be fixed into the database via the database
1228 # modifictions below, but we need them set from the start.
1229 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1230 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1231 samdb.set_opaque_integer("domainControllerFunctionality",
1232 domainControllerFunctionality)
1234 samdb.set_domain_sid(str(domainsid))
1235 samdb.set_invocation_id(invocationid)
1237 logger.info("Adding DomainDN: %s" % names.domaindn)
1239 # impersonate domain admin
1240 admin_session_info = admin_session(lp, str(domainsid))
1241 samdb.set_session_info(admin_session_info)
1242 if domainguid is not None:
1243 domainguid_line = "objectGUID: %s\n-" % domainguid
1244 else:
1245 domainguid_line = ""
1247 descr = b64encode(get_domain_descriptor(domainsid))
1248 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1249 "DOMAINDN": names.domaindn,
1250 "DOMAINSID": str(domainsid),
1251 "DESCRIPTOR": descr,
1252 "DOMAINGUID": domainguid_line
1255 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1256 "DOMAINDN": names.domaindn,
1257 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1258 "NEXTRID": str(next_rid),
1259 "DEFAULTSITE": names.sitename,
1260 "CONFIGDN": names.configdn,
1261 "POLICYGUID": policyguid,
1262 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1263 "SAMBA_VERSION_STRING": version
1266 logger.info("Adding configuration container")
1267 descr = b64encode(get_config_descriptor(domainsid))
1268 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1269 "CONFIGDN": names.configdn,
1270 "DESCRIPTOR": descr,
1273 # Now register this container in the root of the forest
1274 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1275 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1276 "subRefs")
1278 # The LDIF here was created when the Schema object was constructed
1279 logger.info("Setting up sam.ldb schema")
1280 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1281 samdb.modify_ldif(schema.schema_dn_modify)
1282 samdb.write_prefixes_from_schema()
1283 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1284 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1285 {"SCHEMADN": names.schemadn})
1287 logger.info("Reopening sam.ldb with new schema")
1288 except Exception:
1289 samdb.transaction_cancel()
1290 raise
1291 else:
1292 samdb.transaction_commit()
1294 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1295 credentials=provision_backend.credentials, lp=lp,
1296 global_schema=False, am_rodc=am_rodc)
1298 # Set the NTDS settings DN manually - in order to have it already around
1299 # before the provisioned tree exists and we connect
1300 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1301 samdb.connect(path)
1303 samdb.transaction_start()
1304 try:
1305 samdb.invocation_id = invocationid
1307 logger.info("Setting up sam.ldb configuration data")
1308 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1309 "CONFIGDN": names.configdn,
1310 "NETBIOSNAME": names.netbiosname,
1311 "DEFAULTSITE": names.sitename,
1312 "DNSDOMAIN": names.dnsdomain,
1313 "DOMAIN": names.domain,
1314 "SCHEMADN": names.schemadn,
1315 "DOMAINDN": names.domaindn,
1316 "SERVERDN": names.serverdn,
1317 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1318 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1321 logger.info("Setting up display specifiers")
1322 display_specifiers_ldif = read_ms_ldif(
1323 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1324 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1325 {"CONFIGDN": names.configdn})
1326 check_all_substituted(display_specifiers_ldif)
1327 samdb.add_ldif(display_specifiers_ldif)
1329 logger.info("Adding users container")
1330 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1331 "DOMAINDN": names.domaindn})
1332 logger.info("Modifying users container")
1333 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1334 "DOMAINDN": names.domaindn})
1335 logger.info("Adding computers container")
1336 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1337 "DOMAINDN": names.domaindn})
1338 logger.info("Modifying computers container")
1339 setup_modify_ldif(samdb,
1340 setup_path("provision_computers_modify.ldif"), {
1341 "DOMAINDN": names.domaindn})
1342 logger.info("Setting up sam.ldb data")
1343 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1344 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1345 "DOMAINDN": names.domaindn,
1346 "NETBIOSNAME": names.netbiosname,
1347 "DEFAULTSITE": names.sitename,
1348 "CONFIGDN": names.configdn,
1349 "SERVERDN": names.serverdn,
1350 "RIDAVAILABLESTART": str(next_rid + 600),
1351 "POLICYGUID_DC": policyguid_dc
1354 setup_modify_ldif(samdb,
1355 setup_path("provision_basedn_references.ldif"), {
1356 "DOMAINDN": names.domaindn})
1358 setup_modify_ldif(samdb,
1359 setup_path("provision_configuration_references.ldif"), {
1360 "CONFIGDN": names.configdn,
1361 "SCHEMADN": names.schemadn})
1362 if fill == FILL_FULL:
1363 logger.info("Setting up sam.ldb users and groups")
1364 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1365 "DOMAINDN": names.domaindn,
1366 "DOMAINSID": str(domainsid),
1367 "CONFIGDN": names.configdn,
1368 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1369 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1372 logger.info("Setting up self join")
1373 setup_self_join(samdb, names=names, invocationid=invocationid,
1374 dnspass=dnspass,
1375 machinepass=machinepass,
1376 domainsid=domainsid,
1377 next_rid=next_rid,
1378 policyguid=policyguid,
1379 policyguid_dc=policyguid_dc,
1380 domainControllerFunctionality=domainControllerFunctionality,
1381 ntdsguid=ntdsguid)
1383 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1384 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1385 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1386 assert isinstance(names.ntdsguid, str)
1387 except Exception:
1388 samdb.transaction_cancel()
1389 raise
1390 else:
1391 samdb.transaction_commit()
1392 return samdb
1395 FILL_FULL = "FULL"
1396 FILL_NT4SYNC = "NT4SYNC"
1397 FILL_DRS = "DRS"
1398 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1399 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)"
1402 def set_dir_acl(path, acl, lp, domsid):
1403 setntacl(lp, path, acl, domsid)
1404 for root, dirs, files in os.walk(path, topdown=False):
1405 for name in files:
1406 setntacl(lp, os.path.join(root, name), acl, domsid)
1407 for name in dirs:
1408 setntacl(lp, os.path.join(root, name), acl, domsid)
1411 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1412 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1413 folders beneath.
1415 :param sysvol: Physical path for the sysvol folder
1416 :param dnsdomain: The DNS name of the domain
1417 :param domainsid: The SID of the domain
1418 :param domaindn: The DN of the domain (ie. DC=...)
1419 :param samdb: An LDB object on the SAM db
1420 :param lp: an LP object
1423 # Set ACL for GPO root folder
1424 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1425 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1427 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1428 attrs=["cn", "nTSecurityDescriptor"],
1429 expression="", scope=ldb.SCOPE_ONELEVEL)
1431 for policy in res:
1432 acl = ndr_unpack(security.descriptor,
1433 str(policy["nTSecurityDescriptor"])).as_sddl()
1434 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1435 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1436 str(domainsid))
1439 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1440 lp):
1441 """Set the ACL for the sysvol share and the subfolders
1443 :param samdb: An LDB object on the SAM db
1444 :param netlogon: Physical path for the netlogon folder
1445 :param sysvol: Physical path for the sysvol folder
1446 :param gid: The GID of the "Domain adminstrators" group
1447 :param domainsid: The SID of the domain
1448 :param dnsdomain: The DNS name of the domain
1449 :param domaindn: The DN of the domain (ie. DC=...)
1452 try:
1453 os.chown(sysvol, -1, gid)
1454 except OSError:
1455 canchown = False
1456 else:
1457 canchown = True
1459 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1460 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1461 for root, dirs, files in os.walk(sysvol, topdown=False):
1462 for name in files:
1463 if canchown:
1464 os.chown(os.path.join(root, name), -1, gid)
1465 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1466 for name in dirs:
1467 if canchown:
1468 os.chown(os.path.join(root, name), -1, gid)
1469 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1471 # Set acls on Policy folder and policies folders
1472 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1475 def interface_ips_v4(lp):
1476 '''return only IPv4 IPs'''
1477 ips = samba.interface_ips(lp, False)
1478 ret = []
1479 for i in ips:
1480 if i.find(':') == -1:
1481 ret.append(i)
1482 return ret
1484 def interface_ips_v6(lp, linklocal=False):
1485 '''return only IPv6 IPs'''
1486 ips = samba.interface_ips(lp, False)
1487 ret = []
1488 for i in ips:
1489 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1490 ret.append(i)
1491 return ret
1494 def provision(logger, session_info, credentials, smbconf=None,
1495 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1496 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1497 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1498 next_rid=1000, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1499 domainguid=None, policyguid=None, policyguid_dc=None,
1500 invocationid=None, machinepass=None, ntdsguid=None, dnspass=None,
1501 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1502 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1503 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1504 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1505 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1506 lp=None):
1507 """Provision samba4
1509 :note: caution, this wipes all existing data!
1512 if domainsid is None:
1513 domainsid = security.random_sid()
1514 else:
1515 domainsid = security.dom_sid(domainsid)
1517 # create/adapt the group policy GUIDs
1518 # Default GUID for default policy are described at
1519 # "How Core Group Policy Works"
1520 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1521 if policyguid is None:
1522 policyguid = DEFAULT_POLICY_GUID
1523 policyguid = policyguid.upper()
1524 if policyguid_dc is None:
1525 policyguid_dc = DEFAULT_DC_POLICY_GUID
1526 policyguid_dc = policyguid_dc.upper()
1528 if adminpass is None:
1529 adminpass = samba.generate_random_password(12, 32)
1530 if krbtgtpass is None:
1531 krbtgtpass = samba.generate_random_password(128, 255)
1532 if machinepass is None:
1533 machinepass = samba.generate_random_password(128, 255)
1534 if dnspass is None:
1535 dnspass = samba.generate_random_password(128, 255)
1536 if ldapadminpass is None:
1537 # Make a new, random password between Samba and it's LDAP server
1538 ldapadminpass=samba.generate_random_password(128, 255)
1540 if backend_type is None:
1541 backend_type = "ldb"
1543 sid_generator = "internal"
1544 if backend_type == "fedora-ds":
1545 sid_generator = "backend"
1547 root_uid = findnss_uid([root or "root"])
1548 nobody_uid = findnss_uid([nobody or "nobody"])
1549 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1550 if wheel is None:
1551 wheel_gid = findnss_gid(["wheel", "adm"])
1552 else:
1553 wheel_gid = findnss_gid([wheel])
1554 try:
1555 bind_gid = findnss_gid(["bind", "named"])
1556 except KeyError:
1557 bind_gid = None
1559 if targetdir is not None:
1560 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1561 elif smbconf is None:
1562 smbconf = samba.param.default_path()
1563 if not os.path.exists(os.path.dirname(smbconf)):
1564 os.makedirs(os.path.dirname(smbconf))
1566 # only install a new smb.conf if there isn't one there already
1567 if os.path.exists(smbconf):
1568 # if Samba Team members can't figure out the weird errors
1569 # loading an empty smb.conf gives, then we need to be smarter.
1570 # Pretend it just didn't exist --abartlet
1571 data = open(smbconf, 'r').read()
1572 data = data.lstrip()
1573 if data is None or data == "":
1574 make_smbconf(smbconf, hostname, domain, realm,
1575 serverrole, targetdir, sid_generator, useeadb,
1576 lp=lp)
1577 else:
1578 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1579 targetdir, sid_generator, useeadb, lp=lp)
1581 if lp is None:
1582 lp = samba.param.LoadParm()
1583 lp.load(smbconf)
1584 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1585 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1586 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1587 sitename=sitename)
1588 paths = provision_paths_from_lp(lp, names.dnsdomain)
1590 paths.bind_gid = bind_gid
1592 if hostip is None:
1593 logger.info("Looking up IPv4 addresses")
1594 hostips = interface_ips_v4(lp)
1595 if len(hostips) > 0:
1596 hostip = hostips[0]
1597 if len(hostips) > 1:
1598 logger.warning("More than one IPv4 address found. Using %s",
1599 hostip)
1600 if hostip == "127.0.0.1":
1601 hostip = None
1602 if hostip is None:
1603 logger.warning("No IPv4 address will be assigned")
1605 if hostip6 is None:
1606 logger.info("Looking up IPv6 addresses")
1607 hostips = interface_ips_v6(lp, linklocal=False)
1608 if hostips:
1609 hostip6 = hostips[0]
1610 if len(hostips) > 1:
1611 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1612 if hostip6 is None:
1613 logger.warning("No IPv6 address will be assigned")
1615 if serverrole is None:
1616 serverrole = lp.get("server role")
1618 assert serverrole in ("domain controller", "member server", "standalone")
1619 if invocationid is None:
1620 invocationid = str(uuid.uuid4())
1622 if not os.path.exists(paths.private_dir):
1623 os.mkdir(paths.private_dir)
1624 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1625 os.mkdir(os.path.join(paths.private_dir, "tls"))
1627 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1629 schema = Schema(domainsid, invocationid=invocationid,
1630 schemadn=names.schemadn)
1632 if backend_type == "ldb":
1633 provision_backend = LDBBackend(backend_type, paths=paths,
1634 lp=lp, credentials=credentials,
1635 names=names, logger=logger)
1636 elif backend_type == "existing":
1637 provision_backend = ExistingBackend(backend_type, paths=paths,
1638 lp=lp, credentials=credentials,
1639 names=names, logger=logger,
1640 ldap_backend_forced_uri=ldap_backend_forced_uri)
1641 elif backend_type == "fedora-ds":
1642 provision_backend = FDSBackend(backend_type, paths=paths,
1643 lp=lp, credentials=credentials,
1644 names=names, logger=logger, domainsid=domainsid,
1645 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1646 slapd_path=slapd_path,
1647 ldap_backend_extra_port=ldap_backend_extra_port,
1648 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1649 setup_ds_path=setup_ds_path,
1650 ldap_backend_forced_uri=ldap_backend_forced_uri)
1651 elif backend_type == "openldap":
1652 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1653 lp=lp, credentials=credentials,
1654 names=names, logger=logger, domainsid=domainsid,
1655 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1656 slapd_path=slapd_path,
1657 ldap_backend_extra_port=ldap_backend_extra_port,
1658 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1659 nosync=nosync,
1660 ldap_backend_forced_uri=ldap_backend_forced_uri)
1661 else:
1662 raise ValueError("Unknown LDAP backend type selected")
1664 provision_backend.init()
1665 provision_backend.start()
1667 # only install a new shares config db if there is none
1668 if not os.path.exists(paths.shareconf):
1669 logger.info("Setting up share.ldb")
1670 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1671 lp=lp)
1672 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1674 logger.info("Setting up secrets.ldb")
1675 secrets_ldb = setup_secretsdb(paths,
1676 session_info=session_info,
1677 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1679 try:
1680 logger.info("Setting up the registry")
1681 setup_registry(paths.hklm, session_info,
1682 lp=lp)
1684 logger.info("Setting up the privileges database")
1685 setup_privileges(paths.privilege, session_info, lp=lp)
1687 logger.info("Setting up idmap db")
1688 idmap = setup_idmapdb(paths.idmapdb,
1689 session_info=session_info, lp=lp)
1691 logger.info("Setting up SAM db")
1692 samdb = setup_samdb(paths.samdb, session_info,
1693 provision_backend, lp, names, logger=logger,
1694 domainsid=domainsid, schema=schema, domainguid=domainguid,
1695 policyguid=policyguid, policyguid_dc=policyguid_dc,
1696 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1697 invocationid=invocationid, machinepass=machinepass,
1698 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1699 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1700 next_rid=next_rid)
1702 if serverrole == "domain controller":
1703 if paths.netlogon is None:
1704 logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1705 logger.info("Please either remove %s or see the template at %s" %
1706 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1707 assert paths.netlogon is not None
1709 if paths.sysvol is None:
1710 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1711 " are configuring a DC.")
1712 logger.info("Please either remove %s or see the template at %s" %
1713 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1714 assert paths.sysvol is not None
1716 if not os.path.isdir(paths.netlogon):
1717 os.makedirs(paths.netlogon, 0755)
1719 if samdb_fill == FILL_FULL:
1720 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1721 root_uid=root_uid, nobody_uid=nobody_uid,
1722 users_gid=users_gid, wheel_gid=wheel_gid)
1724 if serverrole == "domain controller":
1725 # Set up group policies (domain policy and domain controller
1726 # policy)
1727 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1728 policyguid_dc)
1729 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1730 domainsid, names.dnsdomain, names.domaindn, lp)
1732 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1733 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1735 secretsdb_self_join(secrets_ldb, domain=names.domain,
1736 realm=names.realm, dnsdomain=names.dnsdomain,
1737 netbiosname=names.netbiosname, domainsid=domainsid,
1738 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1740 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1741 # In future, this might be determined from some configuration
1742 kerberos_enctypes = str(ENC_ALL_TYPES)
1744 try:
1745 msg = ldb.Message(ldb.Dn(samdb,
1746 samdb.searchone("distinguishedName",
1747 expression="samAccountName=%s$" % names.netbiosname,
1748 scope=ldb.SCOPE_SUBTREE)))
1749 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1750 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1751 name="msDS-SupportedEncryptionTypes")
1752 samdb.modify(msg)
1753 except ldb.LdbError, (enum, estr):
1754 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1755 # It might be that this attribute does not exist in this schema
1756 raise
1758 if serverrole == "domain controller":
1759 secretsdb_setup_dns(secrets_ldb, names,
1760 paths.private_dir, realm=names.realm,
1761 dnsdomain=names.dnsdomain,
1762 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1764 domainguid = samdb.searchone(basedn=domaindn,
1765 attribute="objectGUID")
1766 assert isinstance(domainguid, str)
1768 # Only make a zone file on the first DC, it should be
1769 # replicated with DNS replication
1770 create_zone_file(lp, logger, paths, targetdir,
1771 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1772 hostname=names.hostname, realm=names.realm,
1773 domainguid=domainguid, ntdsguid=names.ntdsguid)
1775 create_named_conf(paths, realm=names.realm,
1776 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1778 create_named_txt(paths.namedtxt,
1779 realm=names.realm, dnsdomain=names.dnsdomain,
1780 dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1781 private_dir=paths.private_dir,
1782 keytab_name=paths.dns_keytab)
1783 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1784 logger.info("and %s for further documentation required for secure DNS "
1785 "updates", paths.namedtxt)
1787 lastProvisionUSNs = get_last_provision_usn(samdb)
1788 maxUSN = get_max_usn(samdb, str(names.rootdn))
1789 if lastProvisionUSNs is not None:
1790 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1791 else:
1792 set_provision_usn(samdb, 0, maxUSN, invocationid)
1794 create_krb5_conf(paths.krb5conf,
1795 dnsdomain=names.dnsdomain, hostname=names.hostname,
1796 realm=names.realm)
1797 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1798 "generated at %s", paths.krb5conf)
1800 if serverrole == "domain controller":
1801 create_dns_update_list(lp, logger, paths)
1803 provision_backend.post_setup()
1804 provision_backend.shutdown()
1806 create_phpldapadmin_config(paths.phpldapadminconfig,
1807 ldapi_url)
1808 except Exception:
1809 secrets_ldb.transaction_cancel()
1810 raise
1812 # Now commit the secrets.ldb to disk
1813 secrets_ldb.transaction_commit()
1815 # the commit creates the dns.keytab, now chown it
1816 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1817 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1818 try:
1819 os.chmod(dns_keytab_path, 0640)
1820 os.chown(dns_keytab_path, -1, paths.bind_gid)
1821 except OSError:
1822 if not os.environ.has_key('SAMBA_SELFTEST'):
1823 logger.info("Failed to chown %s to bind gid %u",
1824 dns_keytab_path, paths.bind_gid)
1827 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1828 paths.phpldapadminconfig)
1830 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1831 logger.info("Server Role: %s" % serverrole)
1832 logger.info("Hostname: %s" % names.hostname)
1833 logger.info("NetBIOS Domain: %s" % names.domain)
1834 logger.info("DNS Domain: %s" % names.dnsdomain)
1835 logger.info("DOMAIN SID: %s" % str(domainsid))
1836 if samdb_fill == FILL_FULL:
1837 logger.info("Admin password: %s" % adminpass)
1838 if provision_backend.type is not "ldb":
1839 if provision_backend.credentials.get_bind_dn() is not None:
1840 logger.info("LDAP Backend Admin DN: %s" %
1841 provision_backend.credentials.get_bind_dn())
1842 else:
1843 logger.info("LDAP Admin User: %s" %
1844 provision_backend.credentials.get_username())
1846 logger.info("LDAP Admin Password: %s" %
1847 provision_backend.credentials.get_password())
1849 if provision_backend.slapd_command_escaped is not None:
1850 # now display slapd_command_file.txt to show how slapd must be
1851 # started next time
1852 logger.info("Use later the following commandline to start slapd, then Samba:")
1853 logger.info(provision_backend.slapd_command_escaped)
1854 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1855 provision_backend.ldapdir)
1857 result = ProvisionResult()
1858 result.domaindn = domaindn
1859 result.paths = paths
1860 result.lp = lp
1861 result.samdb = samdb
1862 return result
1865 def provision_become_dc(smbconf=None, targetdir=None,
1866 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1867 serverdn=None, domain=None, hostname=None, domainsid=None,
1868 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1869 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1870 root=None, nobody=None, users=None, wheel=None, backup=None,
1871 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1872 sitename=None, debuglevel=1):
1874 logger = logging.getLogger("provision")
1875 samba.set_debug_level(debuglevel)
1877 res = provision(logger, system_session(), None,
1878 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1879 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1880 configdn=configdn, serverdn=serverdn, domain=domain,
1881 hostname=hostname, hostip=None, domainsid=domainsid,
1882 machinepass=machinepass, serverrole="domain controller",
1883 sitename=sitename)
1884 res.lp.set("debuglevel", str(debuglevel))
1885 return res
1888 def create_phpldapadmin_config(path, ldapi_uri):
1889 """Create a PHP LDAP admin configuration file.
1891 :param path: Path to write the configuration to.
1893 setup_file(setup_path("phpldapadmin-config.php"), path,
1894 {"S4_LDAPI_URI": ldapi_uri})
1897 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1898 hostip, hostip6, hostname, realm, domainguid,
1899 ntdsguid):
1900 """Write out a DNS zone file, from the info in the current database.
1902 :param paths: paths object
1903 :param dnsdomain: DNS Domain name
1904 :param domaindn: DN of the Domain
1905 :param hostip: Local IPv4 IP
1906 :param hostip6: Local IPv6 IP
1907 :param hostname: Local hostname
1908 :param realm: Realm name
1909 :param domainguid: GUID of the domain.
1910 :param ntdsguid: GUID of the hosts nTDSDSA record.
1912 assert isinstance(domainguid, str)
1914 if hostip6 is not None:
1915 hostip6_base_line = " IN AAAA " + hostip6
1916 hostip6_host_line = hostname + " IN AAAA " + hostip6
1917 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1918 else:
1919 hostip6_base_line = ""
1920 hostip6_host_line = ""
1921 gc_msdcs_ip6_line = ""
1923 if hostip is not None:
1924 hostip_base_line = " IN A " + hostip
1925 hostip_host_line = hostname + " IN A " + hostip
1926 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1927 else:
1928 hostip_base_line = ""
1929 hostip_host_line = ""
1930 gc_msdcs_ip_line = ""
1932 dns_dir = os.path.dirname(paths.dns)
1934 try:
1935 shutil.rmtree(dns_dir, True)
1936 except OSError:
1937 pass
1939 os.mkdir(dns_dir, 0775)
1941 # we need to freeze the zone while we update the contents
1942 if targetdir is None:
1943 rndc = ' '.join(lp.get("rndc command"))
1944 os.system(rndc + " freeze " + lp.get("realm"))
1946 setup_file(setup_path("provision.zone"), paths.dns, {
1947 "HOSTNAME": hostname,
1948 "DNSDOMAIN": dnsdomain,
1949 "REALM": realm,
1950 "HOSTIP_BASE_LINE": hostip_base_line,
1951 "HOSTIP_HOST_LINE": hostip_host_line,
1952 "DOMAINGUID": domainguid,
1953 "DATESTRING": time.strftime("%Y%m%d%H"),
1954 "DEFAULTSITE": DEFAULTSITE,
1955 "NTDSGUID": ntdsguid,
1956 "HOSTIP6_BASE_LINE": hostip6_base_line,
1957 "HOSTIP6_HOST_LINE": hostip6_host_line,
1958 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
1959 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
1962 # note that we use no variable substitution on this file
1963 # the substitution is done at runtime by samba_dnsupdate
1964 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1966 # and the SPN update list
1967 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1969 if paths.bind_gid is not None:
1970 try:
1971 os.chown(dns_dir, -1, paths.bind_gid)
1972 os.chown(paths.dns, -1, paths.bind_gid)
1973 # chmod needed to cope with umask
1974 os.chmod(dns_dir, 0775)
1975 os.chmod(paths.dns, 0664)
1976 except OSError:
1977 if not os.environ.has_key('SAMBA_SELFTEST'):
1978 logger.error("Failed to chown %s to bind gid %u" % (
1979 dns_dir, paths.bind_gid))
1981 if targetdir is None:
1982 os.system(rndc + " unfreeze " + lp.get("realm"))
1985 def create_dns_update_list(lp, logger, paths):
1986 """Write out a dns_update_list file"""
1987 # note that we use no variable substitution on this file
1988 # the substitution is done at runtime by samba_dnsupdate
1989 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1990 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1993 def create_named_conf(paths, realm, dnsdomain,
1994 private_dir):
1995 """Write out a file containing zone statements suitable for inclusion in a
1996 named.conf file (including GSS-TSIG configuration).
1998 :param paths: all paths
1999 :param realm: Realm name
2000 :param dnsdomain: DNS Domain name
2001 :param private_dir: Path to private directory
2002 :param keytab_name: File name of DNS keytab file
2005 setup_file(setup_path("named.conf"), paths.namedconf, {
2006 "DNSDOMAIN": dnsdomain,
2007 "REALM": realm,
2008 "ZONE_FILE": paths.dns,
2009 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2010 "NAMED_CONF": paths.namedconf,
2011 "NAMED_CONF_UPDATE": paths.namedconf_update
2014 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2017 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2018 keytab_name):
2019 """Write out a file containing zone statements suitable for inclusion in a
2020 named.conf file (including GSS-TSIG configuration).
2022 :param path: Path of the new named.conf file.
2023 :param realm: Realm name
2024 :param dnsdomain: DNS Domain name
2025 :param private_dir: Path to private directory
2026 :param keytab_name: File name of DNS keytab file
2028 setup_file(setup_path("named.txt"), path, {
2029 "DNSDOMAIN": dnsdomain,
2030 "DNSNAME" : dnsname,
2031 "REALM": realm,
2032 "DNS_KEYTAB": keytab_name,
2033 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2034 "PRIVATE_DIR": private_dir
2038 def create_krb5_conf(path, dnsdomain, hostname, realm):
2039 """Write out a file containing zone statements suitable for inclusion in a
2040 named.conf file (including GSS-TSIG configuration).
2042 :param path: Path of the new named.conf file.
2043 :param dnsdomain: DNS Domain name
2044 :param hostname: Local hostname
2045 :param realm: Realm name
2047 setup_file(setup_path("krb5.conf"), path, {
2048 "DNSDOMAIN": dnsdomain,
2049 "HOSTNAME": hostname,
2050 "REALM": realm,
2054 class ProvisioningError(Exception):
2055 """A generic provision error."""
2057 def __init__(self, value):
2058 self.value = value
2060 def __str__(self):
2061 return "ProvisioningError: " + self.value
2064 class InvalidNetbiosName(Exception):
2065 """A specified name was not a valid NetBIOS name."""
2066 def __init__(self, name):
2067 super(InvalidNetbiosName, self).__init__(
2068 "The name '%r' is not a valid NetBIOS name" % name)