s4:provision Simplify the module list
[Samba/ekacnet.git] / source4 / scripting / python / samba / provision.py
blob334a4c9bf8a4d4f59a8bdbe59fa1ebfc3bd1cebe
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 signal import SIGTERM
57 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
59 __docformat__ = "restructuredText"
61 def find_setup_dir():
62 """Find the setup directory used by provision."""
63 dirname = os.path.dirname(__file__)
64 if "/site-packages/" in dirname:
65 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
66 for suffix in ["share/setup", "share/samba/setup", "setup"]:
67 ret = os.path.join(prefix, suffix)
68 if os.path.isdir(ret):
69 return ret
70 # In source tree
71 ret = os.path.join(dirname, "../../../setup")
72 if os.path.isdir(ret):
73 return ret
74 raise Exception("Unable to find setup directory.")
76 # descriptors of the naming contexts
77 # hard coded at this point, but will probably be changed when
78 # we enable different fsmo roles
80 def get_config_descriptor(domain_sid):
81 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
82 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
83 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
84 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
85 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
86 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
87 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
88 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
89 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
90 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
91 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
92 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
93 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
94 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
95 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
96 sec = security.descriptor.from_sddl(sddl, domain_sid)
97 return b64encode(ndr_pack(sec))
99 def get_domain_descriptor(domain_sid):
100 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
101 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
102 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
103 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
104 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
105 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
106 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
107 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
108 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
109 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
110 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-832762594-175224951-1765713900-498)" \
111 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
112 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
113 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
114 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
115 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
116 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
119 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
120 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
121 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)" \
122 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
123 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
124 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
125 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
126 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
127 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
128 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
129 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
130 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
131 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
133 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
134 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
135 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
136 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
137 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
138 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
139 "(A;;RPRC;;;RU)" \
140 "(A;CI;LC;;;RU)" \
141 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
142 "(A;;RP;;;WD)" \
143 "(A;;RPLCLORC;;;ED)" \
144 "(A;;RPLCLORC;;;AU)" \
145 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
146 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
147 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
148 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
149 sec = security.descriptor.from_sddl(sddl, domain_sid)
150 return b64encode(ndr_pack(sec))
152 DEFAULTSITE = "Default-First-Site-Name"
154 # Exception classes
156 class ProvisioningError(Exception):
157 """A generic provision error."""
159 class InvalidNetbiosName(Exception):
160 """A specified name was not a valid NetBIOS name."""
161 def __init__(self, name):
162 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
165 class ProvisionPaths(object):
166 def __init__(self):
167 self.shareconf = None
168 self.hklm = None
169 self.hkcu = None
170 self.hkcr = None
171 self.hku = None
172 self.hkpd = None
173 self.hkpt = None
174 self.samdb = None
175 self.idmapdb = None
176 self.secrets = None
177 self.keytab = None
178 self.dns_keytab = None
179 self.dns = None
180 self.winsdb = None
181 self.private_dir = None
182 self.ldapdir = None
183 self.slapdconf = None
184 self.modulesconf = None
185 self.memberofconf = None
186 self.olmmron = None
187 self.olmmrserveridsconf = None
188 self.olmmrsyncreplconf = None
189 self.olcdir = None
190 self.olslapd = None
191 self.olcseedldif = None
194 class ProvisionNames(object):
195 def __init__(self):
196 self.rootdn = None
197 self.domaindn = None
198 self.configdn = None
199 self.schemadn = None
200 self.ldapmanagerdn = None
201 self.dnsdomain = None
202 self.realm = None
203 self.netbiosname = None
204 self.domain = None
205 self.hostname = None
206 self.sitename = None
207 self.smbconf = None
210 class ProvisionResult(object):
211 def __init__(self):
212 self.paths = None
213 self.domaindn = None
214 self.lp = None
215 self.samdb = None
217 def check_install(lp, session_info, credentials):
218 """Check whether the current install seems ok.
220 :param lp: Loadparm context
221 :param session_info: Session information
222 :param credentials: Credentials
224 if lp.get("realm") == "":
225 raise Exception("Realm empty")
226 ldb = Ldb(lp.get("sam database"), session_info=session_info,
227 credentials=credentials, lp=lp)
228 if len(ldb.search("(cn=Administrator)")) != 1:
229 raise ProvisioningError("No administrator account found")
232 def findnss(nssfn, names):
233 """Find a user or group from a list of possibilities.
235 :param nssfn: NSS Function to try (should raise KeyError if not found)
236 :param names: Names to check.
237 :return: Value return by first names list.
239 for name in names:
240 try:
241 return nssfn(name)
242 except KeyError:
243 pass
244 raise KeyError("Unable to find user/group %r" % names)
247 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
248 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
251 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
252 """Setup a ldb in the private dir.
254 :param ldb: LDB file to import data into
255 :param ldif_path: Path of the LDIF file to load
256 :param subst_vars: Optional variables to subsitute in LDIF.
257 :param nocontrols: Optional list of controls, can be None for no controls
259 assert isinstance(ldif_path, str)
260 data = read_and_sub_file(ldif_path, subst_vars)
261 ldb.add_ldif(data,controls)
264 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
265 """Modify a ldb in the private dir.
267 :param ldb: LDB object.
268 :param ldif_path: LDIF file path.
269 :param subst_vars: Optional dictionary with substitution variables.
271 data = read_and_sub_file(ldif_path, subst_vars)
273 ldb.modify_ldif(data)
276 def setup_ldb(ldb, ldif_path, subst_vars):
277 """Import a LDIF a file into a LDB handle, optionally substituting variables.
279 :note: Either all LDIF data will be added or none (using transactions).
281 :param ldb: LDB file to import into.
282 :param ldif_path: Path to the LDIF file.
283 :param subst_vars: Dictionary with substitution variables.
285 assert ldb is not None
286 ldb.transaction_start()
287 try:
288 setup_add_ldif(ldb, ldif_path, subst_vars)
289 except:
290 ldb.transaction_cancel()
291 raise
292 ldb.transaction_commit()
295 def provision_paths_from_lp(lp, dnsdomain):
296 """Set the default paths for provisioning.
298 :param lp: Loadparm context.
299 :param dnsdomain: DNS Domain name
301 paths = ProvisionPaths()
302 paths.private_dir = lp.get("private dir")
303 paths.dns_keytab = "dns.keytab"
305 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
306 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
307 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
308 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
309 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
310 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
311 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
312 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
313 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
314 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
315 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
316 paths.phpldapadminconfig = os.path.join(paths.private_dir,
317 "phpldapadmin-config.php")
318 paths.ldapdir = os.path.join(paths.private_dir,
319 "ldap")
320 paths.slapdconf = os.path.join(paths.ldapdir,
321 "slapd.conf")
322 paths.slapdpid = os.path.join(paths.ldapdir,
323 "slapd.pid")
324 paths.modulesconf = os.path.join(paths.ldapdir,
325 "modules.conf")
326 paths.memberofconf = os.path.join(paths.ldapdir,
327 "memberof.conf")
328 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
329 "mmr_serverids.conf")
330 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
331 "mmr_syncrepl.conf")
332 paths.olcdir = os.path.join(paths.ldapdir,
333 "slapd.d")
334 paths.olcseedldif = os.path.join(paths.ldapdir,
335 "olc_seed.ldif")
336 paths.hklm = "hklm.ldb"
337 paths.hkcr = "hkcr.ldb"
338 paths.hkcu = "hkcu.ldb"
339 paths.hku = "hku.ldb"
340 paths.hkpd = "hkpd.ldb"
341 paths.hkpt = "hkpt.ldb"
343 paths.sysvol = lp.get("path", "sysvol")
345 paths.netlogon = lp.get("path", "netlogon")
347 paths.smbconf = lp.configfile
349 return paths
352 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
353 serverrole=None, rootdn=None, domaindn=None, configdn=None,
354 schemadn=None, serverdn=None, sitename=None):
355 """Guess configuration settings to use."""
357 if hostname is None:
358 hostname = socket.gethostname().split(".")[0]
360 netbiosname = lp.get("netbios name")
361 if netbiosname is None:
362 netbiosname = hostname
363 assert netbiosname is not None
364 netbiosname = netbiosname.upper()
365 if not valid_netbios_name(netbiosname):
366 raise InvalidNetbiosName(netbiosname)
368 if dnsdomain is None:
369 dnsdomain = lp.get("realm")
370 assert dnsdomain is not None
371 dnsdomain = dnsdomain.lower()
373 if serverrole is None:
374 serverrole = lp.get("server role")
375 assert serverrole is not None
376 serverrole = serverrole.lower()
378 realm = dnsdomain.upper()
380 if lp.get("realm").upper() != realm:
381 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
383 if serverrole == "domain controller":
384 if domain is None:
385 domain = lp.get("workgroup")
386 assert domain is not None
387 domain = domain.upper()
389 if lp.get("workgroup").upper() != domain:
390 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
392 if domaindn is None:
393 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
394 else:
395 domain = netbiosname
396 if domaindn is None:
397 domaindn = "DC=" + netbiosname
399 if not valid_netbios_name(domain):
400 raise InvalidNetbiosName(domain)
402 if hostname.upper() == realm:
403 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
404 if netbiosname == realm:
405 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
406 if domain == realm:
407 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
409 if rootdn is None:
410 rootdn = domaindn
412 if configdn is None:
413 configdn = "CN=Configuration," + rootdn
414 if schemadn is None:
415 schemadn = "CN=Schema," + configdn
417 if sitename is None:
418 sitename=DEFAULTSITE
420 names = ProvisionNames()
421 names.rootdn = rootdn
422 names.domaindn = domaindn
423 names.configdn = configdn
424 names.schemadn = schemadn
425 names.ldapmanagerdn = "CN=Manager," + rootdn
426 names.dnsdomain = dnsdomain
427 names.domain = domain
428 names.realm = realm
429 names.netbiosname = netbiosname
430 names.hostname = hostname
431 names.sitename = sitename
432 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
434 return names
437 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
438 targetdir, sid_generator):
439 """Create a new smb.conf file based on a couple of basic settings.
441 assert smbconf is not None
442 if hostname is None:
443 hostname = socket.gethostname().split(".")[0]
444 netbiosname = hostname.upper()
446 if serverrole is None:
447 serverrole = "standalone"
449 assert serverrole in ("domain controller", "member server", "standalone")
450 if serverrole == "domain controller":
451 smbconfsuffix = "dc"
452 elif serverrole == "member server":
453 smbconfsuffix = "member"
454 elif serverrole == "standalone":
455 smbconfsuffix = "standalone"
457 if sid_generator is None:
458 sid_generator = "internal"
460 assert domain is not None
461 domain = domain.upper()
463 assert realm is not None
464 realm = realm.upper()
466 default_lp = param.LoadParm()
467 #Load non-existant file
468 if os.path.exists(smbconf):
469 default_lp.load(smbconf)
471 if targetdir is not None:
472 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
473 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
475 default_lp.set("lock dir", os.path.abspath(targetdir))
476 else:
477 privatedir_line = ""
478 lockdir_line = ""
480 if sid_generator == "internal":
481 sid_generator_line = ""
482 else:
483 sid_generator_line = "sid generator = " + sid_generator
485 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
486 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
488 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
489 smbconf, {
490 "NETBIOS_NAME": netbiosname,
491 "DOMAIN": domain,
492 "REALM": realm,
493 "SERVERROLE": serverrole,
494 "NETLOGONPATH": netlogon,
495 "SYSVOLPATH": sysvol,
496 "SIDGENERATOR_LINE": sid_generator_line,
497 "PRIVATEDIR_LINE": privatedir_line,
498 "LOCKDIR_LINE": lockdir_line
502 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
503 users_gid, wheel_gid):
504 """setup reasonable name mappings for sam names to unix names.
506 :param samdb: SamDB object.
507 :param idmap: IDmap db object.
508 :param sid: The domain sid.
509 :param domaindn: The domain DN.
510 :param root_uid: uid of the UNIX root user.
511 :param nobody_uid: uid of the UNIX nobody user.
512 :param users_gid: gid of the UNIX users group.
513 :param wheel_gid: gid of the UNIX wheel group."""
515 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
516 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
518 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
519 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
521 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
522 provision_backend, names, schema,
523 serverrole,
524 erase=False):
525 """Setup the partitions for the SAM database.
527 Alternatively, provision() may call this, and then populate the database.
529 :note: This will wipe the Sam Database!
531 :note: This function always removes the local SAM LDB file. The erase
532 parameter controls whether to erase the existing data, which
533 may not be stored locally but in LDAP.
536 assert session_info is not None
538 old_partitions = None
539 new_partitions = None
541 # We use options=["modules:"] to stop the modules loading - we
542 # just want to wipe and re-initialise the database, not start it up
544 try:
545 os.unlink(samdb_path)
546 except OSError:
547 pass
549 samdb = Ldb(url=samdb_path, session_info=session_info,
550 lp=lp, options=["modules:"])
552 #Add modules to the list to activate them by default
553 #beware often order is important
555 # Some Known ordering constraints:
556 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
557 # - extended_dn_in must be before objectclass.c, as it resolves the DN
558 # - objectclass must be before password_hash, because password_hash checks
559 # that the objectclass is of type person (filled in by objectclass
560 # module when expanding the objectclass list)
561 # - partition must be last
562 # - each partition has its own module list then
563 modules_list = ["resolve_oids",
564 "rootdse",
565 "lazy_commit",
566 "paged_results",
567 "ranged_results",
568 "anr",
569 "server_sort",
570 "asq",
571 "extended_dn_store",
572 "extended_dn_in",
573 "rdn_name",
574 "objectclass",
575 "descriptor",
576 "acl",
577 "samldb",
578 "password_hash",
579 "operational",
580 "kludge_acl",
581 "schema_load",
582 "instancetype"]
583 if serverrole == "domain controller":
584 objectguid_module = "repl_meta_data"
585 else:
586 objectguid_module = "objectguid"
587 tdb_modules_list = [
588 "subtree_rename",
589 "subtree_delete",
590 "linked_attributes"]
591 extended_dn_module = "extended_dn_out_ldb"
592 modules_list2 = ["show_deleted",
593 "new_partition",
594 "partition"]
596 backend_modules = []
598 ldap_backend_line = "# No LDAP backend"
599 if provision_backend.type is not "ldb":
600 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
602 # The LDAP backends assign the objectGUID on thier own
603 objectguid_module = None
604 # OpenLDAP and FDS handles subtree renames, so we don't want to do any of these things
605 tdb_modules_list = []
607 if provision_backend.ldap_backend_type == "fedora-ds":
608 backend_modules = ["nsuniqueid", "paged_searches"]
609 extended_dn_module_list = ["extended_dn_out_fds"];
610 elif provision_backend.ldap_backend_type == "openldap":
611 backend_modules = ["entryuuid", "paged_searches"]
612 extended_dn_module_list = ["extended_dn_out_openldap"]
614 if objectguid_module is not None:
615 modules_list.append(objectguid_module)
617 for m in tdb_modules_list:
618 modules_list.append(m)
620 modules_list.append(extended_dn_module)
622 for m in modules_list2:
623 modules_list.append(m)
625 samdb.transaction_start()
626 try:
627 message("Setting up sam.ldb partitions and settings")
628 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
629 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
630 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
631 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
632 "SCHEMADN_MOD": "schema_data",
633 "CONFIGDN_MOD": "naming_fsmo",
634 "DOMAINDN_MOD": "pdc_fsmo",
635 "MODULES_LIST": ",".join(modules_list),
636 "BACKEND_MOD": ",".join(backend_modules),
637 "LDAP_BACKEND_LINE": ldap_backend_line,
641 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
643 message("Setting up sam.ldb rootDSE")
644 setup_samdb_rootdse(samdb, setup_path, names)
646 except:
647 samdb.transaction_cancel()
648 raise
650 samdb.transaction_commit()
653 def secretsdb_self_join(secretsdb, domain,
654 netbiosname, domainsid, machinepass,
655 realm=None, dnsdomain=None,
656 keytab_path=None,
657 key_version_number=1,
658 secure_channel_type=SEC_CHAN_WKSTA):
659 """Add domain join-specific bits to a secrets database.
661 :param secretsdb: Ldb Handle to the secrets database
662 :param machinepass: Machine password
664 attrs=["whenChanged",
665 "secret",
666 "priorSecret",
667 "priorChanged",
668 "krb5Keytab",
669 "privateKeytab"]
672 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
673 msg["secureChannelType"] = str(secure_channel_type)
674 msg["flatname"] = [domain]
675 msg["objectClass"] = ["top", "primaryDomain"]
676 if realm is not None:
677 if dnsdomain is None:
678 dnsdomain = realm.lower()
679 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
680 msg["realm"] = realm
681 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
682 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
683 msg["privateKeytab"] = ["secrets.keytab"];
686 msg["secret"] = [machinepass]
687 msg["samAccountName"] = ["%s$" % netbiosname]
688 msg["secureChannelType"] = [str(secure_channel_type)]
689 msg["objectSid"] = [ndr_pack(domainsid)]
691 res = secretsdb.search(base="cn=Primary Domains",
692 attrs=attrs,
693 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
694 scope=SCOPE_ONELEVEL)
696 for del_msg in res:
697 if del_msg.dn is not msg.dn:
698 secretsdb.delete(del_msg.dn)
700 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
702 if len(res) == 1:
703 msg["priorSecret"] = res[0]["secret"]
704 msg["priorWhenChanged"] = res[0]["whenChanged"]
706 if res["privateKeytab"] is not None:
707 msg["privateKeytab"] = res[0]["privateKeytab"]
709 if res["krb5Keytab"] is not None:
710 msg["krb5Keytab"] = res[0]["krb5Keytab"]
712 for el in msg:
713 el.set_flags(ldb.FLAG_MOD_REPLACE)
714 secretsdb.modify(msg)
715 else:
716 secretsdb.add(msg)
719 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
720 dns_keytab_path, dnspass):
721 """Add DNS specific bits to a secrets database.
723 :param secretsdb: Ldb Handle to the secrets database
724 :param setup_path: Setup path function
725 :param machinepass: Machine password
727 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
728 "REALM": realm,
729 "DNSDOMAIN": dnsdomain,
730 "DNS_KEYTAB": dns_keytab_path,
731 "DNSPASS_B64": b64encode(dnspass),
735 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
736 """Setup the secrets database.
738 :param path: Path to the secrets database.
739 :param setup_path: Get the path to a setup file.
740 :param session_info: Session info.
741 :param credentials: Credentials
742 :param lp: Loadparm context
743 :return: LDB handle for the created secrets database
745 if os.path.exists(path):
746 os.unlink(path)
747 secrets_ldb = Ldb(path, session_info=session_info,
748 lp=lp)
749 secrets_ldb.erase()
750 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
751 secrets_ldb = Ldb(path, session_info=session_info,
752 lp=lp)
753 secrets_ldb.transaction_start()
754 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
756 if backend_credentials is not None and backend_credentials.authentication_requested():
757 if backend_credentials.get_bind_dn() is not None:
758 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
759 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
760 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
762 else:
763 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
764 "LDAPADMINUSER": backend_credentials.get_username(),
765 "LDAPADMINREALM": backend_credentials.get_realm(),
766 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
769 return secrets_ldb
771 def setup_privileges(path, setup_path, session_info, lp):
772 """Setup the privileges database.
774 :param path: Path to the privileges database.
775 :param setup_path: Get the path to a setup file.
776 :param session_info: Session info.
777 :param credentials: Credentials
778 :param lp: Loadparm context
779 :return: LDB handle for the created secrets database
781 if os.path.exists(path):
782 os.unlink(path)
783 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
784 privilege_ldb.erase()
785 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
788 def setup_registry(path, setup_path, session_info, lp):
789 """Setup the registry.
791 :param path: Path to the registry database
792 :param setup_path: Function that returns the path to a setup.
793 :param session_info: Session information
794 :param credentials: Credentials
795 :param lp: Loadparm context
797 reg = registry.Registry()
798 hive = registry.open_ldb(path, session_info=session_info,
799 lp_ctx=lp)
800 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
801 provision_reg = setup_path("provision.reg")
802 assert os.path.exists(provision_reg)
803 reg.diff_apply(provision_reg)
806 def setup_idmapdb(path, setup_path, session_info, lp):
807 """Setup the idmap database.
809 :param path: path to the idmap database
810 :param setup_path: Function that returns a path to a setup file
811 :param session_info: Session information
812 :param credentials: Credentials
813 :param lp: Loadparm context
815 if os.path.exists(path):
816 os.unlink(path)
818 idmap_ldb = IDmapDB(path, session_info=session_info,
819 lp=lp)
821 idmap_ldb.erase()
822 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
823 return idmap_ldb
826 def setup_samdb_rootdse(samdb, setup_path, names):
827 """Setup the SamDB rootdse.
829 :param samdb: Sam Database handle
830 :param setup_path: Obtain setup path
832 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
833 "SCHEMADN": names.schemadn,
834 "NETBIOSNAME": names.netbiosname,
835 "DNSDOMAIN": names.dnsdomain,
836 "REALM": names.realm,
837 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
838 "DOMAINDN": names.domaindn,
839 "ROOTDN": names.rootdn,
840 "CONFIGDN": names.configdn,
841 "SERVERDN": names.serverdn,
845 def setup_self_join(samdb, names,
846 machinepass, dnspass,
847 domainsid, invocationid, setup_path,
848 policyguid, policyguid_dc, domainControllerFunctionality,
849 ntdsguid):
850 """Join a host to its own domain."""
851 assert isinstance(invocationid, str)
852 if ntdsguid is not None:
853 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
854 else:
855 ntdsguid_line = ""
856 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
857 "CONFIGDN": names.configdn,
858 "SCHEMADN": names.schemadn,
859 "DOMAINDN": names.domaindn,
860 "SERVERDN": names.serverdn,
861 "INVOCATIONID": invocationid,
862 "NETBIOSNAME": names.netbiosname,
863 "DEFAULTSITE": names.sitename,
864 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
865 "MACHINEPASS_B64": b64encode(machinepass),
866 "DNSPASS_B64": b64encode(dnspass),
867 "REALM": names.realm,
868 "DOMAIN": names.domain,
869 "DNSDOMAIN": names.dnsdomain,
870 "SAMBA_VERSION_STRING": version,
871 "NTDSGUID": ntdsguid_line,
872 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
874 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
875 "POLICYGUID": policyguid,
876 "POLICYGUID_DC": policyguid_dc,
877 "DNSDOMAIN": names.dnsdomain,
878 "DOMAINSID": str(domainsid),
879 "DOMAINDN": names.domaindn})
881 # add the NTDSGUID based SPNs
882 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
883 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
884 expression="", scope=SCOPE_BASE)
885 assert isinstance(names.ntdsguid, str)
887 # Setup fSMORoleOwner entries to point at the newly created DC entry
888 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
889 "DOMAIN": names.domain,
890 "DNSDOMAIN": names.dnsdomain,
891 "DOMAINDN": names.domaindn,
892 "CONFIGDN": names.configdn,
893 "SCHEMADN": names.schemadn,
894 "DEFAULTSITE": names.sitename,
895 "SERVERDN": names.serverdn,
896 "NETBIOSNAME": names.netbiosname,
897 "NTDSGUID": names.ntdsguid
901 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
902 names, message,
903 domainsid, domainguid, policyguid, policyguid_dc,
904 fill, adminpass, krbtgtpass,
905 machinepass, invocationid, dnspass, ntdsguid,
906 serverrole, dom_for_fun_level=None,
907 schema=None):
908 """Setup a complete SAM Database.
910 :note: This will wipe the main SAM database file!
913 # ATTENTION: Do NOT change these default values without discussion with the
914 # team and/or release manager. They have a big impact on the whole program!
915 domainControllerFunctionality = DS_DC_FUNCTION_2008
917 if dom_for_fun_level is None:
918 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
919 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
920 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
922 if dom_for_fun_level > domainControllerFunctionality:
923 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!")
925 domainFunctionality = dom_for_fun_level
926 forestFunctionality = dom_for_fun_level
928 # Also wipes the database
929 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
930 provision_backend=provision_backend, session_info=session_info,
931 names=names,
932 serverrole=serverrole, schema=schema)
934 if (schema == None):
935 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
937 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
938 samdb = Ldb(session_info=session_info,
939 credentials=provision_backend.credentials, lp=lp)
941 message("Pre-loading the Samba 4 and AD schema")
943 # Load the schema from the one we computed earlier
944 samdb.set_schema_from_ldb(schema.ldb)
946 # And now we can connect to the DB - the schema won't be loaded from the DB
947 samdb.connect(path)
949 if fill == FILL_DRS:
950 return samdb
952 samdb.transaction_start()
953 try:
954 # Set the domain functionality levels onto the database.
955 # Various module (the password_hash module in particular) need
956 # to know what level of AD we are emulating.
958 # These will be fixed into the database via the database
959 # modifictions below, but we need them set from the start.
960 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
961 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
962 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
964 samdb.set_domain_sid(str(domainsid))
965 if serverrole == "domain controller":
966 samdb.set_invocation_id(invocationid)
968 message("Adding DomainDN: %s" % names.domaindn)
970 #impersonate domain admin
971 admin_session_info = admin_session(lp, str(domainsid))
972 samdb.set_session_info(admin_session_info)
973 if domainguid is not None:
974 domainguid_line = "objectGUID: %s\n-" % domainguid
975 else:
976 domainguid_line = ""
978 descr = get_domain_descriptor(domainsid)
979 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
980 "DOMAINDN": names.domaindn,
981 "DOMAINGUID": domainguid_line,
982 "DESCRIPTOR": descr
986 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
987 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
988 "DOMAINSID": str(domainsid),
989 "SCHEMADN": names.schemadn,
990 "NETBIOSNAME": names.netbiosname,
991 "DEFAULTSITE": names.sitename,
992 "CONFIGDN": names.configdn,
993 "SERVERDN": names.serverdn,
994 "POLICYGUID": policyguid,
995 "DOMAINDN": names.domaindn,
996 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
997 "SAMBA_VERSION_STRING": version
1000 message("Adding configuration container")
1001 descr = get_config_descriptor(domainsid);
1002 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1003 "CONFIGDN": names.configdn,
1004 "DESCRIPTOR": descr,
1007 # The LDIF here was created when the Schema object was constructed
1008 message("Setting up sam.ldb schema")
1009 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1010 samdb.modify_ldif(schema.schema_dn_modify)
1011 samdb.write_prefixes_from_schema()
1012 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1013 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1014 {"SCHEMADN": names.schemadn})
1016 message("Setting up sam.ldb configuration data")
1017 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1018 "CONFIGDN": names.configdn,
1019 "NETBIOSNAME": names.netbiosname,
1020 "DEFAULTSITE": names.sitename,
1021 "DNSDOMAIN": names.dnsdomain,
1022 "DOMAIN": names.domain,
1023 "SCHEMADN": names.schemadn,
1024 "DOMAINDN": names.domaindn,
1025 "SERVERDN": names.serverdn,
1026 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1029 message("Setting up display specifiers")
1030 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1031 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1032 check_all_substituted(display_specifiers_ldif)
1033 samdb.add_ldif(display_specifiers_ldif)
1035 message("Adding users container")
1036 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1037 "DOMAINDN": names.domaindn})
1038 message("Modifying users container")
1039 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1040 "DOMAINDN": names.domaindn})
1041 message("Adding computers container")
1042 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1043 "DOMAINDN": names.domaindn})
1044 message("Modifying computers container")
1045 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1046 "DOMAINDN": names.domaindn})
1047 message("Setting up sam.ldb data")
1048 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1049 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1050 "DOMAINDN": names.domaindn,
1051 "NETBIOSNAME": names.netbiosname,
1052 "DEFAULTSITE": names.sitename,
1053 "CONFIGDN": names.configdn,
1054 "SERVERDN": names.serverdn,
1055 "POLICYGUID_DC": policyguid_dc
1058 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1059 "DOMAINDN": names.domaindn})
1061 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1062 "CONFIGDN": names.configdn,
1063 "SCHEMADN": names.schemadn})
1064 if fill == FILL_FULL:
1065 message("Setting up sam.ldb users and groups")
1066 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1067 "DOMAINDN": names.domaindn,
1068 "DOMAINSID": str(domainsid),
1069 "CONFIGDN": names.configdn,
1070 "ADMINPASS_B64": b64encode(adminpass),
1071 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1074 if serverrole == "domain controller":
1075 message("Setting up self join")
1076 setup_self_join(samdb, names=names, invocationid=invocationid,
1077 dnspass=dnspass,
1078 machinepass=machinepass,
1079 domainsid=domainsid, policyguid=policyguid,
1080 policyguid_dc=policyguid_dc,
1081 setup_path=setup_path,
1082 domainControllerFunctionality=domainControllerFunctionality,
1083 ntdsguid=ntdsguid)
1085 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1086 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1087 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1088 assert isinstance(names.ntdsguid, str)
1090 except:
1091 samdb.transaction_cancel()
1092 raise
1094 samdb.transaction_commit()
1095 return samdb
1098 FILL_FULL = "FULL"
1099 FILL_NT4SYNC = "NT4SYNC"
1100 FILL_DRS = "DRS"
1103 def provision(setup_dir, message, session_info,
1104 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1105 realm=None,
1106 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1107 serverdn=None,
1108 domain=None, hostname=None, hostip=None, hostip6=None,
1109 domainsid=None, adminpass=None, ldapadminpass=None,
1110 krbtgtpass=None, domainguid=None,
1111 policyguid=None, policyguid_dc=None, invocationid=None,
1112 machinepass=None, ntdsguid=None,
1113 dnspass=None, root=None, nobody=None, users=None,
1114 wheel=None, backup=None, aci=None, serverrole=None,
1115 dom_for_fun_level=None,
1116 ldap_backend_extra_port=None, backend_type=None,
1117 sitename=None,
1118 ol_mmr_urls=None, ol_olc=None,
1119 setup_ds_path=None, slapd_path=None, nosync=False,
1120 ldap_dryrun_mode=False):
1121 """Provision samba4
1123 :note: caution, this wipes all existing data!
1126 def setup_path(file):
1127 return os.path.join(setup_dir, file)
1129 if domainsid is None:
1130 domainsid = security.random_sid()
1131 else:
1132 domainsid = security.dom_sid(domainsid)
1134 # create/adapt the group policy GUIDs
1135 if policyguid is None:
1136 policyguid = str(uuid.uuid4())
1137 policyguid = policyguid.upper()
1138 if policyguid_dc is None:
1139 policyguid_dc = str(uuid.uuid4())
1140 policyguid_dc = policyguid_dc.upper()
1142 if adminpass is None:
1143 adminpass = glue.generate_random_str(12)
1144 if krbtgtpass is None:
1145 krbtgtpass = glue.generate_random_str(12)
1146 if machinepass is None:
1147 machinepass = glue.generate_random_str(12)
1148 if dnspass is None:
1149 dnspass = glue.generate_random_str(12)
1150 if ldapadminpass is None:
1151 #Make a new, random password between Samba and it's LDAP server
1152 ldapadminpass=glue.generate_random_str(12)
1154 if backend_type is None:
1155 backend_type = "ldb"
1157 sid_generator = "internal"
1158 if backend_type == "fedora-ds":
1159 sid_generator = "backend"
1161 root_uid = findnss_uid([root or "root"])
1162 nobody_uid = findnss_uid([nobody or "nobody"])
1163 users_gid = findnss_gid([users or "users"])
1164 if wheel is None:
1165 wheel_gid = findnss_gid(["wheel", "adm"])
1166 else:
1167 wheel_gid = findnss_gid([wheel])
1169 if targetdir is not None:
1170 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1171 os.makedirs(os.path.join(targetdir, "etc"))
1172 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1173 elif smbconf is None:
1174 smbconf = param.default_path()
1176 # only install a new smb.conf if there isn't one there already
1177 if not os.path.exists(smbconf):
1178 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1179 targetdir, sid_generator)
1181 lp = param.LoadParm()
1182 lp.load(smbconf)
1184 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1185 dnsdomain=realm, serverrole=serverrole,
1186 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1187 serverdn=serverdn, sitename=sitename)
1189 paths = provision_paths_from_lp(lp, names.dnsdomain)
1191 if hostip is None:
1192 try:
1193 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1194 except socket.gaierror, (socket.EAI_NODATA, msg):
1195 hostip = None
1197 if hostip6 is None:
1198 try:
1199 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1200 except socket.gaierror, (socket.EAI_NODATA, msg):
1201 hostip6 = None
1203 if serverrole is None:
1204 serverrole = lp.get("server role")
1206 assert serverrole in ("domain controller", "member server", "standalone")
1207 if invocationid is None and serverrole == "domain controller":
1208 invocationid = str(uuid.uuid4())
1210 if not os.path.exists(paths.private_dir):
1211 os.mkdir(paths.private_dir)
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 elif backend_type == "fedora-ds":
1230 provision_backend = FDSBackend(backend_type,
1231 paths=paths, setup_path=setup_path,
1232 lp=lp, credentials=credentials,
1233 names=names,
1234 message=message,
1235 domainsid=domainsid,
1236 schema=schema,
1237 hostname=hostname,
1238 ldapadminpass=ldapadminpass,
1239 slapd_path=slapd_path,
1240 ldap_backend_extra_port=ldap_backend_extra_port,
1241 ldap_dryrun_mode=ldap_dryrun_mode,
1242 root=root,
1243 setup_ds_path=setup_ds_path)
1244 elif backend_type == "openldap":
1245 provision_backend = OpenLDAPBackend(backend_type,
1246 paths=paths, setup_path=setup_path,
1247 lp=lp, credentials=credentials,
1248 names=names,
1249 message=message,
1250 domainsid=domainsid,
1251 schema=schema,
1252 hostname=hostname,
1253 ldapadminpass=ldapadminpass,
1254 slapd_path=slapd_path,
1255 ldap_backend_extra_port=ldap_backend_extra_port,
1256 ldap_dryrun_mode=ldap_dryrun_mode,
1257 ol_mmr_urls=ol_mmr_urls,
1258 nosync=nosync)
1259 else:
1260 raise ProvisioningError("Unknown LDAP backend type selected")
1262 provision_backend.init()
1263 provision_backend.start()
1265 # only install a new shares config db if there is none
1266 if not os.path.exists(paths.shareconf):
1267 message("Setting up share.ldb")
1268 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1269 lp=lp)
1270 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1273 message("Setting up secrets.ldb")
1274 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1275 session_info=session_info,
1276 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1278 message("Setting up the registry")
1279 setup_registry(paths.hklm, setup_path, session_info,
1280 lp=lp)
1282 message("Setting up the privileges database")
1283 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1285 message("Setting up idmap db")
1286 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1287 lp=lp)
1289 message("Setting up SAM db")
1290 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1291 provision_backend, lp, names,
1292 message,
1293 domainsid=domainsid,
1294 schema=schema, domainguid=domainguid,
1295 policyguid=policyguid, policyguid_dc=policyguid_dc,
1296 fill=samdb_fill,
1297 adminpass=adminpass, krbtgtpass=krbtgtpass,
1298 invocationid=invocationid,
1299 machinepass=machinepass, dnspass=dnspass,
1300 ntdsguid=ntdsguid, serverrole=serverrole,
1301 dom_for_fun_level=dom_for_fun_level)
1303 if serverrole == "domain controller":
1304 if paths.netlogon is None:
1305 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1306 message("Please either remove %s or see the template at %s" %
1307 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1308 assert(paths.netlogon is not None)
1310 if paths.sysvol is None:
1311 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1312 message("Please either remove %s or see the template at %s" %
1313 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1314 assert(paths.sysvol is not None)
1316 # Set up group policies (domain policy and domain controller policy)
1318 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1319 "{" + policyguid + "}")
1320 os.makedirs(policy_path, 0755)
1321 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1322 "[General]\r\nVersion=65543")
1323 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1324 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1326 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1327 "{" + policyguid_dc + "}")
1328 os.makedirs(policy_path_dc, 0755)
1329 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1330 "[General]\r\nVersion=2")
1331 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1332 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1334 if not os.path.isdir(paths.netlogon):
1335 os.makedirs(paths.netlogon, 0755)
1337 if samdb_fill == FILL_FULL:
1338 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1339 root_uid=root_uid, nobody_uid=nobody_uid,
1340 users_gid=users_gid, wheel_gid=wheel_gid)
1342 message("Setting up sam.ldb rootDSE marking as synchronized")
1343 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1345 # Only make a zone file on the first DC, it should be replicated with DNS replication
1346 if serverrole == "domain controller":
1347 secretsdb_self_join(secrets_ldb, domain=domain,
1348 realm=names.realm,
1349 dnsdomain=names.dnsdomain,
1350 netbiosname=names.netbiosname,
1351 domainsid=domainsid,
1352 machinepass=machinepass,
1353 secure_channel_type=SEC_CHAN_BDC)
1355 secretsdb_setup_dns(secrets_ldb, setup_path,
1356 realm=names.realm, dnsdomain=names.dnsdomain,
1357 dns_keytab_path=paths.dns_keytab,
1358 dnspass=dnspass)
1360 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1361 assert isinstance(domainguid, str)
1363 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1364 hostip=hostip,
1365 hostip6=hostip6, hostname=names.hostname,
1366 realm=names.realm,
1367 domainguid=domainguid, ntdsguid=names.ntdsguid)
1369 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1370 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1372 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1373 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1374 keytab_name=paths.dns_keytab)
1375 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1376 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1378 create_krb5_conf(paths.krb5conf, setup_path,
1379 dnsdomain=names.dnsdomain, hostname=names.hostname,
1380 realm=names.realm)
1381 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1383 provision_backend.post_setup()
1384 provision_backend.shutdown()
1386 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1387 ldapi_url)
1389 #Now commit the secrets.ldb to disk
1390 secrets_ldb.transaction_commit()
1392 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1394 message("Once the above files are installed, your Samba4 server will be ready to use")
1395 message("Server Role: %s" % serverrole)
1396 message("Hostname: %s" % names.hostname)
1397 message("NetBIOS Domain: %s" % names.domain)
1398 message("DNS Domain: %s" % names.dnsdomain)
1399 message("DOMAIN SID: %s" % str(domainsid))
1400 if samdb_fill == FILL_FULL:
1401 message("Admin password: %s" % adminpass)
1402 if provision_backend.type is not "ldb":
1403 if provision_backend.credentials.get_bind_dn() is not None:
1404 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1405 else:
1406 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1408 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1410 if provision_backend.slapd_command_escaped is not None:
1411 # now display slapd_command_file.txt to show how slapd must be started next time
1412 message("Use later the following commandline to start slapd, then Samba:")
1413 message(provision_backend.slapd_command_escaped)
1414 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1417 result = ProvisionResult()
1418 result.domaindn = domaindn
1419 result.paths = paths
1420 result.lp = lp
1421 result.samdb = samdb
1422 return result
1426 def provision_become_dc(setup_dir=None,
1427 smbconf=None, targetdir=None, realm=None,
1428 rootdn=None, domaindn=None, schemadn=None,
1429 configdn=None, serverdn=None,
1430 domain=None, hostname=None, domainsid=None,
1431 adminpass=None, krbtgtpass=None, domainguid=None,
1432 policyguid=None, policyguid_dc=None, invocationid=None,
1433 machinepass=None,
1434 dnspass=None, root=None, nobody=None, users=None,
1435 wheel=None, backup=None, serverrole=None,
1436 ldap_backend=None, ldap_backend_type=None,
1437 sitename=None, debuglevel=1):
1439 def message(text):
1440 """print a message if quiet is not set."""
1441 print text
1443 glue.set_debug_level(debuglevel)
1445 return provision(setup_dir, message, system_session(), None,
1446 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1447 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1448 configdn=configdn, serverdn=serverdn, domain=domain,
1449 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1450 machinepass=machinepass, serverrole="domain controller",
1451 sitename=sitename)
1454 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1455 """Create a PHP LDAP admin configuration file.
1457 :param path: Path to write the configuration to.
1458 :param setup_path: Function to generate setup paths.
1460 setup_file(setup_path("phpldapadmin-config.php"), path,
1461 {"S4_LDAPI_URI": ldapi_uri})
1464 def create_zone_file(path, setup_path, dnsdomain,
1465 hostip, hostip6, hostname, realm, domainguid,
1466 ntdsguid):
1467 """Write out a DNS zone file, from the info in the current database.
1469 :param path: Path of the new zone file.
1470 :param setup_path: Setup path function.
1471 :param dnsdomain: DNS Domain name
1472 :param domaindn: DN of the Domain
1473 :param hostip: Local IPv4 IP
1474 :param hostip6: Local IPv6 IP
1475 :param hostname: Local hostname
1476 :param realm: Realm name
1477 :param domainguid: GUID of the domain.
1478 :param ntdsguid: GUID of the hosts nTDSDSA record.
1480 assert isinstance(domainguid, str)
1482 if hostip6 is not None:
1483 hostip6_base_line = " IN AAAA " + hostip6
1484 hostip6_host_line = hostname + " IN AAAA " + hostip6
1485 else:
1486 hostip6_base_line = ""
1487 hostip6_host_line = ""
1489 if hostip is not None:
1490 hostip_base_line = " IN A " + hostip
1491 hostip_host_line = hostname + " IN A " + hostip
1492 else:
1493 hostip_base_line = ""
1494 hostip_host_line = ""
1496 setup_file(setup_path("provision.zone"), path, {
1497 "HOSTNAME": hostname,
1498 "DNSDOMAIN": dnsdomain,
1499 "REALM": realm,
1500 "HOSTIP_BASE_LINE": hostip_base_line,
1501 "HOSTIP_HOST_LINE": hostip_host_line,
1502 "DOMAINGUID": domainguid,
1503 "DATESTRING": time.strftime("%Y%m%d%H"),
1504 "DEFAULTSITE": DEFAULTSITE,
1505 "NTDSGUID": ntdsguid,
1506 "HOSTIP6_BASE_LINE": hostip6_base_line,
1507 "HOSTIP6_HOST_LINE": hostip6_host_line,
1511 def create_named_conf(path, setup_path, realm, dnsdomain,
1512 private_dir):
1513 """Write out a file containing zone statements suitable for inclusion in a
1514 named.conf file (including GSS-TSIG configuration).
1516 :param path: Path of the new named.conf file.
1517 :param setup_path: Setup path function.
1518 :param realm: Realm name
1519 :param dnsdomain: DNS Domain name
1520 :param private_dir: Path to private directory
1521 :param keytab_name: File name of DNS keytab file
1524 setup_file(setup_path("named.conf"), path, {
1525 "DNSDOMAIN": dnsdomain,
1526 "REALM": realm,
1527 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1528 "PRIVATE_DIR": private_dir
1531 def create_named_txt(path, setup_path, realm, dnsdomain,
1532 private_dir, keytab_name):
1533 """Write out a file containing zone statements suitable for inclusion in a
1534 named.conf file (including GSS-TSIG configuration).
1536 :param path: Path of the new named.conf file.
1537 :param setup_path: Setup path function.
1538 :param realm: Realm name
1539 :param dnsdomain: DNS Domain name
1540 :param private_dir: Path to private directory
1541 :param keytab_name: File name of DNS keytab file
1544 setup_file(setup_path("named.txt"), path, {
1545 "DNSDOMAIN": dnsdomain,
1546 "REALM": realm,
1547 "DNS_KEYTAB": keytab_name,
1548 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1549 "PRIVATE_DIR": private_dir
1552 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1553 """Write out a file containing zone statements suitable for inclusion in a
1554 named.conf file (including GSS-TSIG configuration).
1556 :param path: Path of the new named.conf file.
1557 :param setup_path: Setup path function.
1558 :param dnsdomain: DNS Domain name
1559 :param hostname: Local hostname
1560 :param realm: Realm name
1563 setup_file(setup_path("krb5.conf"), path, {
1564 "DNSDOMAIN": dnsdomain,
1565 "HOSTNAME": hostname,
1566 "REALM": realm,