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