s4-provision: Add DNS backend option to provision
[Samba.git] / source4 / scripting / python / samba / provision / __init__.py
blob9c2815721fc6975201140a4ffe18ae60fd77aa80
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 __docformat__ = "restructuredText"
30 from base64 import b64encode
31 import os
32 import re
33 import pwd
34 import grp
35 import logging
36 import time
37 import uuid
38 import socket
39 import urllib
40 import shutil
41 import string
43 import ldb
45 from samba.auth import system_session, admin_session
46 import samba
47 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
48 from samba import (
49 Ldb,
50 check_all_substituted,
51 read_and_sub_file,
52 setup_file,
53 substitute_var,
54 valid_netbios_name,
55 version,
57 from samba.dcerpc import security, misc
58 from samba.dcerpc.misc import (
59 SEC_CHAN_BDC,
60 SEC_CHAN_WKSTA,
62 from samba.dsdb import (
63 DS_DOMAIN_FUNCTION_2003,
64 DS_DOMAIN_FUNCTION_2008_R2,
65 ENC_ALL_TYPES,
67 from samba.idmap import IDmapDB
68 from samba.ms_display_specifiers import read_ms_ldif
69 from samba.ntacls import setntacl, dsacl2fsacl
70 from samba.ndr import ndr_pack, ndr_unpack
71 from samba.provision.backend import (
72 ExistingBackend,
73 FDSBackend,
74 LDBBackend,
75 OpenLDAPBackend,
77 from samba.provision.sambadns import setup_ad_dns
79 import samba.param
80 import samba.registry
81 from samba.schema import Schema
82 from samba.samdb import SamDB
83 from samba.dbchecker import dbcheck
86 VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
87 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
88 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
89 DEFAULTSITE = "Default-First-Site-Name"
90 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
93 def setup_path(file):
94 """Return an absolute path to the provision tempate file specified by file"""
95 return os.path.join(samba.param.setup_dir(), file)
97 # Descriptors of naming contexts and other important objects
99 # "get_schema_descriptor" is located in "schema.py"
101 def get_config_descriptor(domain_sid):
102 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
103 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
104 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
105 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
106 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
107 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
108 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
109 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
110 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
111 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
112 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
113 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
114 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
115 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
116 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
117 sec = security.descriptor.from_sddl(sddl, domain_sid)
118 return ndr_pack(sec)
121 def get_domain_descriptor(domain_sid):
122 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
126 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
127 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
129 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
130 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
131 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
132 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
133 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
134 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
135 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
136 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
137 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
138 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
139 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
140 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
141 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
142 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
143 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
144 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
145 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
146 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
147 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
148 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
149 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
150 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
151 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
152 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
153 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
154 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
155 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
156 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
157 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
158 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
159 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
160 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
161 "(A;;RPRC;;;RU)" \
162 "(A;CI;LC;;;RU)" \
163 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
164 "(A;;RP;;;WD)" \
165 "(A;;RPLCLORC;;;ED)" \
166 "(A;;RPLCLORC;;;AU)" \
167 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
168 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
169 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
170 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
171 sec = security.descriptor.from_sddl(sddl, domain_sid)
172 return ndr_pack(sec)
175 class ProvisionPaths(object):
177 def __init__(self):
178 self.shareconf = None
179 self.hklm = None
180 self.hkcu = None
181 self.hkcr = None
182 self.hku = None
183 self.hkpd = None
184 self.hkpt = None
185 self.samdb = None
186 self.idmapdb = None
187 self.secrets = None
188 self.keytab = None
189 self.dns_keytab = None
190 self.dns = None
191 self.winsdb = None
192 self.private_dir = None
195 class ProvisionNames(object):
197 def __init__(self):
198 self.rootdn = None
199 self.domaindn = None
200 self.configdn = None
201 self.schemadn = None
202 self.ldapmanagerdn = None
203 self.dnsdomain = None
204 self.realm = None
205 self.netbiosname = None
206 self.domain = None
207 self.hostname = None
208 self.sitename = None
209 self.smbconf = None
211 def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp):
212 """Get key provision parameters (realm, domain, ...) from a given provision
214 :param samdb: An LDB object connected to the sam.ldb file
215 :param secretsdb: An LDB object connected to the secrets.ldb file
216 :param idmapdb: An LDB object connected to the idmap.ldb file
217 :param paths: A list of path to provision object
218 :param smbconf: Path to the smb.conf file
219 :param lp: A LoadParm object
220 :return: A list of key provision parameters
222 names = ProvisionNames()
223 names.adminpass = None
225 # NT domain, kerberos realm, root dn, domain dn, domain dns name
226 names.domain = string.upper(lp.get("workgroup"))
227 names.realm = lp.get("realm")
228 names.dnsdomain = names.realm.lower()
229 basedn = samba.dn_from_dns_name(names.dnsdomain)
230 names.realm = string.upper(names.realm)
231 # netbiosname
232 # Get the netbiosname first (could be obtained from smb.conf in theory)
233 res = secretsdb.search(expression="(flatname=%s)" %
234 names.domain,base="CN=Primary Domains",
235 scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"])
236 names.netbiosname = str(res[0]["sAMAccountName"]).replace("$","")
238 names.smbconf = smbconf
240 # That's a bit simplistic but it's ok as long as we have only 3
241 # partitions
242 current = samdb.search(expression="(objectClass=*)",
243 base="", scope=ldb.SCOPE_BASE,
244 attrs=["defaultNamingContext", "schemaNamingContext",
245 "configurationNamingContext","rootDomainNamingContext"])
247 names.configdn = current[0]["configurationNamingContext"]
248 configdn = str(names.configdn)
249 names.schemadn = current[0]["schemaNamingContext"]
250 if not (ldb.Dn(samdb, basedn) == (ldb.Dn(samdb,
251 current[0]["defaultNamingContext"][0]))):
252 raise ProvisioningError(("basedn in %s (%s) and from %s (%s)"
253 "is not the same ..." % (paths.samdb,
254 str(current[0]["defaultNamingContext"][0]),
255 paths.smbconf, basedn)))
257 names.domaindn=current[0]["defaultNamingContext"]
258 names.rootdn=current[0]["rootDomainNamingContext"]
259 # default site name
260 res3 = samdb.search(expression="(objectClass=site)",
261 base="CN=Sites," + configdn, scope=ldb.SCOPE_ONELEVEL, attrs=["cn"])
262 names.sitename = str(res3[0]["cn"])
264 # dns hostname and server dn
265 res4 = samdb.search(expression="(CN=%s)" % names.netbiosname,
266 base="OU=Domain Controllers,%s" % basedn,
267 scope=ldb.SCOPE_ONELEVEL, attrs=["dNSHostName"])
268 names.hostname = str(res4[0]["dNSHostName"]).replace("." + names.dnsdomain,"")
270 server_res = samdb.search(expression="serverReference=%s" % res4[0].dn,
271 attrs=[], base=configdn)
272 names.serverdn = server_res[0].dn
274 # invocation id/objectguid
275 res5 = samdb.search(expression="(objectClass=*)",
276 base="CN=NTDS Settings,%s" % str(names.serverdn), scope=ldb.SCOPE_BASE,
277 attrs=["invocationID", "objectGUID"])
278 names.invocation = str(ndr_unpack(misc.GUID, res5[0]["invocationId"][0]))
279 names.ntdsguid = str(ndr_unpack(misc.GUID, res5[0]["objectGUID"][0]))
281 # domain guid/sid
282 res6 = samdb.search(expression="(objectClass=*)", base=basedn,
283 scope=ldb.SCOPE_BASE, attrs=["objectGUID",
284 "objectSid","msDS-Behavior-Version" ])
285 names.domainguid = str(ndr_unpack(misc.GUID, res6[0]["objectGUID"][0]))
286 names.domainsid = ndr_unpack( security.dom_sid, res6[0]["objectSid"][0])
287 if res6[0].get("msDS-Behavior-Version") is None or \
288 int(res6[0]["msDS-Behavior-Version"][0]) < DS_DOMAIN_FUNCTION_2000:
289 names.domainlevel = DS_DOMAIN_FUNCTION_2000
290 else:
291 names.domainlevel = int(res6[0]["msDS-Behavior-Version"][0])
293 # policy guid
294 res7 = samdb.search(expression="(displayName=Default Domain Policy)",
295 base="CN=Policies,CN=System," + basedn,
296 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
297 names.policyid = str(res7[0]["cn"]).replace("{","").replace("}","")
298 # dc policy guid
299 res8 = samdb.search(expression="(displayName=Default Domain Controllers"
300 " Policy)",
301 base="CN=Policies,CN=System," + basedn,
302 scope=ldb.SCOPE_ONELEVEL, attrs=["cn","displayName"])
303 if len(res8) == 1:
304 names.policyid_dc = str(res8[0]["cn"]).replace("{","").replace("}","")
305 else:
306 names.policyid_dc = None
307 res9 = idmapdb.search(expression="(cn=%s)" %
308 (security.SID_BUILTIN_ADMINISTRATORS),
309 attrs=["xidNumber"])
310 if len(res9) == 1:
311 names.wheel_gid = res9[0]["xidNumber"]
312 else:
313 raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
314 return names
316 def update_provision_usn(samdb, low, high, id, replace=False):
317 """Update the field provisionUSN in sam.ldb
319 This field is used to track range of USN modified by provision and
320 upgradeprovision.
321 This value is used afterward by next provision to figure out if
322 the field have been modified since last provision.
324 :param samdb: An LDB object connect to sam.ldb
325 :param low: The lowest USN modified by this upgrade
326 :param high: The highest USN modified by this upgrade
327 :param id: The invocation id of the samba's dc
328 :param replace: A boolean indicating if the range should replace any
329 existing one or appended (default)
332 tab = []
333 if not replace:
334 entry = samdb.search(base="@PROVISION",
335 scope=ldb.SCOPE_BASE,
336 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "dn"])
337 for e in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
338 if not re.search(';', e):
339 e = "%s;%s" % (e, id)
340 tab.append(str(e))
342 tab.append("%s-%s;%s" % (low, high, id))
343 delta = ldb.Message()
344 delta.dn = ldb.Dn(samdb, "@PROVISION")
345 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
346 ldb.FLAG_MOD_REPLACE, LAST_PROVISION_USN_ATTRIBUTE)
347 entry = samdb.search(expression='provisionnerID=*',
348 base="@PROVISION", scope=ldb.SCOPE_BASE,
349 attrs=["provisionnerID"])
350 if len(entry) == 0 or len(entry[0]) == 0:
351 delta["provisionnerID"] = ldb.MessageElement(id, ldb.FLAG_MOD_ADD, "provisionnerID")
352 samdb.modify(delta)
355 def set_provision_usn(samdb, low, high, id):
356 """Set the field provisionUSN in sam.ldb
357 This field is used to track range of USN modified by provision and
358 upgradeprovision.
359 This value is used afterward by next provision to figure out if
360 the field have been modified since last provision.
362 :param samdb: An LDB object connect to sam.ldb
363 :param low: The lowest USN modified by this upgrade
364 :param high: The highest USN modified by this upgrade
365 :param id: The invocationId of the provision"""
367 tab = []
368 tab.append("%s-%s;%s" % (low, high, id))
370 delta = ldb.Message()
371 delta.dn = ldb.Dn(samdb, "@PROVISION")
372 delta[LAST_PROVISION_USN_ATTRIBUTE] = ldb.MessageElement(tab,
373 ldb.FLAG_MOD_ADD, LAST_PROVISION_USN_ATTRIBUTE)
374 samdb.add(delta)
377 def get_max_usn(samdb,basedn):
378 """ This function return the biggest USN present in the provision
380 :param samdb: A LDB object pointing to the sam.ldb
381 :param basedn: A string containing the base DN of the provision
382 (ie. DC=foo, DC=bar)
383 :return: The biggest USN in the provision"""
385 res = samdb.search(expression="objectClass=*",base=basedn,
386 scope=ldb.SCOPE_SUBTREE,attrs=["uSNChanged"],
387 controls=["search_options:1:2",
388 "server_sort:1:1:uSNChanged",
389 "paged_results:1:1"])
390 return res[0]["uSNChanged"]
393 def get_last_provision_usn(sam):
394 """Get USNs ranges modified by a provision or an upgradeprovision
396 :param sam: An LDB object pointing to the sam.ldb
397 :return: a dictionnary which keys are invocation id and values are an array
398 of integer representing the different ranges
400 try:
401 entry = sam.search(expression="%s=*" % LAST_PROVISION_USN_ATTRIBUTE,
402 base="@PROVISION", scope=ldb.SCOPE_BASE,
403 attrs=[LAST_PROVISION_USN_ATTRIBUTE, "provisionnerID"])
404 except ldb.LdbError, (ecode, emsg):
405 if ecode == ldb.ERR_NO_SUCH_OBJECT:
406 return None
407 raise
408 if len(entry):
409 myids = []
410 range = {}
411 p = re.compile(r'-')
412 if entry[0].get("provisionnerID"):
413 for e in entry[0]["provisionnerID"]:
414 myids.append(str(e))
415 for r in entry[0][LAST_PROVISION_USN_ATTRIBUTE]:
416 tab1 = str(r).split(';')
417 if len(tab1) == 2:
418 id = tab1[1]
419 else:
420 id = "default"
421 if (len(myids) > 0 and id not in myids):
422 continue
423 tab2 = p.split(tab1[0])
424 if range.get(id) == None:
425 range[id] = []
426 range[id].append(tab2[0])
427 range[id].append(tab2[1])
428 return range
429 else:
430 return None
433 class ProvisionResult(object):
435 def __init__(self):
436 self.paths = None
437 self.domaindn = None
438 self.lp = None
439 self.samdb = None
440 self.idmap = None
443 def check_install(lp, session_info, credentials):
444 """Check whether the current install seems ok.
446 :param lp: Loadparm context
447 :param session_info: Session information
448 :param credentials: Credentials
450 if lp.get("realm") == "":
451 raise Exception("Realm empty")
452 samdb = Ldb(lp.samdb_url(), session_info=session_info,
453 credentials=credentials, lp=lp)
454 if len(samdb.search("(cn=Administrator)")) != 1:
455 raise ProvisioningError("No administrator account found")
458 def findnss(nssfn, names):
459 """Find a user or group from a list of possibilities.
461 :param nssfn: NSS Function to try (should raise KeyError if not found)
462 :param names: Names to check.
463 :return: Value return by first names list.
465 for name in names:
466 try:
467 return nssfn(name)
468 except KeyError:
469 pass
470 raise KeyError("Unable to find user/group in %r" % names)
473 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
474 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
477 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
478 """Setup a ldb in the private dir.
480 :param ldb: LDB file to import data into
481 :param ldif_path: Path of the LDIF file to load
482 :param subst_vars: Optional variables to subsitute in LDIF.
483 :param nocontrols: Optional list of controls, can be None for no controls
485 assert isinstance(ldif_path, str)
486 data = read_and_sub_file(ldif_path, subst_vars)
487 ldb.add_ldif(data, controls)
490 def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
491 """Modify a ldb in the private dir.
493 :param ldb: LDB object.
494 :param ldif_path: LDIF file path.
495 :param subst_vars: Optional dictionary with substitution variables.
497 data = read_and_sub_file(ldif_path, subst_vars)
498 ldb.modify_ldif(data, controls)
501 def setup_ldb(ldb, ldif_path, subst_vars):
502 """Import a LDIF a file into a LDB handle, optionally substituting
503 variables.
505 :note: Either all LDIF data will be added or none (using transactions).
507 :param ldb: LDB file to import into.
508 :param ldif_path: Path to the LDIF file.
509 :param subst_vars: Dictionary with substitution variables.
511 assert ldb is not None
512 ldb.transaction_start()
513 try:
514 setup_add_ldif(ldb, ldif_path, subst_vars)
515 except Exception:
516 ldb.transaction_cancel()
517 raise
518 else:
519 ldb.transaction_commit()
522 def provision_paths_from_lp(lp, dnsdomain):
523 """Set the default paths for provisioning.
525 :param lp: Loadparm context.
526 :param dnsdomain: DNS Domain name
528 paths = ProvisionPaths()
529 paths.private_dir = lp.get("private dir")
531 # This is stored without path prefix for the "privateKeytab" attribute in
532 # "secrets_dns.ldif".
533 paths.dns_keytab = "dns.keytab"
534 paths.keytab = "secrets.keytab"
536 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
537 paths.samdb = os.path.join(paths.private_dir, "sam.ldb")
538 paths.idmapdb = os.path.join(paths.private_dir, "idmap.ldb")
539 paths.secrets = os.path.join(paths.private_dir, "secrets.ldb")
540 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
541 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
542 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
543 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
544 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
545 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
546 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
547 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
548 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
549 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
550 paths.phpldapadminconfig = os.path.join(paths.private_dir,
551 "phpldapadmin-config.php")
552 paths.hklm = "hklm.ldb"
553 paths.hkcr = "hkcr.ldb"
554 paths.hkcu = "hkcu.ldb"
555 paths.hku = "hku.ldb"
556 paths.hkpd = "hkpd.ldb"
557 paths.hkpt = "hkpt.ldb"
558 paths.sysvol = lp.get("path", "sysvol")
559 paths.netlogon = lp.get("path", "netlogon")
560 paths.smbconf = lp.configfile
561 return paths
564 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
565 serverrole=None, rootdn=None, domaindn=None, configdn=None,
566 schemadn=None, serverdn=None, sitename=None):
567 """Guess configuration settings to use."""
569 if hostname is None:
570 hostname = socket.gethostname().split(".")[0]
572 netbiosname = lp.get("netbios name")
573 if netbiosname is None:
574 netbiosname = hostname
575 # remove forbidden chars
576 newnbname = ""
577 for x in netbiosname:
578 if x.isalnum() or x in VALID_NETBIOS_CHARS:
579 newnbname = "%s%c" % (newnbname, x)
580 # force the length to be <16
581 netbiosname = newnbname[0:15]
582 assert netbiosname is not None
583 netbiosname = netbiosname.upper()
584 if not valid_netbios_name(netbiosname):
585 raise InvalidNetbiosName(netbiosname)
587 if dnsdomain is None:
588 dnsdomain = lp.get("realm")
589 if dnsdomain is None or dnsdomain == "":
590 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
592 dnsdomain = dnsdomain.lower()
594 if serverrole is None:
595 serverrole = lp.get("server role")
596 if serverrole is None:
597 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
599 serverrole = serverrole.lower()
601 realm = dnsdomain.upper()
603 if lp.get("realm") == "":
604 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
606 if lp.get("realm").upper() != realm:
607 raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
609 if lp.get("server role").lower() != serverrole:
610 raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), serverrole, lp.configfile))
612 if serverrole == "domain controller":
613 if domain is None:
614 # This will, for better or worse, default to 'WORKGROUP'
615 domain = lp.get("workgroup")
616 domain = domain.upper()
618 if lp.get("workgroup").upper() != domain:
619 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
621 if domaindn is None:
622 domaindn = samba.dn_from_dns_name(dnsdomain)
624 if domain == netbiosname:
625 raise ProvisioningError("guess_names: Domain '%s' must not be equal to short host name '%s'!" % (domain, netbiosname))
626 else:
627 domain = netbiosname
628 if domaindn is None:
629 domaindn = "DC=" + netbiosname
631 if not valid_netbios_name(domain):
632 raise InvalidNetbiosName(domain)
634 if hostname.upper() == realm:
635 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
636 if netbiosname.upper() == realm:
637 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
638 if domain == realm:
639 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
641 if rootdn is None:
642 rootdn = domaindn
644 if configdn is None:
645 configdn = "CN=Configuration," + rootdn
646 if schemadn is None:
647 schemadn = "CN=Schema," + configdn
649 if sitename is None:
650 sitename=DEFAULTSITE
652 names = ProvisionNames()
653 names.rootdn = rootdn
654 names.domaindn = domaindn
655 names.configdn = configdn
656 names.schemadn = schemadn
657 names.ldapmanagerdn = "CN=Manager," + rootdn
658 names.dnsdomain = dnsdomain
659 names.domain = domain
660 names.realm = realm
661 names.netbiosname = netbiosname
662 names.hostname = hostname
663 names.sitename = sitename
664 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (
665 netbiosname, sitename, configdn)
667 return names
670 def make_smbconf(smbconf, hostname, domain, realm, serverrole,
671 targetdir, sid_generator="internal", eadb=False, lp=None):
672 """Create a new smb.conf file based on a couple of basic settings.
674 assert smbconf is not None
675 if hostname is None:
676 hostname = socket.gethostname().split(".")[0]
677 netbiosname = hostname.upper()
678 # remove forbidden chars
679 newnbname = ""
680 for x in netbiosname:
681 if x.isalnum() or x in VALID_NETBIOS_CHARS:
682 newnbname = "%s%c" % (newnbname, x)
683 #force the length to be <16
684 netbiosname = newnbname[0:15]
685 else:
686 netbiosname = hostname.upper()
688 if serverrole is None:
689 serverrole = "standalone"
691 assert serverrole in ("domain controller", "member server", "standalone")
692 if serverrole == "domain controller":
693 smbconfsuffix = "dc"
694 elif serverrole == "member server":
695 smbconfsuffix = "member"
696 elif serverrole == "standalone":
697 smbconfsuffix = "standalone"
699 if sid_generator is None:
700 sid_generator = "internal"
702 assert domain is not None
703 domain = domain.upper()
705 assert realm is not None
706 realm = realm.upper()
708 if lp is None:
709 lp = samba.param.LoadParm()
710 #Load non-existant file
711 if os.path.exists(smbconf):
712 lp.load(smbconf)
713 if eadb and not lp.get("posix:eadb"):
714 if targetdir is not None:
715 privdir = os.path.join(targetdir, "private")
716 else:
717 privdir = lp.get("private dir")
718 lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
720 if targetdir is not None:
721 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
722 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
723 statedir_line = "state directory = " + os.path.abspath(targetdir)
724 cachedir_line = "cache directory = " + os.path.abspath(targetdir)
726 lp.set("lock dir", os.path.abspath(targetdir))
727 lp.set("state directory", os.path.abspath(targetdir))
728 lp.set("cache directory", os.path.abspath(targetdir))
729 else:
730 privatedir_line = ""
731 lockdir_line = ""
732 statedir_line = ""
733 cachedir_line = ""
735 sysvol = os.path.join(lp.get("state directory"), "sysvol")
736 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
738 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
739 smbconf, {
740 "NETBIOS_NAME": netbiosname,
741 "DOMAIN": domain,
742 "REALM": realm,
743 "SERVERROLE": serverrole,
744 "NETLOGONPATH": netlogon,
745 "SYSVOLPATH": sysvol,
746 "PRIVATEDIR_LINE": privatedir_line,
747 "LOCKDIR_LINE": lockdir_line,
748 "STATEDIR_LINE": statedir_line,
749 "CACHEDIR_LINE": cachedir_line
752 # reload the smb.conf
753 lp.load(smbconf)
755 # and dump it without any values that are the default
756 # this ensures that any smb.conf parameters that were set
757 # on the provision/join command line are set in the resulting smb.conf
758 f = open(smbconf, mode='w')
759 lp.dump(f, False)
760 f.close()
764 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
765 users_gid, wheel_gid):
766 """setup reasonable name mappings for sam names to unix names.
768 :param samdb: SamDB object.
769 :param idmap: IDmap db object.
770 :param sid: The domain sid.
771 :param domaindn: The domain DN.
772 :param root_uid: uid of the UNIX root user.
773 :param nobody_uid: uid of the UNIX nobody user.
774 :param users_gid: gid of the UNIX users group.
775 :param wheel_gid: gid of the UNIX wheel group.
777 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
778 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
780 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
781 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
784 def setup_samdb_partitions(samdb_path, logger, lp, session_info,
785 provision_backend, names, schema, serverrole,
786 erase=False):
787 """Setup the partitions for the SAM database.
789 Alternatively, provision() may call this, and then populate the database.
791 :note: This will wipe the Sam Database!
793 :note: This function always removes the local SAM LDB file. The erase
794 parameter controls whether to erase the existing data, which
795 may not be stored locally but in LDAP.
798 assert session_info is not None
800 # We use options=["modules:"] to stop the modules loading - we
801 # just want to wipe and re-initialise the database, not start it up
803 try:
804 os.unlink(samdb_path)
805 except OSError:
806 pass
808 samdb = Ldb(url=samdb_path, session_info=session_info,
809 lp=lp, options=["modules:"])
811 ldap_backend_line = "# No LDAP backend"
812 if provision_backend.type is not "ldb":
813 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
815 samdb.transaction_start()
816 try:
817 logger.info("Setting up sam.ldb partitions and settings")
818 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
819 "LDAP_BACKEND_LINE": ldap_backend_line
823 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
824 "BACKEND_TYPE": provision_backend.type,
825 "SERVER_ROLE": serverrole
828 logger.info("Setting up sam.ldb rootDSE")
829 setup_samdb_rootdse(samdb, names)
830 except Exception:
831 samdb.transaction_cancel()
832 raise
833 else:
834 samdb.transaction_commit()
837 def secretsdb_self_join(secretsdb, domain,
838 netbiosname, machinepass, domainsid=None,
839 realm=None, dnsdomain=None,
840 keytab_path=None,
841 key_version_number=1,
842 secure_channel_type=SEC_CHAN_WKSTA):
843 """Add domain join-specific bits to a secrets database.
845 :param secretsdb: Ldb Handle to the secrets database
846 :param machinepass: Machine password
848 attrs = ["whenChanged",
849 "secret",
850 "priorSecret",
851 "priorChanged",
852 "krb5Keytab",
853 "privateKeytab"]
855 if realm is not None:
856 if dnsdomain is None:
857 dnsdomain = realm.lower()
858 dnsname = '%s.%s' % (netbiosname.lower(), dnsdomain.lower())
859 else:
860 dnsname = None
861 shortname = netbiosname.lower()
863 # We don't need to set msg["flatname"] here, because rdn_name will handle
864 # it, and it causes problems for modifies anyway
865 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
866 msg["secureChannelType"] = [str(secure_channel_type)]
867 msg["objectClass"] = ["top", "primaryDomain"]
868 if dnsname is not None:
869 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
870 msg["realm"] = [realm]
871 msg["saltPrincipal"] = ["host/%s@%s" % (dnsname, realm.upper())]
872 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
873 msg["privateKeytab"] = ["secrets.keytab"]
875 msg["secret"] = [machinepass]
876 msg["samAccountName"] = ["%s$" % netbiosname]
877 msg["secureChannelType"] = [str(secure_channel_type)]
878 if domainsid is not None:
879 msg["objectSid"] = [ndr_pack(domainsid)]
881 # This complex expression tries to ensure that we don't have more
882 # than one record for this SID, realm or netbios domain at a time,
883 # but we don't delete the old record that we are about to modify,
884 # because that would delete the keytab and previous password.
885 res = secretsdb.search(base="cn=Primary Domains", attrs=attrs,
886 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain)(!(dn=%s)))" % (domain, realm, str(domainsid), str(msg.dn))),
887 scope=ldb.SCOPE_ONELEVEL)
889 for del_msg in res:
890 secretsdb.delete(del_msg.dn)
892 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
894 if len(res) == 1:
895 msg["priorSecret"] = [res[0]["secret"][0]]
896 msg["priorWhenChanged"] = [res[0]["whenChanged"][0]]
898 try:
899 msg["privateKeytab"] = [res[0]["privateKeytab"][0]]
900 except KeyError:
901 pass
903 try:
904 msg["krb5Keytab"] = [res[0]["krb5Keytab"][0]]
905 except KeyError:
906 pass
908 for el in msg:
909 if el != 'dn':
910 msg[el].set_flags(ldb.FLAG_MOD_REPLACE)
911 secretsdb.modify(msg)
912 secretsdb.rename(res[0].dn, msg.dn)
913 else:
914 spn = [ 'HOST/%s' % shortname ]
915 if secure_channel_type == SEC_CHAN_BDC and dnsname is not None:
916 # we are a domain controller then we add servicePrincipalName
917 # entries for the keytab code to update.
918 spn.extend([ 'HOST/%s' % dnsname ])
919 msg["servicePrincipalName"] = spn
921 secretsdb.add(msg)
924 def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
925 dnsdomain, dns_keytab_path, dnspass):
926 """Add DNS specific bits to a secrets database.
928 :param secretsdb: Ldb Handle to the secrets database
929 :param machinepass: Machine password
931 try:
932 os.unlink(os.path.join(private_dir, dns_keytab_path))
933 except OSError:
934 pass
936 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
937 "REALM": realm,
938 "DNSDOMAIN": dnsdomain,
939 "DNS_KEYTAB": dns_keytab_path,
940 "DNSPASS_B64": b64encode(dnspass),
941 "HOSTNAME": names.hostname,
942 "DNSNAME" : '%s.%s' % (
943 names.netbiosname.lower(), names.dnsdomain.lower())
947 def setup_secretsdb(paths, session_info, backend_credentials, lp):
948 """Setup the secrets database.
950 :note: This function does not handle exceptions and transaction on purpose,
951 it's up to the caller to do this job.
953 :param path: Path to the secrets database.
954 :param session_info: Session info.
955 :param credentials: Credentials
956 :param lp: Loadparm context
957 :return: LDB handle for the created secrets database
959 if os.path.exists(paths.secrets):
960 os.unlink(paths.secrets)
962 keytab_path = os.path.join(paths.private_dir, paths.keytab)
963 if os.path.exists(keytab_path):
964 os.unlink(keytab_path)
966 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
967 if os.path.exists(dns_keytab_path):
968 os.unlink(dns_keytab_path)
970 path = paths.secrets
972 secrets_ldb = Ldb(path, session_info=session_info,
973 lp=lp)
974 secrets_ldb.erase()
975 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
976 secrets_ldb = Ldb(path, session_info=session_info,
977 lp=lp)
978 secrets_ldb.transaction_start()
979 try:
980 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
982 if (backend_credentials is not None and
983 backend_credentials.authentication_requested()):
984 if backend_credentials.get_bind_dn() is not None:
985 setup_add_ldif(secrets_ldb,
986 setup_path("secrets_simple_ldap.ldif"), {
987 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
988 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
990 else:
991 setup_add_ldif(secrets_ldb,
992 setup_path("secrets_sasl_ldap.ldif"), {
993 "LDAPADMINUSER": backend_credentials.get_username(),
994 "LDAPADMINREALM": backend_credentials.get_realm(),
995 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
998 return secrets_ldb
999 except Exception:
1000 secrets_ldb.transaction_cancel()
1001 raise
1004 def setup_privileges(path, session_info, lp):
1005 """Setup the privileges database.
1007 :param path: Path to the privileges database.
1008 :param session_info: Session info.
1009 :param credentials: Credentials
1010 :param lp: Loadparm context
1011 :return: LDB handle for the created secrets database
1013 if os.path.exists(path):
1014 os.unlink(path)
1015 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
1016 privilege_ldb.erase()
1017 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
1020 def setup_registry(path, session_info, lp):
1021 """Setup the registry.
1023 :param path: Path to the registry database
1024 :param session_info: Session information
1025 :param credentials: Credentials
1026 :param lp: Loadparm context
1028 reg = samba.registry.Registry()
1029 hive = samba.registry.open_ldb(path, session_info=session_info, lp_ctx=lp)
1030 reg.mount_hive(hive, samba.registry.HKEY_LOCAL_MACHINE)
1031 provision_reg = setup_path("provision.reg")
1032 assert os.path.exists(provision_reg)
1033 reg.diff_apply(provision_reg)
1036 def setup_idmapdb(path, session_info, lp):
1037 """Setup the idmap database.
1039 :param path: path to the idmap database
1040 :param session_info: Session information
1041 :param credentials: Credentials
1042 :param lp: Loadparm context
1044 if os.path.exists(path):
1045 os.unlink(path)
1047 idmap_ldb = IDmapDB(path, session_info=session_info, lp=lp)
1048 idmap_ldb.erase()
1049 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
1050 return idmap_ldb
1053 def setup_samdb_rootdse(samdb, names):
1054 """Setup the SamDB rootdse.
1056 :param samdb: Sam Database handle
1058 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
1059 "SCHEMADN": names.schemadn,
1060 "DOMAINDN": names.domaindn,
1061 "ROOTDN" : names.rootdn,
1062 "CONFIGDN": names.configdn,
1063 "SERVERDN": names.serverdn,
1067 def setup_self_join(samdb, names, machinepass, dnspass,
1068 domainsid, next_rid, invocationid,
1069 policyguid, policyguid_dc, domainControllerFunctionality,
1070 ntdsguid, dc_rid=None):
1071 """Join a host to its own domain."""
1072 assert isinstance(invocationid, str)
1073 if ntdsguid is not None:
1074 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
1075 else:
1076 ntdsguid_line = ""
1078 if dc_rid is None:
1079 dc_rid = next_rid
1081 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
1082 "CONFIGDN": names.configdn,
1083 "SCHEMADN": names.schemadn,
1084 "DOMAINDN": names.domaindn,
1085 "SERVERDN": names.serverdn,
1086 "INVOCATIONID": invocationid,
1087 "NETBIOSNAME": names.netbiosname,
1088 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
1089 "MACHINEPASS_B64": b64encode(machinepass.encode('utf-16-le')),
1090 "DOMAINSID": str(domainsid),
1091 "DCRID": str(dc_rid),
1092 "SAMBA_VERSION_STRING": version,
1093 "NTDSGUID": ntdsguid_line,
1094 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(
1095 domainControllerFunctionality)})
1097 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
1098 "POLICYGUID": policyguid,
1099 "POLICYGUID_DC": policyguid_dc,
1100 "DNSDOMAIN": names.dnsdomain,
1101 "DOMAINDN": names.domaindn})
1103 # add the NTDSGUID based SPNs
1104 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1105 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
1106 expression="", scope=ldb.SCOPE_BASE)
1107 assert isinstance(names.ntdsguid, str)
1109 # Setup fSMORoleOwner entries to point at the newly created DC entry
1110 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
1111 "DOMAINDN": names.domaindn,
1112 "CONFIGDN": names.configdn,
1113 "SCHEMADN": names.schemadn,
1114 "DEFAULTSITE": names.sitename,
1115 "SERVERDN": names.serverdn,
1116 "NETBIOSNAME": names.netbiosname,
1117 "RIDALLOCATIONSTART": str(next_rid + 100),
1118 "RIDALLOCATIONEND": str(next_rid + 100 + 499),
1121 # This is Samba4 specific and should be replaced by the correct
1122 # DNS AD-style setup
1123 setup_add_ldif(samdb, setup_path("provision_dns_add_samba.ldif"), {
1124 "DNSDOMAIN": names.dnsdomain,
1125 "DOMAINDN": names.domaindn,
1126 "DNSPASS_B64": b64encode(dnspass.encode('utf-16-le')),
1127 "HOSTNAME" : names.hostname,
1128 "DNSNAME" : '%s.%s' % (
1129 names.netbiosname.lower(), names.dnsdomain.lower())
1133 def getpolicypath(sysvolpath, dnsdomain, guid):
1134 """Return the physical path of policy given its guid.
1136 :param sysvolpath: Path to the sysvol folder
1137 :param dnsdomain: DNS name of the AD domain
1138 :param guid: The GUID of the policy
1139 :return: A string with the complete path to the policy folder
1142 if guid[0] != "{":
1143 guid = "{%s}" % guid
1144 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
1145 return policy_path
1148 def create_gpo_struct(policy_path):
1149 if not os.path.exists(policy_path):
1150 os.makedirs(policy_path, 0775)
1151 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1152 "[General]\r\nVersion=0")
1153 p = os.path.join(policy_path, "MACHINE")
1154 if not os.path.exists(p):
1155 os.makedirs(p, 0775)
1156 p = os.path.join(policy_path, "USER")
1157 if not os.path.exists(p):
1158 os.makedirs(p, 0775)
1161 def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
1162 """Create the default GPO for a domain
1164 :param sysvolpath: Physical path for the sysvol folder
1165 :param dnsdomain: DNS domain name of the AD domain
1166 :param policyguid: GUID of the default domain policy
1167 :param policyguid_dc: GUID of the default domain controler policy
1169 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
1170 create_gpo_struct(policy_path)
1172 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
1173 create_gpo_struct(policy_path)
1176 def setup_samdb(path, session_info, provision_backend, lp, names,
1177 logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
1178 adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
1179 serverrole, am_rodc=False, dom_for_fun_level=None, schema=None,
1180 next_rid=None, dc_rid=None):
1181 """Setup a complete SAM Database.
1183 :note: This will wipe the main SAM database file!
1186 if next_rid is None:
1187 next_rid = 1000
1189 # Provision does not make much sense values larger than 1000000000
1190 # as the upper range of the rIDAvailablePool is 1073741823 and
1191 # we don't want to create a domain that cannot allocate rids.
1192 if next_rid < 1000 or next_rid > 1000000000:
1193 error = "You want to run SAMBA 4 with a next_rid of %u, " % (next_rid)
1194 error += "the valid range is %u-%u. The default is %u." % (
1195 1000, 1000000000, 1000)
1196 raise ProvisioningError(error)
1198 # ATTENTION: Do NOT change these default values without discussion with the
1199 # team and/or release manager. They have a big impact on the whole program!
1200 domainControllerFunctionality = DS_DOMAIN_FUNCTION_2008_R2
1202 if dom_for_fun_level is None:
1203 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
1205 if dom_for_fun_level > domainControllerFunctionality:
1206 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!")
1208 domainFunctionality = dom_for_fun_level
1209 forestFunctionality = dom_for_fun_level
1211 # Also wipes the database
1212 setup_samdb_partitions(path, logger=logger, lp=lp,
1213 provision_backend=provision_backend, session_info=session_info,
1214 names=names, serverrole=serverrole, schema=schema)
1216 if schema is None:
1217 schema = Schema(domainsid, schemadn=names.schemadn)
1219 # Load the database, but don's load the global schema and don't connect
1220 # quite yet
1221 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
1222 credentials=provision_backend.credentials, lp=lp,
1223 global_schema=False, am_rodc=am_rodc)
1225 logger.info("Pre-loading the Samba 4 and AD schema")
1227 # Load the schema from the one we computed earlier
1228 samdb.set_schema(schema)
1230 # Set the NTDS settings DN manually - in order to have it already around
1231 # before the provisioned tree exists and we connect
1232 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1234 # And now we can connect to the DB - the schema won't be loaded from the
1235 # DB
1236 samdb.connect(path)
1238 if fill == FILL_DRS:
1239 return samdb
1241 samdb.transaction_start()
1242 try:
1243 # Set the domain functionality levels onto the database.
1244 # Various module (the password_hash module in particular) need
1245 # to know what level of AD we are emulating.
1247 # These will be fixed into the database via the database
1248 # modifictions below, but we need them set from the start.
1249 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
1250 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
1251 samdb.set_opaque_integer("domainControllerFunctionality",
1252 domainControllerFunctionality)
1254 samdb.set_domain_sid(str(domainsid))
1255 samdb.set_invocation_id(invocationid)
1257 logger.info("Adding DomainDN: %s" % names.domaindn)
1259 # impersonate domain admin
1260 admin_session_info = admin_session(lp, str(domainsid))
1261 samdb.set_session_info(admin_session_info)
1262 if domainguid is not None:
1263 domainguid_line = "objectGUID: %s\n-" % domainguid
1264 else:
1265 domainguid_line = ""
1267 descr = b64encode(get_domain_descriptor(domainsid))
1268 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1269 "DOMAINDN": names.domaindn,
1270 "DOMAINSID": str(domainsid),
1271 "DESCRIPTOR": descr,
1272 "DOMAINGUID": domainguid_line
1275 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1276 "DOMAINDN": names.domaindn,
1277 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1278 "NEXTRID": str(next_rid),
1279 "DEFAULTSITE": names.sitename,
1280 "CONFIGDN": names.configdn,
1281 "POLICYGUID": policyguid,
1282 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1283 "SAMBA_VERSION_STRING": version
1286 logger.info("Adding configuration container")
1287 descr = b64encode(get_config_descriptor(domainsid))
1288 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1289 "CONFIGDN": names.configdn,
1290 "DESCRIPTOR": descr,
1293 # Now register this container in the root of the forest
1294 msg = ldb.Message(ldb.Dn(samdb, names.domaindn))
1295 msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
1296 "subRefs")
1298 # The LDIF here was created when the Schema object was constructed
1299 logger.info("Setting up sam.ldb schema")
1300 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1301 samdb.modify_ldif(schema.schema_dn_modify)
1302 samdb.write_prefixes_from_schema()
1303 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1304 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1305 {"SCHEMADN": names.schemadn})
1307 logger.info("Reopening sam.ldb with new schema")
1308 except Exception:
1309 samdb.transaction_cancel()
1310 raise
1311 else:
1312 samdb.transaction_commit()
1314 samdb = SamDB(session_info=admin_session_info, auto_connect=False,
1315 credentials=provision_backend.credentials, lp=lp,
1316 global_schema=False, am_rodc=am_rodc)
1318 # Set the NTDS settings DN manually - in order to have it already around
1319 # before the provisioned tree exists and we connect
1320 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
1321 samdb.connect(path)
1323 samdb.transaction_start()
1324 try:
1325 samdb.invocation_id = invocationid
1327 logger.info("Setting up sam.ldb configuration data")
1328 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1329 "CONFIGDN": names.configdn,
1330 "NETBIOSNAME": names.netbiosname,
1331 "DEFAULTSITE": names.sitename,
1332 "DNSDOMAIN": names.dnsdomain,
1333 "DOMAIN": names.domain,
1334 "SCHEMADN": names.schemadn,
1335 "DOMAINDN": names.domaindn,
1336 "SERVERDN": names.serverdn,
1337 "FOREST_FUNCTIONALITY": str(forestFunctionality),
1338 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1341 logger.info("Setting up display specifiers")
1342 display_specifiers_ldif = read_ms_ldif(
1343 setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1344 display_specifiers_ldif = substitute_var(display_specifiers_ldif,
1345 {"CONFIGDN": names.configdn})
1346 check_all_substituted(display_specifiers_ldif)
1347 samdb.add_ldif(display_specifiers_ldif)
1349 logger.info("Adding users container")
1350 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1351 "DOMAINDN": names.domaindn})
1352 logger.info("Modifying users container")
1353 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1354 "DOMAINDN": names.domaindn})
1355 logger.info("Adding computers container")
1356 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1357 "DOMAINDN": names.domaindn})
1358 logger.info("Modifying computers container")
1359 setup_modify_ldif(samdb,
1360 setup_path("provision_computers_modify.ldif"), {
1361 "DOMAINDN": names.domaindn})
1362 logger.info("Setting up sam.ldb data")
1363 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1364 "CREATTIME": str(samba.unix2nttime(int(time.time()))),
1365 "DOMAINDN": names.domaindn,
1366 "NETBIOSNAME": names.netbiosname,
1367 "DEFAULTSITE": names.sitename,
1368 "CONFIGDN": names.configdn,
1369 "SERVERDN": names.serverdn,
1370 "RIDAVAILABLESTART": str(next_rid + 600),
1371 "POLICYGUID_DC": policyguid_dc
1374 setup_modify_ldif(samdb,
1375 setup_path("provision_basedn_references.ldif"), {
1376 "DOMAINDN": names.domaindn})
1378 setup_modify_ldif(samdb,
1379 setup_path("provision_configuration_references.ldif"), {
1380 "CONFIGDN": names.configdn,
1381 "SCHEMADN": names.schemadn})
1382 if fill == FILL_FULL:
1383 logger.info("Setting up sam.ldb users and groups")
1384 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1385 "DOMAINDN": names.domaindn,
1386 "DOMAINSID": str(domainsid),
1387 "CONFIGDN": names.configdn,
1388 "ADMINPASS_B64": b64encode(adminpass.encode('utf-16-le')),
1389 "KRBTGTPASS_B64": b64encode(krbtgtpass.encode('utf-16-le'))
1392 logger.info("Setting up self join")
1393 setup_self_join(samdb, names=names, invocationid=invocationid,
1394 dnspass=dnspass,
1395 machinepass=machinepass,
1396 domainsid=domainsid,
1397 next_rid=next_rid,
1398 dc_rid=dc_rid,
1399 policyguid=policyguid,
1400 policyguid_dc=policyguid_dc,
1401 domainControllerFunctionality=domainControllerFunctionality,
1402 ntdsguid=ntdsguid)
1404 ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
1405 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1406 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1407 assert isinstance(names.ntdsguid, str)
1408 except Exception:
1409 samdb.transaction_cancel()
1410 raise
1411 else:
1412 samdb.transaction_commit()
1413 return samdb
1416 FILL_FULL = "FULL"
1417 FILL_NT4SYNC = "NT4SYNC"
1418 FILL_DRS = "DRS"
1419 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1420 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)"
1423 def set_dir_acl(path, acl, lp, domsid):
1424 setntacl(lp, path, acl, domsid)
1425 for root, dirs, files in os.walk(path, topdown=False):
1426 for name in files:
1427 setntacl(lp, os.path.join(root, name), acl, domsid)
1428 for name in dirs:
1429 setntacl(lp, os.path.join(root, name), acl, domsid)
1432 def set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1433 """Set ACL on the sysvol/<dnsname>/Policies folder and the policy
1434 folders beneath.
1436 :param sysvol: Physical path for the sysvol folder
1437 :param dnsdomain: The DNS name of the domain
1438 :param domainsid: The SID of the domain
1439 :param domaindn: The DN of the domain (ie. DC=...)
1440 :param samdb: An LDB object on the SAM db
1441 :param lp: an LP object
1444 # Set ACL for GPO root folder
1445 root_policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1446 setntacl(lp, root_policy_path, POLICIES_ACL, str(domainsid))
1448 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1449 attrs=["cn", "nTSecurityDescriptor"],
1450 expression="", scope=ldb.SCOPE_ONELEVEL)
1452 for policy in res:
1453 acl = ndr_unpack(security.descriptor,
1454 str(policy["nTSecurityDescriptor"])).as_sddl()
1455 policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"]))
1456 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1457 str(domainsid))
1460 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1461 lp):
1462 """Set the ACL for the sysvol share and the subfolders
1464 :param samdb: An LDB object on the SAM db
1465 :param netlogon: Physical path for the netlogon folder
1466 :param sysvol: Physical path for the sysvol folder
1467 :param gid: The GID of the "Domain adminstrators" group
1468 :param domainsid: The SID of the domain
1469 :param dnsdomain: The DNS name of the domain
1470 :param domaindn: The DN of the domain (ie. DC=...)
1473 try:
1474 os.chown(sysvol, -1, gid)
1475 except OSError:
1476 canchown = False
1477 else:
1478 canchown = True
1480 # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level)
1481 setntacl(lp,sysvol, SYSVOL_ACL, str(domainsid))
1482 for root, dirs, files in os.walk(sysvol, topdown=False):
1483 for name in files:
1484 if canchown:
1485 os.chown(os.path.join(root, name), -1, gid)
1486 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1487 for name in dirs:
1488 if canchown:
1489 os.chown(os.path.join(root, name), -1, gid)
1490 setntacl(lp, os.path.join(root, name), SYSVOL_ACL, str(domainsid))
1492 # Set acls on Policy folder and policies folders
1493 set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp)
1496 def interface_ips_v4(lp):
1497 '''return only IPv4 IPs'''
1498 ips = samba.interface_ips(lp, False)
1499 ret = []
1500 for i in ips:
1501 if i.find(':') == -1:
1502 ret.append(i)
1503 return ret
1505 def interface_ips_v6(lp, linklocal=False):
1506 '''return only IPv6 IPs'''
1507 ips = samba.interface_ips(lp, False)
1508 ret = []
1509 for i in ips:
1510 if i.find(':') != -1 and (linklocal or i.find('%') == -1):
1511 ret.append(i)
1512 return ret
1515 def provision(logger, session_info, credentials, smbconf=None,
1516 targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
1517 domaindn=None, schemadn=None, configdn=None, serverdn=None,
1518 domain=None, hostname=None, hostip=None, hostip6=None, domainsid=None,
1519 next_rid=1000, dc_rid=None, adminpass=None, ldapadminpass=None, krbtgtpass=None,
1520 domainguid=None, policyguid=None, policyguid_dc=None,
1521 invocationid=None, machinepass=None, ntdsguid=None,
1522 dns_backend=None, dnspass=None,
1523 root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
1524 serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
1525 ldap_backend_forced_uri=None, backend_type=None, sitename=None,
1526 ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
1527 nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
1528 lp=None):
1529 """Provision samba4
1531 :note: caution, this wipes all existing data!
1534 if domainsid is None:
1535 domainsid = security.random_sid()
1536 else:
1537 domainsid = security.dom_sid(domainsid)
1539 # create/adapt the group policy GUIDs
1540 # Default GUID for default policy are described at
1541 # "How Core Group Policy Works"
1542 # http://technet.microsoft.com/en-us/library/cc784268%28WS.10%29.aspx
1543 if policyguid is None:
1544 policyguid = DEFAULT_POLICY_GUID
1545 policyguid = policyguid.upper()
1546 if policyguid_dc is None:
1547 policyguid_dc = DEFAULT_DC_POLICY_GUID
1548 policyguid_dc = policyguid_dc.upper()
1550 if adminpass is None:
1551 adminpass = samba.generate_random_password(12, 32)
1552 if krbtgtpass is None:
1553 krbtgtpass = samba.generate_random_password(128, 255)
1554 if machinepass is None:
1555 machinepass = samba.generate_random_password(128, 255)
1556 if dnspass is None:
1557 dnspass = samba.generate_random_password(128, 255)
1558 if ldapadminpass is None:
1559 # Make a new, random password between Samba and it's LDAP server
1560 ldapadminpass=samba.generate_random_password(128, 255)
1562 if backend_type is None:
1563 backend_type = "ldb"
1565 sid_generator = "internal"
1566 if backend_type == "fedora-ds":
1567 sid_generator = "backend"
1569 root_uid = findnss_uid([root or "root"])
1570 nobody_uid = findnss_uid([nobody or "nobody"])
1571 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1572 if wheel is None:
1573 wheel_gid = findnss_gid(["wheel", "adm"])
1574 else:
1575 wheel_gid = findnss_gid([wheel])
1576 try:
1577 bind_gid = findnss_gid(["bind", "named"])
1578 except KeyError:
1579 bind_gid = None
1581 if targetdir is not None:
1582 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1583 elif smbconf is None:
1584 smbconf = samba.param.default_path()
1585 if not os.path.exists(os.path.dirname(smbconf)):
1586 os.makedirs(os.path.dirname(smbconf))
1588 # only install a new smb.conf if there isn't one there already
1589 if os.path.exists(smbconf):
1590 # if Samba Team members can't figure out the weird errors
1591 # loading an empty smb.conf gives, then we need to be smarter.
1592 # Pretend it just didn't exist --abartlet
1593 data = open(smbconf, 'r').read()
1594 data = data.lstrip()
1595 if data is None or data == "":
1596 make_smbconf(smbconf, hostname, domain, realm,
1597 serverrole, targetdir, sid_generator, useeadb,
1598 lp=lp)
1599 else:
1600 make_smbconf(smbconf, hostname, domain, realm, serverrole,
1601 targetdir, sid_generator, useeadb, lp=lp)
1603 if lp is None:
1604 lp = samba.param.LoadParm()
1605 lp.load(smbconf)
1606 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1607 dnsdomain=realm, serverrole=serverrole, domaindn=domaindn,
1608 configdn=configdn, schemadn=schemadn, serverdn=serverdn,
1609 sitename=sitename, rootdn=rootdn)
1610 paths = provision_paths_from_lp(lp, names.dnsdomain)
1612 paths.bind_gid = bind_gid
1614 if hostip is None:
1615 logger.info("Looking up IPv4 addresses")
1616 hostips = interface_ips_v4(lp)
1617 if len(hostips) > 0:
1618 hostip = hostips[0]
1619 if len(hostips) > 1:
1620 logger.warning("More than one IPv4 address found. Using %s",
1621 hostip)
1622 if hostip == "127.0.0.1":
1623 hostip = None
1624 if hostip is None:
1625 logger.warning("No IPv4 address will be assigned")
1627 if hostip6 is None:
1628 logger.info("Looking up IPv6 addresses")
1629 hostips = interface_ips_v6(lp, linklocal=False)
1630 if hostips:
1631 hostip6 = hostips[0]
1632 if len(hostips) > 1:
1633 logger.warning("More than one IPv6 address found. Using %s", hostip6)
1634 if hostip6 is None:
1635 logger.warning("No IPv6 address will be assigned")
1637 if serverrole is None:
1638 serverrole = lp.get("server role")
1640 assert serverrole in ("domain controller", "member server", "standalone")
1641 if invocationid is None:
1642 invocationid = str(uuid.uuid4())
1644 if not os.path.exists(paths.private_dir):
1645 os.mkdir(paths.private_dir)
1646 if not os.path.exists(os.path.join(paths.private_dir, "tls")):
1647 os.mkdir(os.path.join(paths.private_dir, "tls"))
1649 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1651 schema = Schema(domainsid, invocationid=invocationid,
1652 schemadn=names.schemadn)
1654 if backend_type == "ldb":
1655 provision_backend = LDBBackend(backend_type, paths=paths,
1656 lp=lp, credentials=credentials,
1657 names=names, logger=logger)
1658 elif backend_type == "existing":
1659 provision_backend = ExistingBackend(backend_type, paths=paths,
1660 lp=lp, credentials=credentials,
1661 names=names, logger=logger,
1662 ldap_backend_forced_uri=ldap_backend_forced_uri)
1663 elif backend_type == "fedora-ds":
1664 provision_backend = FDSBackend(backend_type, paths=paths,
1665 lp=lp, credentials=credentials,
1666 names=names, logger=logger, domainsid=domainsid,
1667 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1668 slapd_path=slapd_path,
1669 ldap_backend_extra_port=ldap_backend_extra_port,
1670 ldap_dryrun_mode=ldap_dryrun_mode, root=root,
1671 setup_ds_path=setup_ds_path,
1672 ldap_backend_forced_uri=ldap_backend_forced_uri)
1673 elif backend_type == "openldap":
1674 provision_backend = OpenLDAPBackend(backend_type, paths=paths,
1675 lp=lp, credentials=credentials,
1676 names=names, logger=logger, domainsid=domainsid,
1677 schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
1678 slapd_path=slapd_path,
1679 ldap_backend_extra_port=ldap_backend_extra_port,
1680 ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
1681 nosync=nosync,
1682 ldap_backend_forced_uri=ldap_backend_forced_uri)
1683 else:
1684 raise ValueError("Unknown LDAP backend type selected")
1686 provision_backend.init()
1687 provision_backend.start()
1689 # only install a new shares config db if there is none
1690 if not os.path.exists(paths.shareconf):
1691 logger.info("Setting up share.ldb")
1692 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1693 lp=lp)
1694 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1696 logger.info("Setting up secrets.ldb")
1697 secrets_ldb = setup_secretsdb(paths,
1698 session_info=session_info,
1699 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1701 try:
1702 logger.info("Setting up the registry")
1703 setup_registry(paths.hklm, session_info,
1704 lp=lp)
1706 logger.info("Setting up the privileges database")
1707 setup_privileges(paths.privilege, session_info, lp=lp)
1709 logger.info("Setting up idmap db")
1710 idmap = setup_idmapdb(paths.idmapdb,
1711 session_info=session_info, lp=lp)
1713 logger.info("Setting up SAM db")
1714 samdb = setup_samdb(paths.samdb, session_info,
1715 provision_backend, lp, names, logger=logger,
1716 domainsid=domainsid, schema=schema, domainguid=domainguid,
1717 policyguid=policyguid, policyguid_dc=policyguid_dc,
1718 fill=samdb_fill, adminpass=adminpass, krbtgtpass=krbtgtpass,
1719 invocationid=invocationid, machinepass=machinepass,
1720 dnspass=dnspass, ntdsguid=ntdsguid, serverrole=serverrole,
1721 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
1722 next_rid=next_rid, dc_rid=dc_rid)
1724 if serverrole == "domain controller":
1725 if paths.netlogon is None:
1726 logger.info("Existing smb.conf does not have a [netlogon] share, but you 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.netlogon is not None
1731 if paths.sysvol is None:
1732 logger.info("Existing smb.conf does not have a [sysvol] share, but you"
1733 " are configuring a DC.")
1734 logger.info("Please either remove %s or see the template at %s" %
1735 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1736 assert paths.sysvol is not None
1738 if not os.path.isdir(paths.netlogon):
1739 os.makedirs(paths.netlogon, 0755)
1741 if samdb_fill == FILL_FULL:
1742 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1743 root_uid=root_uid, nobody_uid=nobody_uid,
1744 users_gid=users_gid, wheel_gid=wheel_gid)
1746 if serverrole == "domain controller":
1747 # Set up group policies (domain policy and domain controller
1748 # policy)
1749 create_default_gpo(paths.sysvol, names.dnsdomain, policyguid,
1750 policyguid_dc)
1751 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1752 domainsid, names.dnsdomain, names.domaindn, lp)
1754 logger.info("Setting up sam.ldb rootDSE marking as synchronized")
1755 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"),
1756 { 'NTDSGUID' : names.ntdsguid })
1758 secretsdb_self_join(secrets_ldb, domain=names.domain,
1759 realm=names.realm, dnsdomain=names.dnsdomain,
1760 netbiosname=names.netbiosname, domainsid=domainsid,
1761 machinepass=machinepass, secure_channel_type=SEC_CHAN_BDC)
1763 # Now set up the right msDS-SupportedEncryptionTypes into the DB
1764 # In future, this might be determined from some configuration
1765 kerberos_enctypes = str(ENC_ALL_TYPES)
1767 try:
1768 msg = ldb.Message(ldb.Dn(samdb,
1769 samdb.searchone("distinguishedName",
1770 expression="samAccountName=%s$" % names.netbiosname,
1771 scope=ldb.SCOPE_SUBTREE)))
1772 msg["msDS-SupportedEncryptionTypes"] = ldb.MessageElement(
1773 elements=kerberos_enctypes, flags=ldb.FLAG_MOD_REPLACE,
1774 name="msDS-SupportedEncryptionTypes")
1775 samdb.modify(msg)
1776 except ldb.LdbError, (enum, estr):
1777 if enum != ldb.ERR_NO_SUCH_ATTRIBUTE:
1778 # It might be that this attribute does not exist in this schema
1779 raise
1781 if serverrole == "domain controller":
1782 secretsdb_setup_dns(secrets_ldb, names,
1783 paths.private_dir, realm=names.realm,
1784 dnsdomain=names.dnsdomain,
1785 dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
1787 setup_ad_dns(samdb, names, logger, hostip=hostip, hostip6=hostip6,
1788 dns_backend=dns_backend, os_level=dom_for_fun_level)
1790 domainguid = samdb.searchone(basedn=domaindn,
1791 attribute="objectGUID")
1792 assert isinstance(domainguid, str)
1794 # Only make a zone file on the first DC, it should be
1795 # replicated with DNS replication
1796 create_zone_file(lp, logger, paths, targetdir,
1797 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1798 hostname=names.hostname, realm=names.realm,
1799 domainguid=domainguid, ntdsguid=names.ntdsguid)
1801 create_named_conf(paths, realm=names.realm,
1802 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1804 create_named_txt(paths.namedtxt,
1805 realm=names.realm, dnsdomain=names.dnsdomain,
1806 dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
1807 private_dir=paths.private_dir,
1808 keytab_name=paths.dns_keytab)
1809 logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
1810 logger.info("and %s for further documentation required for secure DNS "
1811 "updates", paths.namedtxt)
1813 lastProvisionUSNs = get_last_provision_usn(samdb)
1814 maxUSN = get_max_usn(samdb, str(names.rootdn))
1815 if lastProvisionUSNs is not None:
1816 update_provision_usn(samdb, 0, maxUSN, invocationid, 1)
1817 else:
1818 set_provision_usn(samdb, 0, maxUSN, invocationid)
1820 create_krb5_conf(paths.krb5conf,
1821 dnsdomain=names.dnsdomain, hostname=names.hostname,
1822 realm=names.realm)
1823 logger.info("A Kerberos configuration suitable for Samba 4 has been "
1824 "generated at %s", paths.krb5conf)
1826 if serverrole == "domain controller":
1827 create_dns_update_list(lp, logger, paths)
1829 provision_backend.post_setup()
1830 provision_backend.shutdown()
1832 create_phpldapadmin_config(paths.phpldapadminconfig,
1833 ldapi_url)
1834 except Exception:
1835 secrets_ldb.transaction_cancel()
1836 raise
1838 # Now commit the secrets.ldb to disk
1839 secrets_ldb.transaction_commit()
1841 # the commit creates the dns.keytab, now chown it
1842 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1843 if os.path.isfile(dns_keytab_path) and paths.bind_gid is not None:
1844 try:
1845 os.chmod(dns_keytab_path, 0640)
1846 os.chown(dns_keytab_path, -1, paths.bind_gid)
1847 except OSError:
1848 if not os.environ.has_key('SAMBA_SELFTEST'):
1849 logger.info("Failed to chown %s to bind gid %u",
1850 dns_keytab_path, paths.bind_gid)
1852 if samdb_fill != FILL_DRS:
1853 # fix any dangling GUIDs from the provision
1854 logger.info("Fixing provision GUIDs")
1855 chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True, quiet=True)
1856 samdb.transaction_start()
1857 # a small number of GUIDs are missing because of ordering issues in the
1858 # provision code
1859 for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
1860 chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
1861 scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
1862 chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
1863 scope=ldb.SCOPE_ONELEVEL,
1864 attrs=['ipsecOwnersReference',
1865 'ipsecFilterReference',
1866 'ipsecISAKMPReference',
1867 'ipsecNegotiationPolicyReference',
1868 'ipsecNFAReference'])
1869 samdb.transaction_commit()
1872 logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
1873 paths.phpldapadminconfig)
1875 logger.info("Once the above files are installed, your Samba4 server will be ready to use")
1876 logger.info("Server Role: %s" % serverrole)
1877 logger.info("Hostname: %s" % names.hostname)
1878 logger.info("NetBIOS Domain: %s" % names.domain)
1879 logger.info("DNS Domain: %s" % names.dnsdomain)
1880 logger.info("DOMAIN SID: %s" % str(domainsid))
1881 if samdb_fill == FILL_FULL:
1882 logger.info("Admin password: %s" % adminpass)
1883 if provision_backend.type is not "ldb":
1884 if provision_backend.credentials.get_bind_dn() is not None:
1885 logger.info("LDAP Backend Admin DN: %s" %
1886 provision_backend.credentials.get_bind_dn())
1887 else:
1888 logger.info("LDAP Admin User: %s" %
1889 provision_backend.credentials.get_username())
1891 logger.info("LDAP Admin Password: %s" %
1892 provision_backend.credentials.get_password())
1894 if provision_backend.slapd_command_escaped is not None:
1895 # now display slapd_command_file.txt to show how slapd must be
1896 # started next time
1897 logger.info("Use later the following commandline to start slapd, then Samba:")
1898 logger.info(provision_backend.slapd_command_escaped)
1899 logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
1900 provision_backend.ldapdir)
1902 result = ProvisionResult()
1903 result.domaindn = domaindn
1904 result.paths = paths
1905 result.lp = lp
1906 result.samdb = samdb
1907 result.idmap = idmap
1908 return result
1911 def provision_become_dc(smbconf=None, targetdir=None,
1912 realm=None, rootdn=None, domaindn=None, schemadn=None, configdn=None,
1913 serverdn=None, domain=None, hostname=None, domainsid=None,
1914 adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
1915 policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
1916 dns_backend=None, root=None, nobody=None, users=None, wheel=None, backup=None,
1917 serverrole=None, ldap_backend=None, ldap_backend_type=None,
1918 sitename=None, debuglevel=1):
1920 logger = logging.getLogger("provision")
1921 samba.set_debug_level(debuglevel)
1923 res = provision(logger, system_session(), None,
1924 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1925 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1926 configdn=configdn, serverdn=serverdn, domain=domain,
1927 hostname=hostname, hostip=None, domainsid=domainsid,
1928 machinepass=machinepass, serverrole="domain controller",
1929 sitename=sitename, dns_backend=dns_backend, dnspass=dnspass)
1930 res.lp.set("debuglevel", str(debuglevel))
1931 return res
1934 def create_phpldapadmin_config(path, ldapi_uri):
1935 """Create a PHP LDAP admin configuration file.
1937 :param path: Path to write the configuration to.
1939 setup_file(setup_path("phpldapadmin-config.php"), path,
1940 {"S4_LDAPI_URI": ldapi_uri})
1943 def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
1944 hostip, hostip6, hostname, realm, domainguid,
1945 ntdsguid):
1946 """Write out a DNS zone file, from the info in the current database.
1948 :param paths: paths object
1949 :param dnsdomain: DNS Domain name
1950 :param domaindn: DN of the Domain
1951 :param hostip: Local IPv4 IP
1952 :param hostip6: Local IPv6 IP
1953 :param hostname: Local hostname
1954 :param realm: Realm name
1955 :param domainguid: GUID of the domain.
1956 :param ntdsguid: GUID of the hosts nTDSDSA record.
1958 assert isinstance(domainguid, str)
1960 if hostip6 is not None:
1961 hostip6_base_line = " IN AAAA " + hostip6
1962 hostip6_host_line = hostname + " IN AAAA " + hostip6
1963 gc_msdcs_ip6_line = "gc._msdcs IN AAAA " + hostip6
1964 else:
1965 hostip6_base_line = ""
1966 hostip6_host_line = ""
1967 gc_msdcs_ip6_line = ""
1969 if hostip is not None:
1970 hostip_base_line = " IN A " + hostip
1971 hostip_host_line = hostname + " IN A " + hostip
1972 gc_msdcs_ip_line = "gc._msdcs IN A " + hostip
1973 else:
1974 hostip_base_line = ""
1975 hostip_host_line = ""
1976 gc_msdcs_ip_line = ""
1978 dns_dir = os.path.dirname(paths.dns)
1980 try:
1981 shutil.rmtree(dns_dir, True)
1982 except OSError:
1983 pass
1985 os.mkdir(dns_dir, 0775)
1987 # we need to freeze the zone while we update the contents
1988 if targetdir is None:
1989 rndc = ' '.join(lp.get("rndc command"))
1990 os.system(rndc + " freeze " + lp.get("realm"))
1992 setup_file(setup_path("provision.zone"), paths.dns, {
1993 "HOSTNAME": hostname,
1994 "DNSDOMAIN": dnsdomain,
1995 "REALM": realm,
1996 "HOSTIP_BASE_LINE": hostip_base_line,
1997 "HOSTIP_HOST_LINE": hostip_host_line,
1998 "DOMAINGUID": domainguid,
1999 "DATESTRING": time.strftime("%Y%m%d%H"),
2000 "DEFAULTSITE": DEFAULTSITE,
2001 "NTDSGUID": ntdsguid,
2002 "HOSTIP6_BASE_LINE": hostip6_base_line,
2003 "HOSTIP6_HOST_LINE": hostip6_host_line,
2004 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
2005 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
2008 # note that we use no variable substitution on this file
2009 # the substitution is done at runtime by samba_dnsupdate
2010 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2012 # and the SPN update list
2013 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2015 if paths.bind_gid is not None:
2016 try:
2017 os.chown(dns_dir, -1, paths.bind_gid)
2018 os.chown(paths.dns, -1, paths.bind_gid)
2019 # chmod needed to cope with umask
2020 os.chmod(dns_dir, 0775)
2021 os.chmod(paths.dns, 0664)
2022 except OSError:
2023 if not os.environ.has_key('SAMBA_SELFTEST'):
2024 logger.error("Failed to chown %s to bind gid %u" % (
2025 dns_dir, paths.bind_gid))
2027 if targetdir is None:
2028 os.system(rndc + " unfreeze " + lp.get("realm"))
2031 def create_dns_update_list(lp, logger, paths):
2032 """Write out a dns_update_list file"""
2033 # note that we use no variable substitution on this file
2034 # the substitution is done at runtime by samba_dnsupdate
2035 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
2036 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
2039 def create_named_conf(paths, realm, dnsdomain,
2040 private_dir):
2041 """Write out a file containing zone statements suitable for inclusion in a
2042 named.conf file (including GSS-TSIG configuration).
2044 :param paths: all paths
2045 :param realm: Realm name
2046 :param dnsdomain: DNS Domain name
2047 :param private_dir: Path to private directory
2048 :param keytab_name: File name of DNS keytab file
2051 setup_file(setup_path("named.conf"), paths.namedconf, {
2052 "DNSDOMAIN": dnsdomain,
2053 "REALM": realm,
2054 "ZONE_FILE": paths.dns,
2055 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2056 "NAMED_CONF": paths.namedconf,
2057 "NAMED_CONF_UPDATE": paths.namedconf_update
2060 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
2063 def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
2064 keytab_name):
2065 """Write out a file containing zone statements suitable for inclusion in a
2066 named.conf file (including GSS-TSIG configuration).
2068 :param path: Path of the new named.conf file.
2069 :param realm: Realm name
2070 :param dnsdomain: DNS Domain name
2071 :param private_dir: Path to private directory
2072 :param keytab_name: File name of DNS keytab file
2074 setup_file(setup_path("named.txt"), path, {
2075 "DNSDOMAIN": dnsdomain,
2076 "DNSNAME" : dnsname,
2077 "REALM": realm,
2078 "DNS_KEYTAB": keytab_name,
2079 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2080 "PRIVATE_DIR": private_dir
2084 def create_krb5_conf(path, dnsdomain, hostname, realm):
2085 """Write out a file containing zone statements suitable for inclusion in a
2086 named.conf file (including GSS-TSIG configuration).
2088 :param path: Path of the new named.conf file.
2089 :param dnsdomain: DNS Domain name
2090 :param hostname: Local hostname
2091 :param realm: Realm name
2093 setup_file(setup_path("krb5.conf"), path, {
2094 "DNSDOMAIN": dnsdomain,
2095 "HOSTNAME": hostname,
2096 "REALM": realm,
2100 class ProvisioningError(Exception):
2101 """A generic provision error."""
2103 def __init__(self, value):
2104 self.value = value
2106 def __str__(self):
2107 return "ProvisioningError: " + self.value
2110 class InvalidNetbiosName(Exception):
2111 """A specified name was not a valid NetBIOS name."""
2112 def __init__(self, name):
2113 super(InvalidNetbiosName, self).__init__(
2114 "The name '%r' is not a valid NetBIOS name" % name)