s4:provision - Removed dependency on full Samba 3 schema from FDS
[Samba.git] / source4 / scripting / python / samba / provision.py
blobe2f7cd7953475b006273d7518fbbdf235006ae49
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 # - objectclass must be before password_hash, because password_hash checks
558 # that the objectclass is of type person (filled in by objectclass
559 # module when expanding the objectclass list)
560 # - partition must be last
561 # - each partition has its own module list then
562 modules_list = ["resolve_oids",
563 "rootdse",
564 "lazy_commit",
565 "paged_results",
566 "ranged_results",
567 "anr",
568 "server_sort",
569 "asq",
570 "extended_dn_store",
571 "extended_dn_in",
572 "rdn_name",
573 "objectclass",
574 "descriptor",
575 "acl",
576 "samldb",
577 "password_hash",
578 "operational",
579 "kludge_acl",
580 "instancetype"]
581 tdb_modules_list = [
582 "subtree_rename",
583 "subtree_delete",
584 "linked_attributes",
585 "extended_dn_out_ldb"]
586 modules_list2 = ["show_deleted",
587 "schema_load",
588 "new_partition",
589 "partition"]
591 ldap_backend_line = "# No LDAP backend"
592 if provision_backend.type is not "ldb":
593 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
595 if provision_backend.ldap_backend_type == "fedora-ds":
596 backend_modules = ["nsuniqueid", "paged_searches"]
597 # We can handle linked attributes here, as we don't have directory-side subtree operations
598 tdb_modules_list = ["extended_dn_out_fds"]
599 elif provision_backend.ldap_backend_type == "openldap":
600 backend_modules = ["entryuuid", "paged_searches"]
601 # OpenLDAP handles subtree renames, so we don't want to do any of these things
602 tdb_modules_list = ["extended_dn_out_openldap"]
604 elif serverrole == "domain controller":
605 tdb_modules_list.insert(0, "repl_meta_data")
606 backend_modules = []
607 else:
608 backend_modules = ["objectguid"]
610 if tdb_modules_list is None:
611 tdb_modules_list_as_string = ""
612 else:
613 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
615 samdb.transaction_start()
616 try:
617 message("Setting up sam.ldb partitions and settings")
618 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
619 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
620 "SCHEMADN_MOD2": ",objectguid",
621 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
622 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
623 "SCHEMADN_MOD": "schema_data",
624 "CONFIGDN_MOD": "naming_fsmo",
625 "DOMAINDN_MOD": "pdc_fsmo",
626 "MODULES_LIST": ",".join(modules_list),
627 "TDB_MODULES_LIST": tdb_modules_list_as_string,
628 "MODULES_LIST2": ",".join(modules_list2),
629 "BACKEND_MOD": ",".join(backend_modules),
630 "LDAP_BACKEND_LINE": ldap_backend_line,
634 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
636 message("Setting up sam.ldb rootDSE")
637 setup_samdb_rootdse(samdb, setup_path, names)
639 except:
640 samdb.transaction_cancel()
641 raise
643 samdb.transaction_commit()
646 def secretsdb_self_join(secretsdb, domain,
647 netbiosname, domainsid, machinepass,
648 realm=None, dnsdomain=None,
649 keytab_path=None,
650 key_version_number=1,
651 secure_channel_type=SEC_CHAN_WKSTA):
652 """Add domain join-specific bits to a secrets database.
654 :param secretsdb: Ldb Handle to the secrets database
655 :param machinepass: Machine password
657 attrs=["whenChanged",
658 "secret",
659 "priorSecret",
660 "priorChanged",
661 "krb5Keytab",
662 "privateKeytab"]
665 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
666 msg["secureChannelType"] = str(secure_channel_type)
667 msg["flatname"] = [domain]
668 msg["objectClass"] = ["top", "primaryDomain"]
669 if realm is not None:
670 if dnsdomain is None:
671 dnsdomain = realm.lower()
672 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
673 msg["realm"] = realm
674 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
675 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
676 msg["privateKeytab"] = ["secrets.keytab"];
679 msg["secret"] = [machinepass]
680 msg["samAccountName"] = ["%s$" % netbiosname]
681 msg["secureChannelType"] = [str(secure_channel_type)]
682 msg["objectSid"] = [ndr_pack(domainsid)]
684 res = secretsdb.search(base="cn=Primary Domains",
685 attrs=attrs,
686 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
687 scope=SCOPE_ONELEVEL)
689 for del_msg in res:
690 if del_msg.dn is not msg.dn:
691 secretsdb.delete(del_msg.dn)
693 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
695 if len(res) == 1:
696 msg["priorSecret"] = res[0]["secret"]
697 msg["priorWhenChanged"] = res[0]["whenChanged"]
699 if res["privateKeytab"] is not None:
700 msg["privateKeytab"] = res[0]["privateKeytab"]
702 if res["krb5Keytab"] is not None:
703 msg["krb5Keytab"] = res[0]["krb5Keytab"]
705 for el in msg:
706 el.set_flags(ldb.FLAG_MOD_REPLACE)
707 secretsdb.modify(msg)
708 else:
709 secretsdb.add(msg)
712 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
713 dns_keytab_path, dnspass):
714 """Add DNS specific bits to a secrets database.
716 :param secretsdb: Ldb Handle to the secrets database
717 :param setup_path: Setup path function
718 :param machinepass: Machine password
720 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
721 "REALM": realm,
722 "DNSDOMAIN": dnsdomain,
723 "DNS_KEYTAB": dns_keytab_path,
724 "DNSPASS_B64": b64encode(dnspass),
728 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
729 """Setup the secrets database.
731 :param path: Path to the secrets database.
732 :param setup_path: Get the path to a setup file.
733 :param session_info: Session info.
734 :param credentials: Credentials
735 :param lp: Loadparm context
736 :return: LDB handle for the created secrets database
738 if os.path.exists(path):
739 os.unlink(path)
740 secrets_ldb = Ldb(path, session_info=session_info,
741 lp=lp)
742 secrets_ldb.erase()
743 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
744 secrets_ldb = Ldb(path, session_info=session_info,
745 lp=lp)
746 secrets_ldb.transaction_start()
747 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
749 if backend_credentials is not None and backend_credentials.authentication_requested():
750 if backend_credentials.get_bind_dn() is not None:
751 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
752 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
753 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
755 else:
756 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
757 "LDAPADMINUSER": backend_credentials.get_username(),
758 "LDAPADMINREALM": backend_credentials.get_realm(),
759 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
762 return secrets_ldb
764 def setup_privileges(path, setup_path, session_info, lp):
765 """Setup the privileges database.
767 :param path: Path to the privileges database.
768 :param setup_path: Get the path to a setup file.
769 :param session_info: Session info.
770 :param credentials: Credentials
771 :param lp: Loadparm context
772 :return: LDB handle for the created secrets database
774 if os.path.exists(path):
775 os.unlink(path)
776 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
777 privilege_ldb.erase()
778 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
781 def setup_registry(path, setup_path, session_info, lp):
782 """Setup the registry.
784 :param path: Path to the registry database
785 :param setup_path: Function that returns the path to a setup.
786 :param session_info: Session information
787 :param credentials: Credentials
788 :param lp: Loadparm context
790 reg = registry.Registry()
791 hive = registry.open_ldb(path, session_info=session_info,
792 lp_ctx=lp)
793 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
794 provision_reg = setup_path("provision.reg")
795 assert os.path.exists(provision_reg)
796 reg.diff_apply(provision_reg)
799 def setup_idmapdb(path, setup_path, session_info, lp):
800 """Setup the idmap database.
802 :param path: path to the idmap database
803 :param setup_path: Function that returns a path to a setup file
804 :param session_info: Session information
805 :param credentials: Credentials
806 :param lp: Loadparm context
808 if os.path.exists(path):
809 os.unlink(path)
811 idmap_ldb = IDmapDB(path, session_info=session_info,
812 lp=lp)
814 idmap_ldb.erase()
815 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
816 return idmap_ldb
819 def setup_samdb_rootdse(samdb, setup_path, names):
820 """Setup the SamDB rootdse.
822 :param samdb: Sam Database handle
823 :param setup_path: Obtain setup path
825 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
826 "SCHEMADN": names.schemadn,
827 "NETBIOSNAME": names.netbiosname,
828 "DNSDOMAIN": names.dnsdomain,
829 "REALM": names.realm,
830 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
831 "DOMAINDN": names.domaindn,
832 "ROOTDN": names.rootdn,
833 "CONFIGDN": names.configdn,
834 "SERVERDN": names.serverdn,
838 def setup_self_join(samdb, names,
839 machinepass, dnspass,
840 domainsid, invocationid, setup_path,
841 policyguid, policyguid_dc, domainControllerFunctionality,
842 ntdsguid):
843 """Join a host to its own domain."""
844 assert isinstance(invocationid, str)
845 if ntdsguid is not None:
846 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
847 else:
848 ntdsguid_line = ""
849 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
850 "CONFIGDN": names.configdn,
851 "SCHEMADN": names.schemadn,
852 "DOMAINDN": names.domaindn,
853 "SERVERDN": names.serverdn,
854 "INVOCATIONID": invocationid,
855 "NETBIOSNAME": names.netbiosname,
856 "DEFAULTSITE": names.sitename,
857 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
858 "MACHINEPASS_B64": b64encode(machinepass),
859 "DNSPASS_B64": b64encode(dnspass),
860 "REALM": names.realm,
861 "DOMAIN": names.domain,
862 "DNSDOMAIN": names.dnsdomain,
863 "SAMBA_VERSION_STRING": version,
864 "NTDSGUID": ntdsguid_line,
865 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
867 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
868 "POLICYGUID": policyguid,
869 "POLICYGUID_DC": policyguid_dc,
870 "DNSDOMAIN": names.dnsdomain,
871 "DOMAINSID": str(domainsid),
872 "DOMAINDN": names.domaindn})
874 # add the NTDSGUID based SPNs
875 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
876 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
877 expression="", scope=SCOPE_BASE)
878 assert isinstance(names.ntdsguid, str)
880 # Setup fSMORoleOwner entries to point at the newly created DC entry
881 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
882 "DOMAIN": names.domain,
883 "DNSDOMAIN": names.dnsdomain,
884 "DOMAINDN": names.domaindn,
885 "CONFIGDN": names.configdn,
886 "SCHEMADN": names.schemadn,
887 "DEFAULTSITE": names.sitename,
888 "SERVERDN": names.serverdn,
889 "NETBIOSNAME": names.netbiosname,
890 "NTDSGUID": names.ntdsguid
894 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
895 names, message,
896 domainsid, domainguid, policyguid, policyguid_dc,
897 fill, adminpass, krbtgtpass,
898 machinepass, invocationid, dnspass, ntdsguid,
899 serverrole, dom_for_fun_level=None,
900 schema=None):
901 """Setup a complete SAM Database.
903 :note: This will wipe the main SAM database file!
906 # ATTENTION: Do NOT change these default values without discussion with the
907 # team and/or release manager. They have a big impact on the whole program!
908 domainControllerFunctionality = DS_DC_FUNCTION_2008
910 if dom_for_fun_level is None:
911 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
912 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
913 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
915 if dom_for_fun_level > domainControllerFunctionality:
916 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!")
918 domainFunctionality = dom_for_fun_level
919 forestFunctionality = dom_for_fun_level
921 # Also wipes the database
922 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
923 provision_backend=provision_backend, session_info=session_info,
924 names=names,
925 serverrole=serverrole, schema=schema)
927 if (schema == None):
928 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
930 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
931 samdb = Ldb(session_info=session_info,
932 credentials=provision_backend.credentials, lp=lp)
934 message("Pre-loading the Samba 4 and AD schema")
936 # Load the schema from the one we computed earlier
937 samdb.set_schema_from_ldb(schema.ldb)
939 # And now we can connect to the DB - the schema won't be loaded from the DB
940 samdb.connect(path)
942 if fill == FILL_DRS:
943 return samdb
945 samdb.transaction_start()
946 try:
947 # Set the domain functionality levels onto the database.
948 # Various module (the password_hash module in particular) need
949 # to know what level of AD we are emulating.
951 # These will be fixed into the database via the database
952 # modifictions below, but we need them set from the start.
953 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
954 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
955 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
957 samdb.set_domain_sid(str(domainsid))
958 if serverrole == "domain controller":
959 samdb.set_invocation_id(invocationid)
961 message("Adding DomainDN: %s" % names.domaindn)
963 #impersonate domain admin
964 admin_session_info = admin_session(lp, str(domainsid))
965 samdb.set_session_info(admin_session_info)
966 if domainguid is not None:
967 domainguid_line = "objectGUID: %s\n-" % domainguid
968 else:
969 domainguid_line = ""
971 descr = get_domain_descriptor(domainsid)
972 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
973 "DOMAINDN": names.domaindn,
974 "DOMAINGUID": domainguid_line,
975 "DESCRIPTOR": descr
979 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
980 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
981 "DOMAINSID": str(domainsid),
982 "SCHEMADN": names.schemadn,
983 "NETBIOSNAME": names.netbiosname,
984 "DEFAULTSITE": names.sitename,
985 "CONFIGDN": names.configdn,
986 "SERVERDN": names.serverdn,
987 "POLICYGUID": policyguid,
988 "DOMAINDN": names.domaindn,
989 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
990 "SAMBA_VERSION_STRING": version
993 message("Adding configuration container")
994 descr = get_config_descriptor(domainsid);
995 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
996 "CONFIGDN": names.configdn,
997 "DESCRIPTOR": descr,
999 message("Modifying configuration container")
1000 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1001 "CONFIGDN": names.configdn,
1002 "SCHEMADN": names.schemadn,
1005 # The LDIF here was created when the Schema object was constructed
1006 message("Setting up sam.ldb schema")
1007 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1008 samdb.modify_ldif(schema.schema_dn_modify)
1009 samdb.write_prefixes_from_schema()
1010 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1011 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1012 {"SCHEMADN": names.schemadn})
1014 message("Setting up sam.ldb configuration data")
1015 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1016 "CONFIGDN": names.configdn,
1017 "NETBIOSNAME": names.netbiosname,
1018 "DEFAULTSITE": names.sitename,
1019 "DNSDOMAIN": names.dnsdomain,
1020 "DOMAIN": names.domain,
1021 "SCHEMADN": names.schemadn,
1022 "DOMAINDN": names.domaindn,
1023 "SERVERDN": names.serverdn,
1024 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1027 message("Setting up display specifiers")
1028 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1029 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1030 check_all_substituted(display_specifiers_ldif)
1031 samdb.add_ldif(display_specifiers_ldif)
1033 message("Adding users container")
1034 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1035 "DOMAINDN": names.domaindn})
1036 message("Modifying users container")
1037 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1038 "DOMAINDN": names.domaindn})
1039 message("Adding computers container")
1040 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1041 "DOMAINDN": names.domaindn})
1042 message("Modifying computers container")
1043 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1044 "DOMAINDN": names.domaindn})
1045 message("Setting up sam.ldb data")
1046 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1047 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1048 "DOMAINDN": names.domaindn,
1049 "NETBIOSNAME": names.netbiosname,
1050 "DEFAULTSITE": names.sitename,
1051 "CONFIGDN": names.configdn,
1052 "SERVERDN": names.serverdn,
1053 "POLICYGUID_DC": policyguid_dc
1056 if fill == FILL_FULL:
1057 message("Setting up sam.ldb users and groups")
1058 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1059 "DOMAINDN": names.domaindn,
1060 "DOMAINSID": str(domainsid),
1061 "CONFIGDN": names.configdn,
1062 "ADMINPASS_B64": b64encode(adminpass),
1063 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1066 if serverrole == "domain controller":
1067 message("Setting up self join")
1068 setup_self_join(samdb, names=names, invocationid=invocationid,
1069 dnspass=dnspass,
1070 machinepass=machinepass,
1071 domainsid=domainsid, policyguid=policyguid,
1072 policyguid_dc=policyguid_dc,
1073 setup_path=setup_path,
1074 domainControllerFunctionality=domainControllerFunctionality,
1075 ntdsguid=ntdsguid)
1077 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1078 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1079 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1080 assert isinstance(names.ntdsguid, str)
1082 except:
1083 samdb.transaction_cancel()
1084 raise
1086 samdb.transaction_commit()
1087 return samdb
1090 FILL_FULL = "FULL"
1091 FILL_NT4SYNC = "NT4SYNC"
1092 FILL_DRS = "DRS"
1095 def provision(setup_dir, message, session_info,
1096 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1097 realm=None,
1098 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1099 serverdn=None,
1100 domain=None, hostname=None, hostip=None, hostip6=None,
1101 domainsid=None, adminpass=None, ldapadminpass=None,
1102 krbtgtpass=None, domainguid=None,
1103 policyguid=None, policyguid_dc=None, invocationid=None,
1104 machinepass=None, ntdsguid=None,
1105 dnspass=None, root=None, nobody=None, users=None,
1106 wheel=None, backup=None, aci=None, serverrole=None,
1107 dom_for_fun_level=None,
1108 ldap_backend_extra_port=None, backend_type=None,
1109 sitename=None,
1110 ol_mmr_urls=None, ol_olc=None,
1111 setup_ds_path=None, slapd_path=None, nosync=False,
1112 ldap_dryrun_mode=False):
1113 """Provision samba4
1115 :note: caution, this wipes all existing data!
1118 def setup_path(file):
1119 return os.path.join(setup_dir, file)
1121 if domainsid is None:
1122 domainsid = security.random_sid()
1123 else:
1124 domainsid = security.dom_sid(domainsid)
1126 # create/adapt the group policy GUIDs
1127 if policyguid is None:
1128 policyguid = str(uuid.uuid4())
1129 policyguid = policyguid.upper()
1130 if policyguid_dc is None:
1131 policyguid_dc = str(uuid.uuid4())
1132 policyguid_dc = policyguid_dc.upper()
1134 if adminpass is None:
1135 adminpass = glue.generate_random_str(12)
1136 if krbtgtpass is None:
1137 krbtgtpass = glue.generate_random_str(12)
1138 if machinepass is None:
1139 machinepass = glue.generate_random_str(12)
1140 if dnspass is None:
1141 dnspass = glue.generate_random_str(12)
1142 if ldapadminpass is None:
1143 #Make a new, random password between Samba and it's LDAP server
1144 ldapadminpass=glue.generate_random_str(12)
1146 if backend_type is None:
1147 backend_type = "ldb"
1149 sid_generator = "internal"
1150 if backend_type == "fedora-ds":
1151 sid_generator = "backend"
1153 root_uid = findnss_uid([root or "root"])
1154 nobody_uid = findnss_uid([nobody or "nobody"])
1155 users_gid = findnss_gid([users or "users"])
1156 if wheel is None:
1157 wheel_gid = findnss_gid(["wheel", "adm"])
1158 else:
1159 wheel_gid = findnss_gid([wheel])
1161 if targetdir is not None:
1162 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1163 os.makedirs(os.path.join(targetdir, "etc"))
1164 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1165 elif smbconf is None:
1166 smbconf = param.default_path()
1168 # only install a new smb.conf if there isn't one there already
1169 if not os.path.exists(smbconf):
1170 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1171 targetdir, sid_generator)
1173 lp = param.LoadParm()
1174 lp.load(smbconf)
1176 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1177 dnsdomain=realm, serverrole=serverrole,
1178 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1179 serverdn=serverdn, sitename=sitename)
1181 paths = provision_paths_from_lp(lp, names.dnsdomain)
1183 if hostip is None:
1184 try:
1185 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1186 except socket.gaierror, (socket.EAI_NODATA, msg):
1187 hostip = None
1189 if hostip6 is None:
1190 try:
1191 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1192 except socket.gaierror, (socket.EAI_NODATA, msg):
1193 hostip6 = None
1195 if serverrole is None:
1196 serverrole = lp.get("server role")
1198 assert serverrole in ("domain controller", "member server", "standalone")
1199 if invocationid is None and serverrole == "domain controller":
1200 invocationid = str(uuid.uuid4())
1202 if not os.path.exists(paths.private_dir):
1203 os.mkdir(paths.private_dir)
1205 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1207 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1209 if backend_type == "ldb":
1210 provision_backend = LDBBackend(backend_type,
1211 paths=paths, setup_path=setup_path,
1212 lp=lp, credentials=credentials,
1213 names=names,
1214 message=message)
1215 elif backend_type == "existing":
1216 provision_backend = ExistingBackend(backend_type,
1217 paths=paths, setup_path=setup_path,
1218 lp=lp, credentials=credentials,
1219 names=names,
1220 message=message)
1221 elif backend_type == "fedora-ds":
1222 provision_backend = FDSBackend(backend_type,
1223 paths=paths, setup_path=setup_path,
1224 lp=lp, credentials=credentials,
1225 names=names,
1226 message=message,
1227 domainsid=domainsid,
1228 schema=schema,
1229 hostname=hostname,
1230 ldapadminpass=ldapadminpass,
1231 slapd_path=slapd_path,
1232 ldap_backend_extra_port=ldap_backend_extra_port,
1233 ldap_dryrun_mode=ldap_dryrun_mode,
1234 root=root,
1235 setup_ds_path=setup_ds_path)
1236 elif backend_type == "openldap":
1237 provision_backend = OpenLDAPBackend(backend_type,
1238 paths=paths, setup_path=setup_path,
1239 lp=lp, credentials=credentials,
1240 names=names,
1241 message=message,
1242 domainsid=domainsid,
1243 schema=schema,
1244 hostname=hostname,
1245 ldapadminpass=ldapadminpass,
1246 slapd_path=slapd_path,
1247 ldap_backend_extra_port=ldap_backend_extra_port,
1248 ldap_dryrun_mode=ldap_dryrun_mode,
1249 ol_mmr_urls=ol_mmr_urls,
1250 nosync=nosync)
1251 else:
1252 raise ProvisioningError("Unknown LDAP backend type selected")
1254 provision_backend.init()
1255 provision_backend.start()
1257 # only install a new shares config db if there is none
1258 if not os.path.exists(paths.shareconf):
1259 message("Setting up share.ldb")
1260 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1261 lp=lp)
1262 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1265 message("Setting up secrets.ldb")
1266 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1267 session_info=session_info,
1268 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1270 message("Setting up the registry")
1271 setup_registry(paths.hklm, setup_path, session_info,
1272 lp=lp)
1274 message("Setting up the privileges database")
1275 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1277 message("Setting up idmap db")
1278 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1279 lp=lp)
1281 message("Setting up SAM db")
1282 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1283 provision_backend, lp, names,
1284 message,
1285 domainsid=domainsid,
1286 schema=schema, domainguid=domainguid,
1287 policyguid=policyguid, policyguid_dc=policyguid_dc,
1288 fill=samdb_fill,
1289 adminpass=adminpass, krbtgtpass=krbtgtpass,
1290 invocationid=invocationid,
1291 machinepass=machinepass, dnspass=dnspass,
1292 ntdsguid=ntdsguid, serverrole=serverrole,
1293 dom_for_fun_level=dom_for_fun_level)
1295 if serverrole == "domain controller":
1296 if paths.netlogon is None:
1297 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1298 message("Please either remove %s or see the template at %s" %
1299 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1300 assert(paths.netlogon is not None)
1302 if paths.sysvol is None:
1303 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1304 message("Please either remove %s or see the template at %s" %
1305 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1306 assert(paths.sysvol is not None)
1308 # Set up group policies (domain policy and domain controller policy)
1310 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1311 "{" + policyguid + "}")
1312 os.makedirs(policy_path, 0755)
1313 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1314 "[General]\r\nVersion=65543")
1315 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1316 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1318 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1319 "{" + policyguid_dc + "}")
1320 os.makedirs(policy_path_dc, 0755)
1321 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1322 "[General]\r\nVersion=2")
1323 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1324 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1326 if not os.path.isdir(paths.netlogon):
1327 os.makedirs(paths.netlogon, 0755)
1329 if samdb_fill == FILL_FULL:
1330 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1331 root_uid=root_uid, nobody_uid=nobody_uid,
1332 users_gid=users_gid, wheel_gid=wheel_gid)
1334 message("Setting up sam.ldb rootDSE marking as synchronized")
1335 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1337 # Only make a zone file on the first DC, it should be replicated with DNS replication
1338 if serverrole == "domain controller":
1339 secretsdb_self_join(secrets_ldb, domain=domain,
1340 realm=names.realm,
1341 dnsdomain=names.dnsdomain,
1342 netbiosname=names.netbiosname,
1343 domainsid=domainsid,
1344 machinepass=machinepass,
1345 secure_channel_type=SEC_CHAN_BDC)
1347 secretsdb_setup_dns(secrets_ldb, setup_path,
1348 realm=names.realm, dnsdomain=names.dnsdomain,
1349 dns_keytab_path=paths.dns_keytab,
1350 dnspass=dnspass)
1352 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1353 assert isinstance(domainguid, str)
1355 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1356 hostip=hostip,
1357 hostip6=hostip6, hostname=names.hostname,
1358 realm=names.realm,
1359 domainguid=domainguid, ntdsguid=names.ntdsguid)
1361 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1362 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1364 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1365 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1366 keytab_name=paths.dns_keytab)
1367 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1368 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1370 create_krb5_conf(paths.krb5conf, setup_path,
1371 dnsdomain=names.dnsdomain, hostname=names.hostname,
1372 realm=names.realm)
1373 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1375 provision_backend.post_setup()
1376 provision_backend.shutdown()
1378 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1379 ldapi_url)
1381 #Now commit the secrets.ldb to disk
1382 secrets_ldb.transaction_commit()
1384 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1386 message("Once the above files are installed, your Samba4 server will be ready to use")
1387 message("Server Role: %s" % serverrole)
1388 message("Hostname: %s" % names.hostname)
1389 message("NetBIOS Domain: %s" % names.domain)
1390 message("DNS Domain: %s" % names.dnsdomain)
1391 message("DOMAIN SID: %s" % str(domainsid))
1392 if samdb_fill == FILL_FULL:
1393 message("Admin password: %s" % adminpass)
1394 if provision_backend.type is not "ldb":
1395 if provision_backend.credentials.get_bind_dn() is not None:
1396 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1397 else:
1398 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1400 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1402 if provision_backend.slapd_command_escaped is not None:
1403 # now display slapd_command_file.txt to show how slapd must be started next time
1404 message("Use later the following commandline to start slapd, then Samba:")
1405 message(provision_backend.slapd_command_escaped)
1406 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1409 result = ProvisionResult()
1410 result.domaindn = domaindn
1411 result.paths = paths
1412 result.lp = lp
1413 result.samdb = samdb
1414 return result
1418 def provision_become_dc(setup_dir=None,
1419 smbconf=None, targetdir=None, realm=None,
1420 rootdn=None, domaindn=None, schemadn=None,
1421 configdn=None, serverdn=None,
1422 domain=None, hostname=None, domainsid=None,
1423 adminpass=None, krbtgtpass=None, domainguid=None,
1424 policyguid=None, policyguid_dc=None, invocationid=None,
1425 machinepass=None,
1426 dnspass=None, root=None, nobody=None, users=None,
1427 wheel=None, backup=None, serverrole=None,
1428 ldap_backend=None, ldap_backend_type=None,
1429 sitename=None, debuglevel=1):
1431 def message(text):
1432 """print a message if quiet is not set."""
1433 print text
1435 glue.set_debug_level(debuglevel)
1437 return provision(setup_dir, message, system_session(), None,
1438 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1439 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1440 configdn=configdn, serverdn=serverdn, domain=domain,
1441 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1442 machinepass=machinepass, serverrole="domain controller",
1443 sitename=sitename)
1446 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1447 """Create a PHP LDAP admin configuration file.
1449 :param path: Path to write the configuration to.
1450 :param setup_path: Function to generate setup paths.
1452 setup_file(setup_path("phpldapadmin-config.php"), path,
1453 {"S4_LDAPI_URI": ldapi_uri})
1456 def create_zone_file(path, setup_path, dnsdomain,
1457 hostip, hostip6, hostname, realm, domainguid,
1458 ntdsguid):
1459 """Write out a DNS zone file, from the info in the current database.
1461 :param path: Path of the new zone file.
1462 :param setup_path: Setup path function.
1463 :param dnsdomain: DNS Domain name
1464 :param domaindn: DN of the Domain
1465 :param hostip: Local IPv4 IP
1466 :param hostip6: Local IPv6 IP
1467 :param hostname: Local hostname
1468 :param realm: Realm name
1469 :param domainguid: GUID of the domain.
1470 :param ntdsguid: GUID of the hosts nTDSDSA record.
1472 assert isinstance(domainguid, str)
1474 if hostip6 is not None:
1475 hostip6_base_line = " IN AAAA " + hostip6
1476 hostip6_host_line = hostname + " IN AAAA " + hostip6
1477 else:
1478 hostip6_base_line = ""
1479 hostip6_host_line = ""
1481 if hostip is not None:
1482 hostip_base_line = " IN A " + hostip
1483 hostip_host_line = hostname + " IN A " + hostip
1484 else:
1485 hostip_base_line = ""
1486 hostip_host_line = ""
1488 setup_file(setup_path("provision.zone"), path, {
1489 "HOSTNAME": hostname,
1490 "DNSDOMAIN": dnsdomain,
1491 "REALM": realm,
1492 "HOSTIP_BASE_LINE": hostip_base_line,
1493 "HOSTIP_HOST_LINE": hostip_host_line,
1494 "DOMAINGUID": domainguid,
1495 "DATESTRING": time.strftime("%Y%m%d%H"),
1496 "DEFAULTSITE": DEFAULTSITE,
1497 "NTDSGUID": ntdsguid,
1498 "HOSTIP6_BASE_LINE": hostip6_base_line,
1499 "HOSTIP6_HOST_LINE": hostip6_host_line,
1503 def create_named_conf(path, setup_path, realm, dnsdomain,
1504 private_dir):
1505 """Write out a file containing zone statements suitable for inclusion in a
1506 named.conf file (including GSS-TSIG configuration).
1508 :param path: Path of the new named.conf file.
1509 :param setup_path: Setup path function.
1510 :param realm: Realm name
1511 :param dnsdomain: DNS Domain name
1512 :param private_dir: Path to private directory
1513 :param keytab_name: File name of DNS keytab file
1516 setup_file(setup_path("named.conf"), path, {
1517 "DNSDOMAIN": dnsdomain,
1518 "REALM": realm,
1519 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1520 "PRIVATE_DIR": private_dir
1523 def create_named_txt(path, setup_path, realm, dnsdomain,
1524 private_dir, keytab_name):
1525 """Write out a file containing zone statements suitable for inclusion in a
1526 named.conf file (including GSS-TSIG configuration).
1528 :param path: Path of the new named.conf file.
1529 :param setup_path: Setup path function.
1530 :param realm: Realm name
1531 :param dnsdomain: DNS Domain name
1532 :param private_dir: Path to private directory
1533 :param keytab_name: File name of DNS keytab file
1536 setup_file(setup_path("named.txt"), path, {
1537 "DNSDOMAIN": dnsdomain,
1538 "REALM": realm,
1539 "DNS_KEYTAB": keytab_name,
1540 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1541 "PRIVATE_DIR": private_dir
1544 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1545 """Write out a file containing zone statements suitable for inclusion in a
1546 named.conf file (including GSS-TSIG configuration).
1548 :param path: Path of the new named.conf file.
1549 :param setup_path: Setup path function.
1550 :param dnsdomain: DNS Domain name
1551 :param hostname: Local hostname
1552 :param realm: Realm name
1555 setup_file(setup_path("krb5.conf"), path, {
1556 "DNSDOMAIN": dnsdomain,
1557 "HOSTNAME": hostname,
1558 "REALM": realm,