s4:provision.py - cosmetic
[Samba/cd1.git] / source4 / scripting / python / samba / provision.py
blob0da03f3ffd1fecb87d810798c5a26ae3acd45c5d
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 sys
31 import pwd
32 import grp
33 import time
34 import uuid, glue
35 import socket
36 import param
37 import registry
38 import samba
39 import subprocess
40 import ldb
43 from auth import system_session, admin_session
44 from samba import version, Ldb, substitute_var, valid_netbios_name, setup_file
45 from samba import check_all_substituted, read_and_sub_file
46 from samba import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008
47 from samba.samdb import SamDB
48 from samba.idmap import IDmapDB
49 from samba.dcerpc import security
50 from samba.ndr import ndr_pack
51 import urllib
52 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
53 from ms_display_specifiers import read_ms_ldif
54 from schema import Schema
55 from provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
56 from provisionexceptions import ProvisioningError, InvalidNetbiosName
57 from signal import SIGTERM
58 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
60 __docformat__ = "restructuredText"
62 def find_setup_dir():
63 """Find the setup directory used by provision."""
64 dirname = os.path.dirname(__file__)
65 if "/site-packages/" in dirname:
66 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
67 for suffix in ["share/setup", "share/samba/setup", "setup"]:
68 ret = os.path.join(prefix, suffix)
69 if os.path.isdir(ret):
70 return ret
71 # In source tree
72 ret = os.path.join(dirname, "../../../setup")
73 if os.path.isdir(ret):
74 return ret
75 raise Exception("Unable to find setup directory.")
77 # descriptors of the naming contexts
78 # hard coded at this point, but will probably be changed when
79 # we enable different fsmo roles
81 def get_config_descriptor(domain_sid):
82 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
83 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
84 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
85 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
86 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
87 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
88 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
89 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
90 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
91 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
92 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
93 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
94 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
95 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
96 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
97 sec = security.descriptor.from_sddl(sddl, domain_sid)
98 return b64encode(ndr_pack(sec))
100 def get_domain_descriptor(domain_sid):
101 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
102 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
103 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
104 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
105 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
106 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
107 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
108 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
109 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
110 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
111 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-832762594-175224951-1765713900-498)" \
112 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
113 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
114 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
115 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
116 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
117 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
119 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
120 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
121 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
122 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)" \
123 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
124 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
125 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
126 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
127 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
129 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
130 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
131 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
132 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
133 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
134 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
135 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
136 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
137 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
138 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
139 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
140 "(A;;RPRC;;;RU)" \
141 "(A;CI;LC;;;RU)" \
142 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
143 "(A;;RP;;;WD)" \
144 "(A;;RPLCLORC;;;ED)" \
145 "(A;;RPLCLORC;;;AU)" \
146 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
147 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
148 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
149 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
150 sec = security.descriptor.from_sddl(sddl, domain_sid)
151 return b64encode(ndr_pack(sec))
153 DEFAULTSITE = "Default-First-Site-Name"
155 # Exception classes
157 class ProvisionPaths(object):
158 def __init__(self):
159 self.shareconf = None
160 self.hklm = None
161 self.hkcu = None
162 self.hkcr = None
163 self.hku = None
164 self.hkpd = None
165 self.hkpt = None
166 self.samdb = None
167 self.idmapdb = None
168 self.secrets = None
169 self.keytab = None
170 self.dns_keytab = None
171 self.dns = None
172 self.winsdb = None
173 self.private_dir = None
174 self.ldapdir = None
175 self.slapdconf = None
176 self.modulesconf = None
177 self.memberofconf = None
178 self.olmmron = None
179 self.olmmrserveridsconf = None
180 self.olmmrsyncreplconf = None
181 self.olcdir = None
182 self.olslapd = None
183 self.olcseedldif = None
186 class ProvisionNames(object):
187 def __init__(self):
188 self.rootdn = None
189 self.domaindn = None
190 self.configdn = None
191 self.schemadn = None
192 self.ldapmanagerdn = None
193 self.dnsdomain = None
194 self.realm = None
195 self.netbiosname = None
196 self.domain = None
197 self.hostname = None
198 self.sitename = None
199 self.smbconf = None
202 class ProvisionResult(object):
203 def __init__(self):
204 self.paths = None
205 self.domaindn = None
206 self.lp = None
207 self.samdb = None
209 def check_install(lp, session_info, credentials):
210 """Check whether the current install seems ok.
212 :param lp: Loadparm context
213 :param session_info: Session information
214 :param credentials: Credentials
216 if lp.get("realm") == "":
217 raise Exception("Realm empty")
218 ldb = Ldb(lp.get("sam database"), session_info=session_info,
219 credentials=credentials, lp=lp)
220 if len(ldb.search("(cn=Administrator)")) != 1:
221 raise ProvisioningError("No administrator account found")
224 def findnss(nssfn, names):
225 """Find a user or group from a list of possibilities.
227 :param nssfn: NSS Function to try (should raise KeyError if not found)
228 :param names: Names to check.
229 :return: Value return by first names list.
231 for name in names:
232 try:
233 return nssfn(name)
234 except KeyError:
235 pass
236 raise KeyError("Unable to find user/group %r" % names)
239 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
240 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
243 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
244 """Setup a ldb in the private dir.
246 :param ldb: LDB file to import data into
247 :param ldif_path: Path of the LDIF file to load
248 :param subst_vars: Optional variables to subsitute in LDIF.
249 :param nocontrols: Optional list of controls, can be None for no controls
251 assert isinstance(ldif_path, str)
252 data = read_and_sub_file(ldif_path, subst_vars)
253 ldb.add_ldif(data,controls)
256 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
257 """Modify a ldb in the private dir.
259 :param ldb: LDB object.
260 :param ldif_path: LDIF file path.
261 :param subst_vars: Optional dictionary with substitution variables.
263 data = read_and_sub_file(ldif_path, subst_vars)
265 ldb.modify_ldif(data)
268 def setup_ldb(ldb, ldif_path, subst_vars):
269 """Import a LDIF a file into a LDB handle, optionally substituting variables.
271 :note: Either all LDIF data will be added or none (using transactions).
273 :param ldb: LDB file to import into.
274 :param ldif_path: Path to the LDIF file.
275 :param subst_vars: Dictionary with substitution variables.
277 assert ldb is not None
278 ldb.transaction_start()
279 try:
280 setup_add_ldif(ldb, ldif_path, subst_vars)
281 except:
282 ldb.transaction_cancel()
283 raise
284 ldb.transaction_commit()
287 def provision_paths_from_lp(lp, dnsdomain):
288 """Set the default paths for provisioning.
290 :param lp: Loadparm context.
291 :param dnsdomain: DNS Domain name
293 paths = ProvisionPaths()
294 paths.private_dir = lp.get("private dir")
295 paths.dns_keytab = "dns.keytab"
297 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
298 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
299 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
300 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
301 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
302 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
303 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
304 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
305 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
306 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
307 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
308 paths.phpldapadminconfig = os.path.join(paths.private_dir,
309 "phpldapadmin-config.php")
310 paths.ldapdir = os.path.join(paths.private_dir,
311 "ldap")
312 paths.slapdconf = os.path.join(paths.ldapdir,
313 "slapd.conf")
314 paths.slapdpid = os.path.join(paths.ldapdir,
315 "slapd.pid")
316 paths.modulesconf = os.path.join(paths.ldapdir,
317 "modules.conf")
318 paths.memberofconf = os.path.join(paths.ldapdir,
319 "memberof.conf")
320 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
321 "mmr_serverids.conf")
322 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
323 "mmr_syncrepl.conf")
324 paths.olcdir = os.path.join(paths.ldapdir,
325 "slapd.d")
326 paths.olcseedldif = os.path.join(paths.ldapdir,
327 "olc_seed.ldif")
328 paths.hklm = "hklm.ldb"
329 paths.hkcr = "hkcr.ldb"
330 paths.hkcu = "hkcu.ldb"
331 paths.hku = "hku.ldb"
332 paths.hkpd = "hkpd.ldb"
333 paths.hkpt = "hkpt.ldb"
335 paths.sysvol = lp.get("path", "sysvol")
337 paths.netlogon = lp.get("path", "netlogon")
339 paths.smbconf = lp.configfile
341 return paths
344 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
345 serverrole=None, rootdn=None, domaindn=None, configdn=None,
346 schemadn=None, serverdn=None, sitename=None):
347 """Guess configuration settings to use."""
349 if hostname is None:
350 hostname = socket.gethostname().split(".")[0]
352 netbiosname = lp.get("netbios name")
353 if netbiosname is None:
354 netbiosname = hostname
355 assert netbiosname is not None
356 netbiosname = netbiosname.upper()
357 if not valid_netbios_name(netbiosname):
358 raise InvalidNetbiosName(netbiosname)
360 if dnsdomain is None:
361 dnsdomain = lp.get("realm")
362 assert dnsdomain is not None
363 dnsdomain = dnsdomain.lower()
365 if serverrole is None:
366 serverrole = lp.get("server role")
367 assert serverrole is not None
368 serverrole = serverrole.lower()
370 realm = dnsdomain.upper()
372 if lp.get("realm").upper() != realm:
373 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
375 if serverrole == "domain controller":
376 if domain is None:
377 domain = lp.get("workgroup")
378 assert domain is not None
379 domain = domain.upper()
381 if lp.get("workgroup").upper() != domain:
382 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
384 if domaindn is None:
385 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
386 else:
387 domain = netbiosname
388 if domaindn is None:
389 domaindn = "DC=" + netbiosname
391 if not valid_netbios_name(domain):
392 raise InvalidNetbiosName(domain)
394 if hostname.upper() == realm:
395 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
396 if netbiosname == realm:
397 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
398 if domain == realm:
399 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
401 if rootdn is None:
402 rootdn = domaindn
404 if configdn is None:
405 configdn = "CN=Configuration," + rootdn
406 if schemadn is None:
407 schemadn = "CN=Schema," + configdn
409 if sitename is None:
410 sitename=DEFAULTSITE
412 names = ProvisionNames()
413 names.rootdn = rootdn
414 names.domaindn = domaindn
415 names.configdn = configdn
416 names.schemadn = schemadn
417 names.ldapmanagerdn = "CN=Manager," + rootdn
418 names.dnsdomain = dnsdomain
419 names.domain = domain
420 names.realm = realm
421 names.netbiosname = netbiosname
422 names.hostname = hostname
423 names.sitename = sitename
424 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
426 return names
429 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
430 targetdir, sid_generator):
431 """Create a new smb.conf file based on a couple of basic settings.
433 assert smbconf is not None
434 if hostname is None:
435 hostname = socket.gethostname().split(".")[0]
436 netbiosname = hostname.upper()
438 if serverrole is None:
439 serverrole = "standalone"
441 assert serverrole in ("domain controller", "member server", "standalone")
442 if serverrole == "domain controller":
443 smbconfsuffix = "dc"
444 elif serverrole == "member server":
445 smbconfsuffix = "member"
446 elif serverrole == "standalone":
447 smbconfsuffix = "standalone"
449 if sid_generator is None:
450 sid_generator = "internal"
452 assert domain is not None
453 domain = domain.upper()
455 assert realm is not None
456 realm = realm.upper()
458 default_lp = param.LoadParm()
459 #Load non-existant file
460 if os.path.exists(smbconf):
461 default_lp.load(smbconf)
463 if targetdir is not None:
464 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
465 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
467 default_lp.set("lock dir", os.path.abspath(targetdir))
468 else:
469 privatedir_line = ""
470 lockdir_line = ""
472 if sid_generator == "internal":
473 sid_generator_line = ""
474 else:
475 sid_generator_line = "sid generator = " + sid_generator
477 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
478 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
480 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
481 smbconf, {
482 "NETBIOS_NAME": netbiosname,
483 "DOMAIN": domain,
484 "REALM": realm,
485 "SERVERROLE": serverrole,
486 "NETLOGONPATH": netlogon,
487 "SYSVOLPATH": sysvol,
488 "SIDGENERATOR_LINE": sid_generator_line,
489 "PRIVATEDIR_LINE": privatedir_line,
490 "LOCKDIR_LINE": lockdir_line
494 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
495 users_gid, wheel_gid):
496 """setup reasonable name mappings for sam names to unix names.
498 :param samdb: SamDB object.
499 :param idmap: IDmap db object.
500 :param sid: The domain sid.
501 :param domaindn: The domain DN.
502 :param root_uid: uid of the UNIX root user.
503 :param nobody_uid: uid of the UNIX nobody user.
504 :param users_gid: gid of the UNIX users group.
505 :param wheel_gid: gid of the UNIX wheel group."""
507 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
508 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
510 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
511 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
513 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
514 provision_backend, names, schema,
515 serverrole,
516 erase=False):
517 """Setup the partitions for the SAM database.
519 Alternatively, provision() may call this, and then populate the database.
521 :note: This will wipe the Sam Database!
523 :note: This function always removes the local SAM LDB file. The erase
524 parameter controls whether to erase the existing data, which
525 may not be stored locally but in LDAP.
528 assert session_info is not None
530 old_partitions = None
531 new_partitions = None
533 # We use options=["modules:"] to stop the modules loading - we
534 # just want to wipe and re-initialise the database, not start it up
536 try:
537 os.unlink(samdb_path)
538 except OSError:
539 pass
541 samdb = Ldb(url=samdb_path, session_info=session_info,
542 lp=lp, options=["modules:"])
544 ldap_backend_line = "# No LDAP backend"
545 if provision_backend.type is not "ldb":
546 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
548 samdb.transaction_start()
549 try:
550 message("Setting up sam.ldb partitions and settings")
551 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
552 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
553 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
554 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
555 "LDAP_BACKEND_LINE": ldap_backend_line,
559 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
560 "BACKEND_TYPE": provision_backend.type,
561 "SERVER_ROLE": serverrole
564 message("Setting up sam.ldb rootDSE")
565 setup_samdb_rootdse(samdb, setup_path, names)
567 except:
568 samdb.transaction_cancel()
569 raise
571 samdb.transaction_commit()
574 def secretsdb_self_join(secretsdb, domain,
575 netbiosname, machinepass, domainsid=None,
576 realm=None, dnsdomain=None,
577 keytab_path=None,
578 key_version_number=1,
579 secure_channel_type=SEC_CHAN_WKSTA):
580 """Add domain join-specific bits to a secrets database.
582 :param secretsdb: Ldb Handle to the secrets database
583 :param machinepass: Machine password
585 attrs=["whenChanged",
586 "secret",
587 "priorSecret",
588 "priorChanged",
589 "krb5Keytab",
590 "privateKeytab"]
593 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
594 msg["secureChannelType"] = str(secure_channel_type)
595 msg["flatname"] = [domain]
596 msg["objectClass"] = ["top", "primaryDomain"]
597 if realm is not None:
598 if dnsdomain is None:
599 dnsdomain = realm.lower()
600 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
601 msg["realm"] = realm
602 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
603 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
604 msg["privateKeytab"] = ["secrets.keytab"];
607 msg["secret"] = [machinepass]
608 msg["samAccountName"] = ["%s$" % netbiosname]
609 msg["secureChannelType"] = [str(secure_channel_type)]
610 if domainsid is not None:
611 msg["objectSid"] = [ndr_pack(domainsid)]
613 res = secretsdb.search(base="cn=Primary Domains",
614 attrs=attrs,
615 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
616 scope=SCOPE_ONELEVEL)
618 for del_msg in res:
619 if del_msg.dn is not msg.dn:
620 secretsdb.delete(del_msg.dn)
622 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
624 if len(res) == 1:
625 msg["priorSecret"] = res[0]["secret"]
626 msg["priorWhenChanged"] = res[0]["whenChanged"]
628 if res["privateKeytab"] is not None:
629 msg["privateKeytab"] = res[0]["privateKeytab"]
631 if res["krb5Keytab"] is not None:
632 msg["krb5Keytab"] = res[0]["krb5Keytab"]
634 for el in msg:
635 el.set_flags(ldb.FLAG_MOD_REPLACE)
636 secretsdb.modify(msg)
637 else:
638 secretsdb.add(msg)
641 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
642 dns_keytab_path, dnspass):
643 """Add DNS specific bits to a secrets database.
645 :param secretsdb: Ldb Handle to the secrets database
646 :param setup_path: Setup path function
647 :param machinepass: Machine password
649 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
650 "REALM": realm,
651 "DNSDOMAIN": dnsdomain,
652 "DNS_KEYTAB": dns_keytab_path,
653 "DNSPASS_B64": b64encode(dnspass),
657 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
658 """Setup the secrets database.
660 :param path: Path to the secrets database.
661 :param setup_path: Get the path to a setup file.
662 :param session_info: Session info.
663 :param credentials: Credentials
664 :param lp: Loadparm context
665 :return: LDB handle for the created secrets database
667 if os.path.exists(path):
668 os.unlink(path)
669 secrets_ldb = Ldb(path, session_info=session_info,
670 lp=lp)
671 secrets_ldb.erase()
672 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
673 secrets_ldb = Ldb(path, session_info=session_info,
674 lp=lp)
675 secrets_ldb.transaction_start()
676 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
678 if backend_credentials is not None and backend_credentials.authentication_requested():
679 if backend_credentials.get_bind_dn() is not None:
680 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
681 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
682 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
684 else:
685 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
686 "LDAPADMINUSER": backend_credentials.get_username(),
687 "LDAPADMINREALM": backend_credentials.get_realm(),
688 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
691 return secrets_ldb
693 def setup_privileges(path, setup_path, session_info, lp):
694 """Setup the privileges database.
696 :param path: Path to the privileges database.
697 :param setup_path: Get the path to a setup file.
698 :param session_info: Session info.
699 :param credentials: Credentials
700 :param lp: Loadparm context
701 :return: LDB handle for the created secrets database
703 if os.path.exists(path):
704 os.unlink(path)
705 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
706 privilege_ldb.erase()
707 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
710 def setup_registry(path, setup_path, session_info, lp):
711 """Setup the registry.
713 :param path: Path to the registry database
714 :param setup_path: Function that returns the path to a setup.
715 :param session_info: Session information
716 :param credentials: Credentials
717 :param lp: Loadparm context
719 reg = registry.Registry()
720 hive = registry.open_ldb(path, session_info=session_info,
721 lp_ctx=lp)
722 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
723 provision_reg = setup_path("provision.reg")
724 assert os.path.exists(provision_reg)
725 reg.diff_apply(provision_reg)
728 def setup_idmapdb(path, setup_path, session_info, lp):
729 """Setup the idmap database.
731 :param path: path to the idmap database
732 :param setup_path: Function that returns a path to a setup file
733 :param session_info: Session information
734 :param credentials: Credentials
735 :param lp: Loadparm context
737 if os.path.exists(path):
738 os.unlink(path)
740 idmap_ldb = IDmapDB(path, session_info=session_info,
741 lp=lp)
743 idmap_ldb.erase()
744 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
745 return idmap_ldb
748 def setup_samdb_rootdse(samdb, setup_path, names):
749 """Setup the SamDB rootdse.
751 :param samdb: Sam Database handle
752 :param setup_path: Obtain setup path
754 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
755 "SCHEMADN": names.schemadn,
756 "NETBIOSNAME": names.netbiosname,
757 "DNSDOMAIN": names.dnsdomain,
758 "REALM": names.realm,
759 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
760 "DOMAINDN": names.domaindn,
761 "ROOTDN": names.rootdn,
762 "CONFIGDN": names.configdn,
763 "SERVERDN": names.serverdn,
767 def setup_self_join(samdb, names,
768 machinepass, dnspass,
769 domainsid, invocationid, setup_path,
770 policyguid, policyguid_dc, domainControllerFunctionality,
771 ntdsguid):
772 """Join a host to its own domain."""
773 assert isinstance(invocationid, str)
774 if ntdsguid is not None:
775 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
776 else:
777 ntdsguid_line = ""
778 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
779 "CONFIGDN": names.configdn,
780 "SCHEMADN": names.schemadn,
781 "DOMAINDN": names.domaindn,
782 "SERVERDN": names.serverdn,
783 "INVOCATIONID": invocationid,
784 "NETBIOSNAME": names.netbiosname,
785 "DEFAULTSITE": names.sitename,
786 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
787 "MACHINEPASS_B64": b64encode(machinepass),
788 "DNSPASS_B64": b64encode(dnspass),
789 "REALM": names.realm,
790 "DOMAIN": names.domain,
791 "DNSDOMAIN": names.dnsdomain,
792 "SAMBA_VERSION_STRING": version,
793 "NTDSGUID": ntdsguid_line,
794 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
796 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
797 "POLICYGUID": policyguid,
798 "POLICYGUID_DC": policyguid_dc,
799 "DNSDOMAIN": names.dnsdomain,
800 "DOMAINSID": str(domainsid),
801 "DOMAINDN": names.domaindn})
803 # add the NTDSGUID based SPNs
804 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
805 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
806 expression="", scope=SCOPE_BASE)
807 assert isinstance(names.ntdsguid, str)
809 # Setup fSMORoleOwner entries to point at the newly created DC entry
810 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
811 "DOMAIN": names.domain,
812 "DNSDOMAIN": names.dnsdomain,
813 "DOMAINDN": names.domaindn,
814 "CONFIGDN": names.configdn,
815 "SCHEMADN": names.schemadn,
816 "DEFAULTSITE": names.sitename,
817 "SERVERDN": names.serverdn,
818 "NETBIOSNAME": names.netbiosname,
819 "NTDSGUID": names.ntdsguid
823 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
824 names, message,
825 domainsid, domainguid, policyguid, policyguid_dc,
826 fill, adminpass, krbtgtpass,
827 machinepass, invocationid, dnspass, ntdsguid,
828 serverrole, dom_for_fun_level=None,
829 schema=None):
830 """Setup a complete SAM Database.
832 :note: This will wipe the main SAM database file!
835 # ATTENTION: Do NOT change these default values without discussion with the
836 # team and/or release manager. They have a big impact on the whole program!
837 domainControllerFunctionality = DS_DC_FUNCTION_2008
839 if dom_for_fun_level is None:
840 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
841 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
842 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
844 if dom_for_fun_level > domainControllerFunctionality:
845 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!")
847 domainFunctionality = dom_for_fun_level
848 forestFunctionality = dom_for_fun_level
850 # Also wipes the database
851 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
852 provision_backend=provision_backend, session_info=session_info,
853 names=names,
854 serverrole=serverrole, schema=schema)
856 if (schema == None):
857 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
859 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
860 samdb = Ldb(session_info=session_info,
861 credentials=provision_backend.credentials, lp=lp)
863 message("Pre-loading the Samba 4 and AD schema")
865 # Load the schema from the one we computed earlier
866 samdb.set_schema_from_ldb(schema.ldb)
868 # And now we can connect to the DB - the schema won't be loaded from the DB
869 samdb.connect(path)
871 if fill == FILL_DRS:
872 return samdb
874 samdb.transaction_start()
875 try:
876 # Set the domain functionality levels onto the database.
877 # Various module (the password_hash module in particular) need
878 # to know what level of AD we are emulating.
880 # These will be fixed into the database via the database
881 # modifictions below, but we need them set from the start.
882 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
883 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
884 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
886 samdb.set_domain_sid(str(domainsid))
887 if serverrole == "domain controller":
888 samdb.set_invocation_id(invocationid)
890 message("Adding DomainDN: %s" % names.domaindn)
892 #impersonate domain admin
893 admin_session_info = admin_session(lp, str(domainsid))
894 samdb.set_session_info(admin_session_info)
895 if domainguid is not None:
896 domainguid_line = "objectGUID: %s\n-" % domainguid
897 else:
898 domainguid_line = ""
900 descr = get_domain_descriptor(domainsid)
901 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
902 "DOMAINDN": names.domaindn,
903 "DOMAINGUID": domainguid_line,
904 "DESCRIPTOR": descr
908 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
909 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
910 "DOMAINSID": str(domainsid),
911 "SCHEMADN": names.schemadn,
912 "NETBIOSNAME": names.netbiosname,
913 "DEFAULTSITE": names.sitename,
914 "CONFIGDN": names.configdn,
915 "SERVERDN": names.serverdn,
916 "POLICYGUID": policyguid,
917 "DOMAINDN": names.domaindn,
918 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
919 "SAMBA_VERSION_STRING": version
922 message("Adding configuration container")
923 descr = get_config_descriptor(domainsid);
924 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
925 "CONFIGDN": names.configdn,
926 "DESCRIPTOR": descr,
929 # The LDIF here was created when the Schema object was constructed
930 message("Setting up sam.ldb schema")
931 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
932 samdb.modify_ldif(schema.schema_dn_modify)
933 samdb.write_prefixes_from_schema()
934 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
935 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
936 {"SCHEMADN": names.schemadn})
938 message("Setting up sam.ldb configuration data")
939 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
940 "CONFIGDN": names.configdn,
941 "NETBIOSNAME": names.netbiosname,
942 "DEFAULTSITE": names.sitename,
943 "DNSDOMAIN": names.dnsdomain,
944 "DOMAIN": names.domain,
945 "SCHEMADN": names.schemadn,
946 "DOMAINDN": names.domaindn,
947 "SERVERDN": names.serverdn,
948 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
951 message("Setting up display specifiers")
952 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
953 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
954 check_all_substituted(display_specifiers_ldif)
955 samdb.add_ldif(display_specifiers_ldif)
957 message("Adding users container")
958 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
959 "DOMAINDN": names.domaindn})
960 message("Modifying users container")
961 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
962 "DOMAINDN": names.domaindn})
963 message("Adding computers container")
964 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
965 "DOMAINDN": names.domaindn})
966 message("Modifying computers container")
967 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
968 "DOMAINDN": names.domaindn})
969 message("Setting up sam.ldb data")
970 setup_add_ldif(samdb, setup_path("provision.ldif"), {
971 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
972 "DOMAINDN": names.domaindn,
973 "NETBIOSNAME": names.netbiosname,
974 "DEFAULTSITE": names.sitename,
975 "CONFIGDN": names.configdn,
976 "SERVERDN": names.serverdn,
977 "POLICYGUID_DC": policyguid_dc
980 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
981 "DOMAINDN": names.domaindn})
983 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
984 "CONFIGDN": names.configdn,
985 "SCHEMADN": names.schemadn})
986 if fill == FILL_FULL:
987 message("Setting up sam.ldb users and groups")
988 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
989 "DOMAINDN": names.domaindn,
990 "DOMAINSID": str(domainsid),
991 "CONFIGDN": names.configdn,
992 "ADMINPASS_B64": b64encode(adminpass),
993 "KRBTGTPASS_B64": b64encode(krbtgtpass),
996 if serverrole == "domain controller":
997 message("Setting up self join")
998 setup_self_join(samdb, names=names, invocationid=invocationid,
999 dnspass=dnspass,
1000 machinepass=machinepass,
1001 domainsid=domainsid, policyguid=policyguid,
1002 policyguid_dc=policyguid_dc,
1003 setup_path=setup_path,
1004 domainControllerFunctionality=domainControllerFunctionality,
1005 ntdsguid=ntdsguid)
1007 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1008 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1009 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1010 assert isinstance(names.ntdsguid, str)
1012 except:
1013 samdb.transaction_cancel()
1014 raise
1016 samdb.transaction_commit()
1017 return samdb
1020 FILL_FULL = "FULL"
1021 FILL_NT4SYNC = "NT4SYNC"
1022 FILL_DRS = "DRS"
1025 def provision(setup_dir, message, session_info,
1026 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1027 realm=None,
1028 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1029 serverdn=None,
1030 domain=None, hostname=None, hostip=None, hostip6=None,
1031 domainsid=None, adminpass=None, ldapadminpass=None,
1032 krbtgtpass=None, domainguid=None,
1033 policyguid=None, policyguid_dc=None, invocationid=None,
1034 machinepass=None, ntdsguid=None,
1035 dnspass=None, root=None, nobody=None, users=None,
1036 wheel=None, backup=None, aci=None, serverrole=None,
1037 dom_for_fun_level=None,
1038 ldap_backend_extra_port=None, backend_type=None,
1039 sitename=None,
1040 ol_mmr_urls=None, ol_olc=None,
1041 setup_ds_path=None, slapd_path=None, nosync=False,
1042 ldap_dryrun_mode=False):
1043 """Provision samba4
1045 :note: caution, this wipes all existing data!
1048 def setup_path(file):
1049 return os.path.join(setup_dir, file)
1051 if domainsid is None:
1052 domainsid = security.random_sid()
1053 else:
1054 domainsid = security.dom_sid(domainsid)
1056 # create/adapt the group policy GUIDs
1057 if policyguid is None:
1058 policyguid = str(uuid.uuid4())
1059 policyguid = policyguid.upper()
1060 if policyguid_dc is None:
1061 policyguid_dc = str(uuid.uuid4())
1062 policyguid_dc = policyguid_dc.upper()
1064 if adminpass is None:
1065 adminpass = glue.generate_random_str(12)
1066 if krbtgtpass is None:
1067 krbtgtpass = glue.generate_random_str(12)
1068 if machinepass is None:
1069 machinepass = glue.generate_random_str(12)
1070 if dnspass is None:
1071 dnspass = glue.generate_random_str(12)
1072 if ldapadminpass is None:
1073 #Make a new, random password between Samba and it's LDAP server
1074 ldapadminpass=glue.generate_random_str(12)
1076 if backend_type is None:
1077 backend_type = "ldb"
1079 sid_generator = "internal"
1080 if backend_type == "fedora-ds":
1081 sid_generator = "backend"
1083 root_uid = findnss_uid([root or "root"])
1084 nobody_uid = findnss_uid([nobody or "nobody"])
1085 users_gid = findnss_gid([users or "users"])
1086 if wheel is None:
1087 wheel_gid = findnss_gid(["wheel", "adm"])
1088 else:
1089 wheel_gid = findnss_gid([wheel])
1091 if targetdir is not None:
1092 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1093 os.makedirs(os.path.join(targetdir, "etc"))
1094 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1095 elif smbconf is None:
1096 smbconf = param.default_path()
1098 # only install a new smb.conf if there isn't one there already
1099 if not os.path.exists(smbconf):
1100 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1101 targetdir, sid_generator)
1103 lp = param.LoadParm()
1104 lp.load(smbconf)
1106 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1107 dnsdomain=realm, serverrole=serverrole,
1108 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1109 serverdn=serverdn, sitename=sitename)
1111 paths = provision_paths_from_lp(lp, names.dnsdomain)
1113 if hostip is None:
1114 try:
1115 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1116 except socket.gaierror, (socket.EAI_NODATA, msg):
1117 hostip = None
1119 if hostip6 is None:
1120 try:
1121 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1122 except socket.gaierror, (socket.EAI_NODATA, msg):
1123 hostip6 = None
1125 if serverrole is None:
1126 serverrole = lp.get("server role")
1128 assert serverrole in ("domain controller", "member server", "standalone")
1129 if invocationid is None and serverrole == "domain controller":
1130 invocationid = str(uuid.uuid4())
1132 if not os.path.exists(paths.private_dir):
1133 os.mkdir(paths.private_dir)
1134 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1135 os.mkdir(os.path.join(paths.private_dir,"tls"))
1137 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1139 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1141 if backend_type == "ldb":
1142 provision_backend = LDBBackend(backend_type,
1143 paths=paths, setup_path=setup_path,
1144 lp=lp, credentials=credentials,
1145 names=names,
1146 message=message)
1147 elif backend_type == "existing":
1148 provision_backend = ExistingBackend(backend_type,
1149 paths=paths, setup_path=setup_path,
1150 lp=lp, credentials=credentials,
1151 names=names,
1152 message=message)
1153 elif backend_type == "fedora-ds":
1154 provision_backend = FDSBackend(backend_type,
1155 paths=paths, setup_path=setup_path,
1156 lp=lp, credentials=credentials,
1157 names=names,
1158 message=message,
1159 domainsid=domainsid,
1160 schema=schema,
1161 hostname=hostname,
1162 ldapadminpass=ldapadminpass,
1163 slapd_path=slapd_path,
1164 ldap_backend_extra_port=ldap_backend_extra_port,
1165 ldap_dryrun_mode=ldap_dryrun_mode,
1166 root=root,
1167 setup_ds_path=setup_ds_path)
1168 elif backend_type == "openldap":
1169 provision_backend = OpenLDAPBackend(backend_type,
1170 paths=paths, setup_path=setup_path,
1171 lp=lp, credentials=credentials,
1172 names=names,
1173 message=message,
1174 domainsid=domainsid,
1175 schema=schema,
1176 hostname=hostname,
1177 ldapadminpass=ldapadminpass,
1178 slapd_path=slapd_path,
1179 ldap_backend_extra_port=ldap_backend_extra_port,
1180 ldap_dryrun_mode=ldap_dryrun_mode,
1181 ol_mmr_urls=ol_mmr_urls,
1182 nosync=nosync)
1183 else:
1184 raise ProvisioningError("Unknown LDAP backend type selected")
1186 provision_backend.init()
1187 provision_backend.start()
1189 # only install a new shares config db if there is none
1190 if not os.path.exists(paths.shareconf):
1191 message("Setting up share.ldb")
1192 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1193 lp=lp)
1194 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1197 message("Setting up secrets.ldb")
1198 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1199 session_info=session_info,
1200 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1202 message("Setting up the registry")
1203 setup_registry(paths.hklm, setup_path, session_info,
1204 lp=lp)
1206 message("Setting up the privileges database")
1207 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1209 message("Setting up idmap db")
1210 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1211 lp=lp)
1213 message("Setting up SAM db")
1214 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1215 provision_backend, lp, names,
1216 message,
1217 domainsid=domainsid,
1218 schema=schema, domainguid=domainguid,
1219 policyguid=policyguid, policyguid_dc=policyguid_dc,
1220 fill=samdb_fill,
1221 adminpass=adminpass, krbtgtpass=krbtgtpass,
1222 invocationid=invocationid,
1223 machinepass=machinepass, dnspass=dnspass,
1224 ntdsguid=ntdsguid, serverrole=serverrole,
1225 dom_for_fun_level=dom_for_fun_level)
1227 if serverrole == "domain controller":
1228 if paths.netlogon is None:
1229 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1230 message("Please either remove %s or see the template at %s" %
1231 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1232 assert(paths.netlogon is not None)
1234 if paths.sysvol is None:
1235 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1236 message("Please either remove %s or see the template at %s" %
1237 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1238 assert(paths.sysvol is not None)
1240 # Set up group policies (domain policy and domain controller policy)
1242 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1243 "{" + policyguid + "}")
1244 os.makedirs(policy_path, 0755)
1245 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1246 "[General]\r\nVersion=65543")
1247 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1248 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1250 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1251 "{" + policyguid_dc + "}")
1252 os.makedirs(policy_path_dc, 0755)
1253 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1254 "[General]\r\nVersion=2")
1255 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1256 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1258 if not os.path.isdir(paths.netlogon):
1259 os.makedirs(paths.netlogon, 0755)
1261 if samdb_fill == FILL_FULL:
1262 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1263 root_uid=root_uid, nobody_uid=nobody_uid,
1264 users_gid=users_gid, wheel_gid=wheel_gid)
1266 message("Setting up sam.ldb rootDSE marking as synchronized")
1267 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1269 secretsdb_self_join(secrets_ldb, domain=names.domain,
1270 realm=names.realm,
1271 dnsdomain=names.dnsdomain,
1272 netbiosname=names.netbiosname,
1273 domainsid=domainsid,
1274 machinepass=machinepass,
1275 secure_channel_type=SEC_CHAN_BDC)
1277 if serverrole == "domain controller":
1278 secretsdb_setup_dns(secrets_ldb, setup_path,
1279 realm=names.realm, dnsdomain=names.dnsdomain,
1280 dns_keytab_path=paths.dns_keytab,
1281 dnspass=dnspass)
1283 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1284 assert isinstance(domainguid, str)
1286 # Only make a zone file on the first DC, it should be replicated
1287 # with DNS replication
1288 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1289 hostip=hostip,
1290 hostip6=hostip6, hostname=names.hostname,
1291 realm=names.realm,
1292 domainguid=domainguid, ntdsguid=names.ntdsguid)
1294 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1295 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1297 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1298 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1299 keytab_name=paths.dns_keytab)
1300 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1301 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1303 create_krb5_conf(paths.krb5conf, setup_path,
1304 dnsdomain=names.dnsdomain, hostname=names.hostname,
1305 realm=names.realm)
1306 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1308 provision_backend.post_setup()
1309 provision_backend.shutdown()
1311 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1312 ldapi_url)
1314 #Now commit the secrets.ldb to disk
1315 secrets_ldb.transaction_commit()
1317 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1319 message("Once the above files are installed, your Samba4 server will be ready to use")
1320 message("Server Role: %s" % serverrole)
1321 message("Hostname: %s" % names.hostname)
1322 message("NetBIOS Domain: %s" % names.domain)
1323 message("DNS Domain: %s" % names.dnsdomain)
1324 message("DOMAIN SID: %s" % str(domainsid))
1325 if samdb_fill == FILL_FULL:
1326 message("Admin password: %s" % adminpass)
1327 if provision_backend.type is not "ldb":
1328 if provision_backend.credentials.get_bind_dn() is not None:
1329 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1330 else:
1331 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1333 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1335 if provision_backend.slapd_command_escaped is not None:
1336 # now display slapd_command_file.txt to show how slapd must be started next time
1337 message("Use later the following commandline to start slapd, then Samba:")
1338 message(provision_backend.slapd_command_escaped)
1339 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1342 result = ProvisionResult()
1343 result.domaindn = domaindn
1344 result.paths = paths
1345 result.lp = lp
1346 result.samdb = samdb
1347 return result
1351 def provision_become_dc(setup_dir=None,
1352 smbconf=None, targetdir=None, realm=None,
1353 rootdn=None, domaindn=None, schemadn=None,
1354 configdn=None, serverdn=None,
1355 domain=None, hostname=None, domainsid=None,
1356 adminpass=None, krbtgtpass=None, domainguid=None,
1357 policyguid=None, policyguid_dc=None, invocationid=None,
1358 machinepass=None,
1359 dnspass=None, root=None, nobody=None, users=None,
1360 wheel=None, backup=None, serverrole=None,
1361 ldap_backend=None, ldap_backend_type=None,
1362 sitename=None, debuglevel=1):
1364 def message(text):
1365 """print a message if quiet is not set."""
1366 print text
1368 glue.set_debug_level(debuglevel)
1370 return provision(setup_dir, message, system_session(), None,
1371 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1372 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1373 configdn=configdn, serverdn=serverdn, domain=domain,
1374 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1375 machinepass=machinepass, serverrole="domain controller",
1376 sitename=sitename)
1379 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1380 """Create a PHP LDAP admin configuration file.
1382 :param path: Path to write the configuration to.
1383 :param setup_path: Function to generate setup paths.
1385 setup_file(setup_path("phpldapadmin-config.php"), path,
1386 {"S4_LDAPI_URI": ldapi_uri})
1389 def create_zone_file(path, setup_path, dnsdomain,
1390 hostip, hostip6, hostname, realm, domainguid,
1391 ntdsguid):
1392 """Write out a DNS zone file, from the info in the current database.
1394 :param path: Path of the new zone file.
1395 :param setup_path: Setup path function.
1396 :param dnsdomain: DNS Domain name
1397 :param domaindn: DN of the Domain
1398 :param hostip: Local IPv4 IP
1399 :param hostip6: Local IPv6 IP
1400 :param hostname: Local hostname
1401 :param realm: Realm name
1402 :param domainguid: GUID of the domain.
1403 :param ntdsguid: GUID of the hosts nTDSDSA record.
1405 assert isinstance(domainguid, str)
1407 if hostip6 is not None:
1408 hostip6_base_line = " IN AAAA " + hostip6
1409 hostip6_host_line = hostname + " IN AAAA " + hostip6
1410 else:
1411 hostip6_base_line = ""
1412 hostip6_host_line = ""
1414 if hostip is not None:
1415 hostip_base_line = " IN A " + hostip
1416 hostip_host_line = hostname + " IN A " + hostip
1417 else:
1418 hostip_base_line = ""
1419 hostip_host_line = ""
1421 setup_file(setup_path("provision.zone"), path, {
1422 "HOSTNAME": hostname,
1423 "DNSDOMAIN": dnsdomain,
1424 "REALM": realm,
1425 "HOSTIP_BASE_LINE": hostip_base_line,
1426 "HOSTIP_HOST_LINE": hostip_host_line,
1427 "DOMAINGUID": domainguid,
1428 "DATESTRING": time.strftime("%Y%m%d%H"),
1429 "DEFAULTSITE": DEFAULTSITE,
1430 "NTDSGUID": ntdsguid,
1431 "HOSTIP6_BASE_LINE": hostip6_base_line,
1432 "HOSTIP6_HOST_LINE": hostip6_host_line,
1436 def create_named_conf(path, setup_path, realm, dnsdomain,
1437 private_dir):
1438 """Write out a file containing zone statements suitable for inclusion in a
1439 named.conf file (including GSS-TSIG configuration).
1441 :param path: Path of the new named.conf file.
1442 :param setup_path: Setup path function.
1443 :param realm: Realm name
1444 :param dnsdomain: DNS Domain name
1445 :param private_dir: Path to private directory
1446 :param keytab_name: File name of DNS keytab file
1449 setup_file(setup_path("named.conf"), path, {
1450 "DNSDOMAIN": dnsdomain,
1451 "REALM": realm,
1452 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1453 "PRIVATE_DIR": private_dir
1456 def create_named_txt(path, setup_path, realm, dnsdomain,
1457 private_dir, keytab_name):
1458 """Write out a file containing zone statements suitable for inclusion in a
1459 named.conf file (including GSS-TSIG configuration).
1461 :param path: Path of the new named.conf file.
1462 :param setup_path: Setup path function.
1463 :param realm: Realm name
1464 :param dnsdomain: DNS Domain name
1465 :param private_dir: Path to private directory
1466 :param keytab_name: File name of DNS keytab file
1469 setup_file(setup_path("named.txt"), path, {
1470 "DNSDOMAIN": dnsdomain,
1471 "REALM": realm,
1472 "DNS_KEYTAB": keytab_name,
1473 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1474 "PRIVATE_DIR": private_dir
1477 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1478 """Write out a file containing zone statements suitable for inclusion in a
1479 named.conf file (including GSS-TSIG configuration).
1481 :param path: Path of the new named.conf file.
1482 :param setup_path: Setup path function.
1483 :param dnsdomain: DNS Domain name
1484 :param hostname: Local hostname
1485 :param realm: Realm name
1488 setup_file(setup_path("krb5.conf"), path, {
1489 "DNSDOMAIN": dnsdomain,
1490 "HOSTNAME": hostname,
1491 "REALM": realm,