provision: Remove unnecessary whitespace.
[Samba/ekacnet.git] / source4 / scripting / python / samba / provision.py
blob9e7f1372c2756848af62d0e85c38ce8abbe422c6
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
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 from base64 import b64encode
29 import os
30 import pwd
31 import grp
32 import time
33 import uuid
34 import socket
35 import param
36 import registry
37 import urllib
38 import shutil
40 import ldb
42 from samba.auth import system_session, admin_session
43 from samba import glue, version, Ldb, substitute_var, valid_netbios_name
44 from samba import check_all_substituted, read_and_sub_file, setup_file
45 from samba import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008
46 from samba.dcerpc import security
47 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
48 from samba.idmap import IDmapDB
49 from samba.ntacls import setntacl, dsacl2fsacl
50 from samba.ndr import ndr_pack,ndr_unpack
51 from samba.schema import Schema
52 from ms_display_specifiers import read_ms_ldif
53 from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
54 from provisionexceptions import ProvisioningError, InvalidNetbiosName
56 __docformat__ = "restructuredText"
58 def find_setup_dir():
59 """Find the setup directory used by provision."""
60 dirname = os.path.dirname(__file__)
61 if "/site-packages/" in dirname:
62 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
63 for suffix in ["share/setup", "share/samba/setup", "setup"]:
64 ret = os.path.join(prefix, suffix)
65 if os.path.isdir(ret):
66 return ret
67 # In source tree
68 ret = os.path.join(dirname, "../../../setup")
69 if os.path.isdir(ret):
70 return ret
71 raise Exception("Unable to find setup directory.")
73 # descriptors of the naming contexts
74 # hard coded at this point, but will probably be changed when
75 # we enable different fsmo roles
77 def get_config_descriptor(domain_sid):
78 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
79 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
85 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
86 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
87 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
88 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
89 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
90 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
91 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
92 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
93 sec = security.descriptor.from_sddl(sddl, domain_sid)
94 return ndr_pack(sec)
96 def get_domain_descriptor(domain_sid):
97 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
98 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
99 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
100 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
101 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
102 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
103 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
104 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
105 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
106 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
107 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
108 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
109 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
110 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
111 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
112 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
113 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
114 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
119 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
120 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
121 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
122 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
123 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
125 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
126 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
127 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
128 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
129 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
133 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
134 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
135 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
136 "(A;;RPRC;;;RU)" \
137 "(A;CI;LC;;;RU)" \
138 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
139 "(A;;RP;;;WD)" \
140 "(A;;RPLCLORC;;;ED)" \
141 "(A;;RPLCLORC;;;AU)" \
142 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
143 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
144 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
146 sec = security.descriptor.from_sddl(sddl, domain_sid)
147 return ndr_pack(sec)
149 DEFAULTSITE = "Default-First-Site-Name"
151 # Exception classes
153 class ProvisionPaths(object):
154 def __init__(self):
155 self.shareconf = None
156 self.hklm = None
157 self.hkcu = None
158 self.hkcr = None
159 self.hku = None
160 self.hkpd = None
161 self.hkpt = None
162 self.samdb = None
163 self.idmapdb = None
164 self.secrets = None
165 self.keytab = None
166 self.dns_keytab = None
167 self.dns = None
168 self.winsdb = None
169 self.private_dir = None
172 class ProvisionNames(object):
173 def __init__(self):
174 self.rootdn = None
175 self.domaindn = None
176 self.configdn = None
177 self.schemadn = None
178 self.ldapmanagerdn = None
179 self.dnsdomain = None
180 self.realm = None
181 self.netbiosname = None
182 self.domain = None
183 self.hostname = None
184 self.sitename = None
185 self.smbconf = None
188 class ProvisionResult(object):
189 def __init__(self):
190 self.paths = None
191 self.domaindn = None
192 self.lp = None
193 self.samdb = None
195 def check_install(lp, session_info, credentials):
196 """Check whether the current install seems ok.
198 :param lp: Loadparm context
199 :param session_info: Session information
200 :param credentials: Credentials
202 if lp.get("realm") == "":
203 raise Exception("Realm empty")
204 ldb = Ldb(lp.get("sam database"), session_info=session_info,
205 credentials=credentials, lp=lp)
206 if len(ldb.search("(cn=Administrator)")) != 1:
207 raise ProvisioningError("No administrator account found")
210 def findnss(nssfn, names):
211 """Find a user or group from a list of possibilities.
213 :param nssfn: NSS Function to try (should raise KeyError if not found)
214 :param names: Names to check.
215 :return: Value return by first names list.
217 for name in names:
218 try:
219 return nssfn(name)
220 except KeyError:
221 pass
222 raise KeyError("Unable to find user/group in %r" % names)
225 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
226 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
229 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
230 """Setup a ldb in the private dir.
232 :param ldb: LDB file to import data into
233 :param ldif_path: Path of the LDIF file to load
234 :param subst_vars: Optional variables to subsitute in LDIF.
235 :param nocontrols: Optional list of controls, can be None for no controls
237 assert isinstance(ldif_path, str)
238 data = read_and_sub_file(ldif_path, subst_vars)
239 ldb.add_ldif(data, controls)
242 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
243 """Modify a ldb in the private dir.
245 :param ldb: LDB object.
246 :param ldif_path: LDIF file path.
247 :param subst_vars: Optional dictionary with substitution variables.
249 data = read_and_sub_file(ldif_path, subst_vars)
250 ldb.modify_ldif(data)
253 def setup_ldb(ldb, ldif_path, subst_vars):
254 """Import a LDIF a file into a LDB handle, optionally substituting variables.
256 :note: Either all LDIF data will be added or none (using transactions).
258 :param ldb: LDB file to import into.
259 :param ldif_path: Path to the LDIF file.
260 :param subst_vars: Dictionary with substitution variables.
262 assert ldb is not None
263 ldb.transaction_start()
264 try:
265 setup_add_ldif(ldb, ldif_path, subst_vars)
266 except:
267 ldb.transaction_cancel()
268 raise
269 ldb.transaction_commit()
272 def provision_paths_from_lp(lp, dnsdomain):
273 """Set the default paths for provisioning.
275 :param lp: Loadparm context.
276 :param dnsdomain: DNS Domain name
278 paths = ProvisionPaths()
279 paths.private_dir = lp.get("private dir")
280 paths.dns_keytab = "dns.keytab"
282 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
283 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
284 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
285 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
286 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
287 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
288 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
289 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
290 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
291 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
292 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
293 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
294 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
295 paths.phpldapadminconfig = os.path.join(paths.private_dir,
296 "phpldapadmin-config.php")
297 paths.hklm = "hklm.ldb"
298 paths.hkcr = "hkcr.ldb"
299 paths.hkcu = "hkcu.ldb"
300 paths.hku = "hku.ldb"
301 paths.hkpd = "hkpd.ldb"
302 paths.hkpt = "hkpt.ldb"
303 paths.sysvol = lp.get("path", "sysvol")
304 paths.netlogon = lp.get("path", "netlogon")
305 paths.smbconf = lp.configfile
306 return paths
309 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
310 serverrole=None, rootdn=None, domaindn=None, configdn=None,
311 schemadn=None, serverdn=None, sitename=None):
312 """Guess configuration settings to use."""
314 if hostname is None:
315 hostname = socket.gethostname().split(".")[0]
317 netbiosname = lp.get("netbios name")
318 if netbiosname is None:
319 netbiosname = hostname
320 assert netbiosname is not None
321 netbiosname = netbiosname.upper()
322 if not valid_netbios_name(netbiosname):
323 raise InvalidNetbiosName(netbiosname)
325 if dnsdomain is None:
326 dnsdomain = lp.get("realm")
327 if dnsdomain is None or dnsdomain == "":
328 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
330 dnsdomain = dnsdomain.lower()
332 if serverrole is None:
333 serverrole = lp.get("server role")
334 if serverrole is None:
335 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
337 serverrole = serverrole.lower()
339 realm = dnsdomain.upper()
341 if lp.get("realm") == "":
342 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
344 if lp.get("realm").upper() != realm:
345 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))
347 if lp.get("server role").lower() != serverrole:
348 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").upper(), serverrole, lp.configfile))
350 if serverrole == "domain controller":
351 if domain is None:
352 # This will, for better or worse, default to 'WORKGROUP'
353 domain = lp.get("workgroup")
354 domain = domain.upper()
356 if lp.get("workgroup").upper() != domain:
357 raise ProvisioningError("guess_names: Workgroup '%s' in %s must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
359 if domaindn is None:
360 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
361 else:
362 domain = netbiosname
363 if domaindn is None:
364 domaindn = "DC=" + netbiosname
366 if not valid_netbios_name(domain):
367 raise InvalidNetbiosName(domain)
369 if hostname.upper() == realm:
370 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
371 if netbiosname == realm:
372 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
373 if domain == realm:
374 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
376 if rootdn is None:
377 rootdn = domaindn
379 if configdn is None:
380 configdn = "CN=Configuration," + rootdn
381 if schemadn is None:
382 schemadn = "CN=Schema," + configdn
384 if sitename is None:
385 sitename=DEFAULTSITE
387 names = ProvisionNames()
388 names.rootdn = rootdn
389 names.domaindn = domaindn
390 names.configdn = configdn
391 names.schemadn = schemadn
392 names.ldapmanagerdn = "CN=Manager," + rootdn
393 names.dnsdomain = dnsdomain
394 names.domain = domain
395 names.realm = realm
396 names.netbiosname = netbiosname
397 names.hostname = hostname
398 names.sitename = sitename
399 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
401 return names
404 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
405 targetdir, sid_generator,eadb):
406 """Create a new smb.conf file based on a couple of basic settings.
408 assert smbconf is not None
409 if hostname is None:
410 hostname = socket.gethostname().split(".")[0]
411 netbiosname = hostname.upper()
413 if serverrole is None:
414 serverrole = "standalone"
416 assert serverrole in ("domain controller", "member server", "standalone")
417 if serverrole == "domain controller":
418 smbconfsuffix = "dc"
419 elif serverrole == "member server":
420 smbconfsuffix = "member"
421 elif serverrole == "standalone":
422 smbconfsuffix = "standalone"
424 if sid_generator is None:
425 sid_generator = "internal"
427 assert domain is not None
428 domain = domain.upper()
430 assert realm is not None
431 realm = realm.upper()
433 default_lp = param.LoadParm()
434 #Load non-existant file
435 if os.path.exists(smbconf):
436 default_lp.load(smbconf)
437 if eadb:
438 if targetdir is not None:
439 privdir = os.path.join(targetdir, "private")
440 else:
441 privdir = default_lp.get("private dir")
442 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
443 else:
444 posixeadb_line = ""
446 if targetdir is not None:
447 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
448 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
450 default_lp.set("lock dir", os.path.abspath(targetdir))
451 else:
452 privatedir_line = ""
453 lockdir_line = ""
455 if sid_generator == "internal":
456 sid_generator_line = ""
457 else:
458 sid_generator_line = "sid generator = " + sid_generator
460 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
461 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
463 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
464 smbconf, {
465 "NETBIOS_NAME": netbiosname,
466 "DOMAIN": domain,
467 "REALM": realm,
468 "SERVERROLE": serverrole,
469 "NETLOGONPATH": netlogon,
470 "SYSVOLPATH": sysvol,
471 "SIDGENERATOR_LINE": sid_generator_line,
472 "PRIVATEDIR_LINE": privatedir_line,
473 "LOCKDIR_LINE": lockdir_line,
474 "POSIXEADB_LINE": posixeadb_line
478 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
479 users_gid, wheel_gid):
480 """setup reasonable name mappings for sam names to unix names.
482 :param samdb: SamDB object.
483 :param idmap: IDmap db object.
484 :param sid: The domain sid.
485 :param domaindn: The domain DN.
486 :param root_uid: uid of the UNIX root user.
487 :param nobody_uid: uid of the UNIX nobody user.
488 :param users_gid: gid of the UNIX users group.
489 :param wheel_gid: gid of the UNIX wheel group."""
491 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
492 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
494 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
495 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
497 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
498 provision_backend, names, schema,
499 serverrole,
500 erase=False):
501 """Setup the partitions for the SAM database.
503 Alternatively, provision() may call this, and then populate the database.
505 :note: This will wipe the Sam Database!
507 :note: This function always removes the local SAM LDB file. The erase
508 parameter controls whether to erase the existing data, which
509 may not be stored locally but in LDAP.
512 assert session_info is not None
514 # We use options=["modules:"] to stop the modules loading - we
515 # just want to wipe and re-initialise the database, not start it up
517 try:
518 os.unlink(samdb_path)
519 except OSError:
520 pass
522 samdb = Ldb(url=samdb_path, session_info=session_info,
523 lp=lp, options=["modules:"])
525 ldap_backend_line = "# No LDAP backend"
526 if provision_backend.type is not "ldb":
527 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
529 samdb.transaction_start()
530 try:
531 message("Setting up sam.ldb partitions and settings")
532 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
533 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
534 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
535 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
536 "LDAP_BACKEND_LINE": ldap_backend_line,
540 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
541 "BACKEND_TYPE": provision_backend.type,
542 "SERVER_ROLE": serverrole
545 message("Setting up sam.ldb rootDSE")
546 setup_samdb_rootdse(samdb, setup_path, names)
548 except:
549 samdb.transaction_cancel()
550 raise
552 samdb.transaction_commit()
555 def secretsdb_self_join(secretsdb, domain,
556 netbiosname, machinepass, domainsid=None,
557 realm=None, dnsdomain=None,
558 keytab_path=None,
559 key_version_number=1,
560 secure_channel_type=SEC_CHAN_WKSTA):
561 """Add domain join-specific bits to a secrets database.
563 :param secretsdb: Ldb Handle to the secrets database
564 :param machinepass: Machine password
566 attrs=["whenChanged",
567 "secret",
568 "priorSecret",
569 "priorChanged",
570 "krb5Keytab",
571 "privateKeytab"]
574 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
575 msg["secureChannelType"] = str(secure_channel_type)
576 msg["flatname"] = [domain]
577 msg["objectClass"] = ["top", "primaryDomain"]
578 if realm is not None:
579 if dnsdomain is None:
580 dnsdomain = realm.lower()
581 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
582 msg["realm"] = realm
583 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
584 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
585 msg["privateKeytab"] = ["secrets.keytab"]
588 msg["secret"] = [machinepass]
589 msg["samAccountName"] = ["%s$" % netbiosname]
590 msg["secureChannelType"] = [str(secure_channel_type)]
591 if domainsid is not None:
592 msg["objectSid"] = [ndr_pack(domainsid)]
594 res = secretsdb.search(base="cn=Primary Domains",
595 attrs=attrs,
596 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
597 scope=ldb.SCOPE_ONELEVEL)
599 for del_msg in res:
600 if del_msg.dn is not msg.dn:
601 secretsdb.delete(del_msg.dn)
603 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
605 if len(res) == 1:
606 msg["priorSecret"] = res[0]["secret"]
607 msg["priorWhenChanged"] = res[0]["whenChanged"]
609 if res["privateKeytab"] is not None:
610 msg["privateKeytab"] = res[0]["privateKeytab"]
612 if res["krb5Keytab"] is not None:
613 msg["krb5Keytab"] = res[0]["krb5Keytab"]
615 for el in msg:
616 el.set_flags(ldb.FLAG_MOD_REPLACE)
617 secretsdb.modify(msg)
618 else:
619 secretsdb.add(msg)
622 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
623 realm, dnsdomain,
624 dns_keytab_path, dnspass):
625 """Add DNS specific bits to a secrets database.
627 :param secretsdb: Ldb Handle to the secrets database
628 :param setup_path: Setup path function
629 :param machinepass: Machine password
631 try:
632 os.unlink(os.path.join(private_dir, dns_keytab_path))
633 except OSError:
634 pass
636 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
637 "REALM": realm,
638 "DNSDOMAIN": dnsdomain,
639 "DNS_KEYTAB": dns_keytab_path,
640 "DNSPASS_B64": b64encode(dnspass),
644 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
645 """Setup the secrets database.
647 :param path: Path to the secrets database.
648 :param setup_path: Get the path to a setup file.
649 :param session_info: Session info.
650 :param credentials: Credentials
651 :param lp: Loadparm context
652 :return: LDB handle for the created secrets database
654 if os.path.exists(path):
655 os.unlink(path)
656 secrets_ldb = Ldb(path, session_info=session_info,
657 lp=lp)
658 secrets_ldb.erase()
659 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
660 secrets_ldb = Ldb(path, session_info=session_info,
661 lp=lp)
662 secrets_ldb.transaction_start()
663 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
665 if backend_credentials is not None and backend_credentials.authentication_requested():
666 if backend_credentials.get_bind_dn() is not None:
667 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
668 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
669 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
671 else:
672 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
673 "LDAPADMINUSER": backend_credentials.get_username(),
674 "LDAPADMINREALM": backend_credentials.get_realm(),
675 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
678 return secrets_ldb
680 def setup_privileges(path, setup_path, session_info, lp):
681 """Setup the privileges database.
683 :param path: Path to the privileges database.
684 :param setup_path: Get the path to a setup file.
685 :param session_info: Session info.
686 :param credentials: Credentials
687 :param lp: Loadparm context
688 :return: LDB handle for the created secrets database
690 if os.path.exists(path):
691 os.unlink(path)
692 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
693 privilege_ldb.erase()
694 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
697 def setup_registry(path, setup_path, session_info, lp):
698 """Setup the registry.
700 :param path: Path to the registry database
701 :param setup_path: Function that returns the path to a setup.
702 :param session_info: Session information
703 :param credentials: Credentials
704 :param lp: Loadparm context
706 reg = registry.Registry()
707 hive = registry.open_ldb(path, session_info=session_info,
708 lp_ctx=lp)
709 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
710 provision_reg = setup_path("provision.reg")
711 assert os.path.exists(provision_reg)
712 reg.diff_apply(provision_reg)
715 def setup_idmapdb(path, setup_path, session_info, lp):
716 """Setup the idmap database.
718 :param path: path to the idmap database
719 :param setup_path: Function that returns a path to a setup file
720 :param session_info: Session information
721 :param credentials: Credentials
722 :param lp: Loadparm context
724 if os.path.exists(path):
725 os.unlink(path)
727 idmap_ldb = IDmapDB(path, session_info=session_info,
728 lp=lp)
730 idmap_ldb.erase()
731 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
732 return idmap_ldb
735 def setup_samdb_rootdse(samdb, setup_path, names):
736 """Setup the SamDB rootdse.
738 :param samdb: Sam Database handle
739 :param setup_path: Obtain setup path
741 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
742 "SCHEMADN": names.schemadn,
743 "NETBIOSNAME": names.netbiosname,
744 "DNSDOMAIN": names.dnsdomain,
745 "REALM": names.realm,
746 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
747 "DOMAINDN": names.domaindn,
748 "ROOTDN": names.rootdn,
749 "CONFIGDN": names.configdn,
750 "SERVERDN": names.serverdn,
754 def setup_self_join(samdb, names,
755 machinepass, dnspass,
756 domainsid, invocationid, setup_path,
757 policyguid, policyguid_dc, domainControllerFunctionality,
758 ntdsguid):
759 """Join a host to its own domain."""
760 assert isinstance(invocationid, str)
761 if ntdsguid is not None:
762 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
763 else:
764 ntdsguid_line = ""
765 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
766 "CONFIGDN": names.configdn,
767 "SCHEMADN": names.schemadn,
768 "DOMAINDN": names.domaindn,
769 "SERVERDN": names.serverdn,
770 "INVOCATIONID": invocationid,
771 "NETBIOSNAME": names.netbiosname,
772 "DEFAULTSITE": names.sitename,
773 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
774 "MACHINEPASS_B64": b64encode(machinepass),
775 "REALM": names.realm,
776 "DOMAIN": names.domain,
777 "DOMAINSID": str(domainsid),
778 "DNSDOMAIN": names.dnsdomain,
779 "SAMBA_VERSION_STRING": version,
780 "NTDSGUID": ntdsguid_line,
781 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
783 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
784 "POLICYGUID": policyguid,
785 "POLICYGUID_DC": policyguid_dc,
786 "DNSDOMAIN": names.dnsdomain,
787 "DOMAINSID": str(domainsid),
788 "DOMAINDN": names.domaindn})
790 # add the NTDSGUID based SPNs
791 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
792 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
793 expression="", scope=ldb.SCOPE_BASE)
794 assert isinstance(names.ntdsguid, str)
796 # Setup fSMORoleOwner entries to point at the newly created DC entry
797 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
798 "DOMAIN": names.domain,
799 "DNSDOMAIN": names.dnsdomain,
800 "DOMAINDN": names.domaindn,
801 "CONFIGDN": names.configdn,
802 "SCHEMADN": names.schemadn,
803 "DEFAULTSITE": names.sitename,
804 "SERVERDN": names.serverdn,
805 "NETBIOSNAME": names.netbiosname,
806 "NTDSGUID": names.ntdsguid,
807 "DNSPASS_B64": b64encode(dnspass),
811 def setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid):
812 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
813 "{" + policyguid + "}")
814 os.makedirs(policy_path, 0755)
815 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
816 "[General]\r\nVersion=65543")
817 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
818 os.makedirs(os.path.join(policy_path, "USER"), 0755)
820 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
821 "{" + policyguid_dc + "}")
822 os.makedirs(policy_path_dc, 0755)
823 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
824 "[General]\r\nVersion=2")
825 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
826 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
829 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
830 names, message,
831 domainsid, domainguid, policyguid, policyguid_dc,
832 fill, adminpass, krbtgtpass,
833 machinepass, invocationid, dnspass, ntdsguid,
834 serverrole, dom_for_fun_level=None,
835 schema=None):
836 """Setup a complete SAM Database.
838 :note: This will wipe the main SAM database file!
841 # ATTENTION: Do NOT change these default values without discussion with the
842 # team and/or release manager. They have a big impact on the whole program!
843 domainControllerFunctionality = DS_DC_FUNCTION_2008
845 if dom_for_fun_level is None:
846 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
847 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
848 message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
850 if dom_for_fun_level > domainControllerFunctionality:
851 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). This won't work!")
853 domainFunctionality = dom_for_fun_level
854 forestFunctionality = dom_for_fun_level
856 # Also wipes the database
857 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
858 provision_backend=provision_backend, session_info=session_info,
859 names=names,
860 serverrole=serverrole, schema=schema)
862 if (schema == None):
863 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
865 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
866 samdb = Ldb(session_info=session_info,
867 credentials=provision_backend.credentials, lp=lp)
869 message("Pre-loading the Samba 4 and AD schema")
871 # Load the schema from the one we computed earlier
872 samdb.set_schema_from_ldb(schema.ldb)
874 # And now we can connect to the DB - the schema won't be loaded from the DB
875 samdb.connect(path)
877 if fill == FILL_DRS:
878 return samdb
880 samdb.transaction_start()
881 try:
882 # Set the domain functionality levels onto the database.
883 # Various module (the password_hash module in particular) need
884 # to know what level of AD we are emulating.
886 # These will be fixed into the database via the database
887 # modifictions below, but we need them set from the start.
888 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
889 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
890 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
892 samdb.set_domain_sid(str(domainsid))
893 samdb.set_invocation_id(invocationid)
895 message("Adding DomainDN: %s" % names.domaindn)
897 #impersonate domain admin
898 admin_session_info = admin_session(lp, str(domainsid))
899 samdb.set_session_info(admin_session_info)
900 if domainguid is not None:
901 domainguid_line = "objectGUID: %s\n-" % domainguid
902 else:
903 domainguid_line = ""
905 descr = b64encode(get_domain_descriptor(domainsid))
906 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
907 "DOMAINDN": names.domaindn,
908 "DOMAINGUID": domainguid_line,
909 "DESCRIPTOR": descr
913 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
914 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
915 "DOMAINSID": str(domainsid),
916 "SCHEMADN": names.schemadn,
917 "NETBIOSNAME": names.netbiosname,
918 "DEFAULTSITE": names.sitename,
919 "CONFIGDN": names.configdn,
920 "SERVERDN": names.serverdn,
921 "POLICYGUID": policyguid,
922 "DOMAINDN": names.domaindn,
923 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
924 "SAMBA_VERSION_STRING": version
927 message("Adding configuration container")
928 descr = b64encode(get_config_descriptor(domainsid))
929 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
930 "CONFIGDN": names.configdn,
931 "DESCRIPTOR": descr,
934 # The LDIF here was created when the Schema object was constructed
935 message("Setting up sam.ldb schema")
936 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
937 samdb.modify_ldif(schema.schema_dn_modify)
938 samdb.write_prefixes_from_schema()
939 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
940 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
941 {"SCHEMADN": names.schemadn})
943 message("Reopening sam.ldb with new schema")
944 samdb.transaction_commit()
945 samdb = Ldb(session_info=admin_session_info,
946 credentials=provision_backend.credentials, lp=lp)
947 samdb.connect(path)
948 samdb.transaction_start()
949 samdb.set_invocation_id(invocationid)
951 message("Setting up sam.ldb configuration data")
952 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
953 "CONFIGDN": names.configdn,
954 "NETBIOSNAME": names.netbiosname,
955 "DEFAULTSITE": names.sitename,
956 "DNSDOMAIN": names.dnsdomain,
957 "DOMAIN": names.domain,
958 "SCHEMADN": names.schemadn,
959 "DOMAINDN": names.domaindn,
960 "SERVERDN": names.serverdn,
961 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
964 message("Setting up display specifiers")
965 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
966 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
967 check_all_substituted(display_specifiers_ldif)
968 samdb.add_ldif(display_specifiers_ldif)
970 message("Adding users container")
971 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
972 "DOMAINDN": names.domaindn})
973 message("Modifying users container")
974 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
975 "DOMAINDN": names.domaindn})
976 message("Adding computers container")
977 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
978 "DOMAINDN": names.domaindn})
979 message("Modifying computers container")
980 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
981 "DOMAINDN": names.domaindn})
982 message("Setting up sam.ldb data")
983 setup_add_ldif(samdb, setup_path("provision.ldif"), {
984 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
985 "DOMAINDN": names.domaindn,
986 "NETBIOSNAME": names.netbiosname,
987 "DEFAULTSITE": names.sitename,
988 "CONFIGDN": names.configdn,
989 "SERVERDN": names.serverdn,
990 "POLICYGUID_DC": policyguid_dc
993 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
994 "DOMAINDN": names.domaindn})
996 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
997 "CONFIGDN": names.configdn,
998 "SCHEMADN": names.schemadn})
999 if fill == FILL_FULL:
1000 message("Setting up sam.ldb users and groups")
1001 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1002 "DOMAINDN": names.domaindn,
1003 "DOMAINSID": str(domainsid),
1004 "CONFIGDN": names.configdn,
1005 "ADMINPASS_B64": b64encode(adminpass),
1006 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1009 message("Setting up self join")
1010 setup_self_join(samdb, names=names, invocationid=invocationid,
1011 dnspass=dnspass,
1012 machinepass=machinepass,
1013 domainsid=domainsid, policyguid=policyguid,
1014 policyguid_dc=policyguid_dc,
1015 setup_path=setup_path,
1016 domainControllerFunctionality=domainControllerFunctionality,
1017 ntdsguid=ntdsguid)
1019 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1020 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1021 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1022 assert isinstance(names.ntdsguid, str)
1024 except:
1025 samdb.transaction_cancel()
1026 raise
1028 samdb.transaction_commit()
1029 return samdb
1032 FILL_FULL = "FULL"
1033 FILL_NT4SYNC = "NT4SYNC"
1034 FILL_DRS = "DRS"
1035 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1036 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)"
1038 def set_gpo_acl(path,acl,lp,domsid):
1039 setntacl(lp,path,acl,domsid)
1040 for root, dirs, files in os.walk(path, topdown=False):
1041 for name in files:
1042 setntacl(lp,os.path.join(root, name),acl,domsid)
1043 for name in dirs:
1044 setntacl(lp,os.path.join(root, name),acl,domsid)
1046 def setsysvolacl(samdb,names,netlogon,sysvol,gid,domainsid,lp):
1047 canchown = 1
1048 try:
1049 os.chown(sysvol,-1,gid)
1050 except:
1051 canchown = 0
1053 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1054 for root, dirs, files in os.walk(sysvol, topdown=False):
1055 for name in files:
1056 if canchown:
1057 os.chown(os.path.join(root, name),-1,gid)
1058 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1059 for name in dirs:
1060 if canchown:
1061 os.chown(os.path.join(root, name),-1,gid)
1062 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1064 # Set ACL for GPO
1065 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies")
1066 set_gpo_acl(policy_path,dsacl2fsacl(POLICIES_ACL,str(domainsid)),lp,str(domainsid))
1067 res = samdb.search(base="CN=Policies,CN=System,%s"%(names.domaindn),
1068 attrs=["cn","nTSecurityDescriptor"],
1069 expression="", scope=ldb.SCOPE_ONELEVEL)
1070 for policy in res:
1071 acl = ndr_unpack(security.descriptor,str(policy["nTSecurityDescriptor"])).as_sddl()
1072 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies",
1073 str(policy["cn"]))
1074 set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
1078 def provision(setup_dir, message, session_info,
1079 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1080 realm=None,
1081 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1082 serverdn=None,
1083 domain=None, hostname=None, hostip=None, hostip6=None,
1084 domainsid=None, adminpass=None, ldapadminpass=None,
1085 krbtgtpass=None, domainguid=None,
1086 policyguid=None, policyguid_dc=None, invocationid=None,
1087 machinepass=None, ntdsguid=None,
1088 dnspass=None, root=None, nobody=None, users=None,
1089 wheel=None, backup=None, aci=None, serverrole=None,
1090 dom_for_fun_level=None,
1091 ldap_backend_extra_port=None, backend_type=None,
1092 sitename=None,
1093 ol_mmr_urls=None, ol_olc=None,
1094 setup_ds_path=None, slapd_path=None, nosync=False,
1095 ldap_dryrun_mode=False,useeadb=False):
1096 """Provision samba4
1098 :note: caution, this wipes all existing data!
1101 def setup_path(file):
1102 return os.path.join(setup_dir, file)
1104 if domainsid is None:
1105 domainsid = security.random_sid()
1106 else:
1107 domainsid = security.dom_sid(domainsid)
1109 # create/adapt the group policy GUIDs
1110 if policyguid is None:
1111 policyguid = str(uuid.uuid4())
1112 policyguid = policyguid.upper()
1113 if policyguid_dc is None:
1114 policyguid_dc = str(uuid.uuid4())
1115 policyguid_dc = policyguid_dc.upper()
1117 if adminpass is None:
1118 adminpass = glue.generate_random_password(12, 32)
1119 if krbtgtpass is None:
1120 krbtgtpass = glue.generate_random_password(128, 255)
1121 if machinepass is None:
1122 machinepass = glue.generate_random_password(128, 255)
1123 if dnspass is None:
1124 dnspass = glue.generate_random_password(128, 255)
1125 if ldapadminpass is None:
1126 #Make a new, random password between Samba and it's LDAP server
1127 ldapadminpass=glue.generate_random_password(128, 255)
1129 if backend_type is None:
1130 backend_type = "ldb"
1132 sid_generator = "internal"
1133 if backend_type == "fedora-ds":
1134 sid_generator = "backend"
1136 root_uid = findnss_uid([root or "root"])
1137 nobody_uid = findnss_uid([nobody or "nobody"])
1138 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1139 if wheel is None:
1140 wheel_gid = findnss_gid(["wheel", "adm"])
1141 else:
1142 wheel_gid = findnss_gid([wheel])
1143 try:
1144 bind_gid = findnss_gid(["bind", "named"])
1145 except KeyError:
1146 bind_gid = None
1148 if targetdir is not None:
1149 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1150 os.makedirs(os.path.join(targetdir, "etc"))
1151 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1152 elif smbconf is None:
1153 smbconf = param.default_path()
1155 # only install a new smb.conf if there isn't one there already
1156 if os.path.exists(smbconf):
1157 # if Samba Team members can't figure out the weird errors
1158 # loading an empty smb.conf gives, then we need to be smarter.
1159 # Pretend it just didn't exist --abartlet
1160 data = open(smbconf, 'r').read()
1161 data = data.lstrip()
1162 if data is None or data == "":
1163 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1164 targetdir, sid_generator, useeadb)
1165 else:
1166 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1167 targetdir, sid_generator, useeadb)
1169 lp = param.LoadParm()
1170 lp.load(smbconf)
1172 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1173 dnsdomain=realm, serverrole=serverrole,
1174 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1175 serverdn=serverdn, sitename=sitename)
1177 paths = provision_paths_from_lp(lp, names.dnsdomain)
1179 paths.bind_gid = bind_gid
1181 if hostip is None:
1182 hostips = glue.interface_ips(lp, False)
1183 if len(hostips) == 0:
1184 message("No external IPv4 address has been found: I use the loopback.")
1185 hostip = '127.0.0.1'
1186 else:
1187 hostip = hostips[0]
1188 if len(hostips) > 1:
1189 message("More than one IPv4 address found: I use " + hostip + ".")
1191 if hostip6 is None:
1192 try:
1193 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1194 if hostip6 is None:
1195 hostip6 = ip[-1][0]
1196 if hostip6 == '::1' and ip[-1][0] != '::1':
1197 hostip6 = ip[-1][0]
1198 except socket.gaierror, (socket.EAI_NODATA, msg):
1199 hostip6 = None
1201 if serverrole is None:
1202 serverrole = lp.get("server role")
1204 assert serverrole in ("domain controller", "member server", "standalone")
1205 if invocationid is None:
1206 invocationid = str(uuid.uuid4())
1208 if not os.path.exists(paths.private_dir):
1209 os.mkdir(paths.private_dir)
1210 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1211 os.mkdir(os.path.join(paths.private_dir,"tls"))
1213 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1215 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1217 if backend_type == "ldb":
1218 provision_backend = LDBBackend(backend_type,
1219 paths=paths, setup_path=setup_path,
1220 lp=lp, credentials=credentials,
1221 names=names,
1222 message=message)
1223 elif backend_type == "existing":
1224 provision_backend = ExistingBackend(backend_type,
1225 paths=paths, setup_path=setup_path,
1226 lp=lp, credentials=credentials,
1227 names=names,
1228 message=message,
1229 ldapi_url=ldapi_url)
1230 elif backend_type == "fedora-ds":
1231 provision_backend = FDSBackend(backend_type,
1232 paths=paths, setup_path=setup_path,
1233 lp=lp, credentials=credentials,
1234 names=names,
1235 message=message,
1236 domainsid=domainsid,
1237 schema=schema,
1238 hostname=hostname,
1239 ldapadminpass=ldapadminpass,
1240 slapd_path=slapd_path,
1241 ldap_backend_extra_port=ldap_backend_extra_port,
1242 ldap_dryrun_mode=ldap_dryrun_mode,
1243 root=root,
1244 setup_ds_path=setup_ds_path)
1245 elif backend_type == "openldap":
1246 provision_backend = OpenLDAPBackend(backend_type,
1247 paths=paths, setup_path=setup_path,
1248 lp=lp, credentials=credentials,
1249 names=names,
1250 message=message,
1251 domainsid=domainsid,
1252 schema=schema,
1253 hostname=hostname,
1254 ldapadminpass=ldapadminpass,
1255 slapd_path=slapd_path,
1256 ldap_backend_extra_port=ldap_backend_extra_port,
1257 ldap_dryrun_mode=ldap_dryrun_mode,
1258 ol_mmr_urls=ol_mmr_urls,
1259 nosync=nosync)
1260 else:
1261 raise ProvisioningError("Unknown LDAP backend type selected")
1263 provision_backend.init()
1264 provision_backend.start()
1266 # only install a new shares config db if there is none
1267 if not os.path.exists(paths.shareconf):
1268 message("Setting up share.ldb")
1269 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1270 lp=lp)
1271 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1274 message("Setting up secrets.ldb")
1275 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1276 session_info=session_info,
1277 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1279 message("Setting up the registry")
1280 setup_registry(paths.hklm, setup_path, session_info,
1281 lp=lp)
1283 message("Setting up the privileges database")
1284 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1286 message("Setting up idmap db")
1287 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1288 lp=lp)
1290 message("Setting up SAM db")
1291 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1292 provision_backend, lp, names,
1293 message,
1294 domainsid=domainsid,
1295 schema=schema, domainguid=domainguid,
1296 policyguid=policyguid, policyguid_dc=policyguid_dc,
1297 fill=samdb_fill,
1298 adminpass=adminpass, krbtgtpass=krbtgtpass,
1299 invocationid=invocationid,
1300 machinepass=machinepass, dnspass=dnspass,
1301 ntdsguid=ntdsguid, serverrole=serverrole,
1302 dom_for_fun_level=dom_for_fun_level)
1304 if serverrole == "domain controller":
1305 if paths.netlogon is None:
1306 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1307 message("Please either remove %s or see the template at %s" %
1308 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1309 assert(paths.netlogon is not None)
1311 if paths.sysvol is None:
1312 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1313 message("Please either remove %s or see the template at %s" %
1314 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1315 assert(paths.sysvol is not None)
1318 if not os.path.isdir(paths.netlogon):
1319 os.makedirs(paths.netlogon, 0755)
1321 if samdb_fill == FILL_FULL:
1322 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1323 root_uid=root_uid, nobody_uid=nobody_uid,
1324 users_gid=users_gid, wheel_gid=wheel_gid)
1326 if serverrole == "domain controller":
1327 # Set up group policies (domain policy and domain controller policy)
1328 setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid)
1329 setsysvolacl(samdb,names,paths.netlogon,paths.sysvol,wheel_gid,domainsid,lp)
1331 message("Setting up sam.ldb rootDSE marking as synchronized")
1332 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1334 secretsdb_self_join(secrets_ldb, domain=names.domain,
1335 realm=names.realm,
1336 dnsdomain=names.dnsdomain,
1337 netbiosname=names.netbiosname,
1338 domainsid=domainsid,
1339 machinepass=machinepass,
1340 secure_channel_type=SEC_CHAN_BDC)
1342 if serverrole == "domain controller":
1343 secretsdb_setup_dns(secrets_ldb, setup_path,
1344 paths.private_dir,
1345 realm=names.realm, dnsdomain=names.dnsdomain,
1346 dns_keytab_path=paths.dns_keytab,
1347 dnspass=dnspass)
1349 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1350 assert isinstance(domainguid, str)
1352 # Only make a zone file on the first DC, it should be replicated
1353 # with DNS replication
1354 create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain=names.dnsdomain,
1355 hostip=hostip,
1356 hostip6=hostip6, hostname=names.hostname,
1357 realm=names.realm,
1358 domainguid=domainguid, ntdsguid=names.ntdsguid)
1360 create_named_conf(paths, setup_path, realm=names.realm,
1361 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1363 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1364 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1365 keytab_name=paths.dns_keytab)
1366 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1367 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1369 create_krb5_conf(paths.krb5conf, setup_path,
1370 dnsdomain=names.dnsdomain, hostname=names.hostname,
1371 realm=names.realm)
1372 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1374 if serverrole == "domain controller":
1375 create_dns_update_list(lp, message, paths, setup_path)
1377 provision_backend.post_setup()
1378 provision_backend.shutdown()
1380 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1381 ldapi_url)
1383 #Now commit the secrets.ldb to disk
1384 secrets_ldb.transaction_commit()
1386 # the commit creates the dns.keytab, now chown it
1387 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1388 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1389 try:
1390 os.chmod(dns_keytab_path, 0640)
1391 os.chown(dns_keytab_path, -1, paths.bind_gid)
1392 except OSError:
1393 message("Failed to chown %s to bind gid %u" % (dns_keytab_path, paths.bind_gid))
1396 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1398 message("Once the above files are installed, your Samba4 server will be ready to use")
1399 message("Server Role: %s" % serverrole)
1400 message("Hostname: %s" % names.hostname)
1401 message("NetBIOS Domain: %s" % names.domain)
1402 message("DNS Domain: %s" % names.dnsdomain)
1403 message("DOMAIN SID: %s" % str(domainsid))
1404 if samdb_fill == FILL_FULL:
1405 message("Admin password: %s" % adminpass)
1406 if provision_backend.type is not "ldb":
1407 if provision_backend.credentials.get_bind_dn() is not None:
1408 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1409 else:
1410 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1412 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1414 if provision_backend.slapd_command_escaped is not None:
1415 # now display slapd_command_file.txt to show how slapd must be started next time
1416 message("Use later the following commandline to start slapd, then Samba:")
1417 message(provision_backend.slapd_command_escaped)
1418 message("This slapd-Commandline is also stored under: " + provision_backend.ldapdir + "/ldap_backend_startup.sh")
1421 result = ProvisionResult()
1422 result.domaindn = domaindn
1423 result.paths = paths
1424 result.lp = lp
1425 result.samdb = samdb
1426 return result
1430 def provision_become_dc(setup_dir=None,
1431 smbconf=None, targetdir=None, realm=None,
1432 rootdn=None, domaindn=None, schemadn=None,
1433 configdn=None, serverdn=None,
1434 domain=None, hostname=None, domainsid=None,
1435 adminpass=None, krbtgtpass=None, domainguid=None,
1436 policyguid=None, policyguid_dc=None, invocationid=None,
1437 machinepass=None,
1438 dnspass=None, root=None, nobody=None, users=None,
1439 wheel=None, backup=None, serverrole=None,
1440 ldap_backend=None, ldap_backend_type=None,
1441 sitename=None, debuglevel=1):
1443 def message(text):
1444 """print a message if quiet is not set."""
1445 print text
1447 glue.set_debug_level(debuglevel)
1449 return provision(setup_dir, message, system_session(), None,
1450 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1451 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1452 configdn=configdn, serverdn=serverdn, domain=domain,
1453 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1454 machinepass=machinepass, serverrole="domain controller",
1455 sitename=sitename)
1458 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1459 """Create a PHP LDAP admin configuration file.
1461 :param path: Path to write the configuration to.
1462 :param setup_path: Function to generate setup paths.
1464 setup_file(setup_path("phpldapadmin-config.php"), path,
1465 {"S4_LDAPI_URI": ldapi_uri})
1468 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1469 hostip, hostip6, hostname, realm, domainguid,
1470 ntdsguid):
1471 """Write out a DNS zone file, from the info in the current database.
1473 :param paths: paths object
1474 :param setup_path: Setup path function.
1475 :param dnsdomain: DNS Domain name
1476 :param domaindn: DN of the Domain
1477 :param hostip: Local IPv4 IP
1478 :param hostip6: Local IPv6 IP
1479 :param hostname: Local hostname
1480 :param realm: Realm name
1481 :param domainguid: GUID of the domain.
1482 :param ntdsguid: GUID of the hosts nTDSDSA record.
1484 assert isinstance(domainguid, str)
1486 if hostip6 is not None:
1487 hostip6_base_line = " IN AAAA " + hostip6
1488 hostip6_host_line = hostname + " IN AAAA " + hostip6
1489 else:
1490 hostip6_base_line = ""
1491 hostip6_host_line = ""
1493 if hostip is not None:
1494 hostip_base_line = " IN A " + hostip
1495 hostip_host_line = hostname + " IN A " + hostip
1496 else:
1497 hostip_base_line = ""
1498 hostip_host_line = ""
1500 dns_dir = os.path.dirname(paths.dns)
1502 try:
1503 shutil.rmtree(dns_dir, True)
1504 except OSError:
1505 pass
1507 os.mkdir(dns_dir, 0775)
1509 # we need to freeze the zone while we update the contents
1510 if targetdir is None:
1511 rndc = ' '.join(lp.get("rndc command"))
1512 os.system(rndc + " freeze " + lp.get("realm"))
1514 setup_file(setup_path("provision.zone"), paths.dns, {
1515 "HOSTNAME": hostname,
1516 "DNSDOMAIN": dnsdomain,
1517 "REALM": realm,
1518 "HOSTIP_BASE_LINE": hostip_base_line,
1519 "HOSTIP_HOST_LINE": hostip_host_line,
1520 "DOMAINGUID": domainguid,
1521 "DATESTRING": time.strftime("%Y%m%d%H"),
1522 "DEFAULTSITE": DEFAULTSITE,
1523 "NTDSGUID": ntdsguid,
1524 "HOSTIP6_BASE_LINE": hostip6_base_line,
1525 "HOSTIP6_HOST_LINE": hostip6_host_line,
1528 # note that we use no variable substitution on this file
1529 # the substitution is done at runtime by samba_dnsupdate
1530 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1532 if paths.bind_gid is not None:
1533 try:
1534 os.chown(dns_dir, -1, paths.bind_gid)
1535 os.chown(paths.dns, -1, paths.bind_gid)
1536 # chmod needed to cope with umask
1537 os.chmod(dns_dir, 0775)
1538 os.chmod(paths.dns, 0664)
1539 except OSError:
1540 message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1542 if targetdir is None:
1543 os.system(rndc + " unfreeze " + lp.get("realm"))
1546 def create_dns_update_list(lp, message, paths, setup_path):
1547 """Write out a dns_update_list file"""
1548 # note that we use no variable substitution on this file
1549 # the substitution is done at runtime by samba_dnsupdate
1550 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1553 def create_named_conf(paths, setup_path, realm, dnsdomain,
1554 private_dir):
1555 """Write out a file containing zone statements suitable for inclusion in a
1556 named.conf file (including GSS-TSIG configuration).
1558 :param paths: all paths
1559 :param setup_path: Setup path function.
1560 :param realm: Realm name
1561 :param dnsdomain: DNS Domain name
1562 :param private_dir: Path to private directory
1563 :param keytab_name: File name of DNS keytab file
1566 setup_file(setup_path("named.conf"), paths.namedconf, {
1567 "DNSDOMAIN": dnsdomain,
1568 "REALM": realm,
1569 "ZONE_FILE": paths.dns,
1570 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1571 "NAMED_CONF": paths.namedconf,
1572 "NAMED_CONF_UPDATE": paths.namedconf_update
1575 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1577 def create_named_txt(path, setup_path, realm, dnsdomain,
1578 private_dir, keytab_name):
1579 """Write out a file containing zone statements suitable for inclusion in a
1580 named.conf file (including GSS-TSIG configuration).
1582 :param path: Path of the new named.conf file.
1583 :param setup_path: Setup path function.
1584 :param realm: Realm name
1585 :param dnsdomain: DNS Domain name
1586 :param private_dir: Path to private directory
1587 :param keytab_name: File name of DNS keytab file
1590 setup_file(setup_path("named.txt"), path, {
1591 "DNSDOMAIN": dnsdomain,
1592 "REALM": realm,
1593 "DNS_KEYTAB": keytab_name,
1594 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1595 "PRIVATE_DIR": private_dir
1598 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1599 """Write out a file containing zone statements suitable for inclusion in a
1600 named.conf file (including GSS-TSIG configuration).
1602 :param path: Path of the new named.conf file.
1603 :param setup_path: Setup path function.
1604 :param dnsdomain: DNS Domain name
1605 :param hostname: Local hostname
1606 :param realm: Realm name
1609 setup_file(setup_path("krb5.conf"), path, {
1610 "DNSDOMAIN": dnsdomain,
1611 "HOSTNAME": hostname,
1612 "REALM": realm,