s4:provision - Added start() method in LDAPBackend.
[Samba/ekacnet.git] / source4 / scripting / python / samba / provision.py
blob1c194c042c7f90d8e19012c932e98f84b4f42fb9
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 ProvisionBackend, 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.fedoradsinf = None
187 self.fedoradspartitions = None
188 self.fedoradssasl = None
189 self.fedoradsdna = None
190 self.fedoradspam = None
191 self.fedoradsrefint = None
192 self.fedoradslinkedattributes = None
193 self.fedoradsindex = None
194 self.fedoradssamba = None
195 self.olmmron = None
196 self.olmmrserveridsconf = None
197 self.olmmrsyncreplconf = None
198 self.olcdir = None
199 self.olslapd = None
200 self.olcseedldif = None
203 class ProvisionNames(object):
204 def __init__(self):
205 self.rootdn = None
206 self.domaindn = None
207 self.configdn = None
208 self.schemadn = None
209 self.ldapmanagerdn = None
210 self.dnsdomain = None
211 self.realm = None
212 self.netbiosname = None
213 self.domain = None
214 self.hostname = None
215 self.sitename = None
216 self.smbconf = None
219 class ProvisionResult(object):
220 def __init__(self):
221 self.paths = None
222 self.domaindn = None
223 self.lp = None
224 self.samdb = None
226 def check_install(lp, session_info, credentials):
227 """Check whether the current install seems ok.
229 :param lp: Loadparm context
230 :param session_info: Session information
231 :param credentials: Credentials
233 if lp.get("realm") == "":
234 raise Exception("Realm empty")
235 ldb = Ldb(lp.get("sam database"), session_info=session_info,
236 credentials=credentials, lp=lp)
237 if len(ldb.search("(cn=Administrator)")) != 1:
238 raise ProvisioningError("No administrator account found")
241 def findnss(nssfn, names):
242 """Find a user or group from a list of possibilities.
244 :param nssfn: NSS Function to try (should raise KeyError if not found)
245 :param names: Names to check.
246 :return: Value return by first names list.
248 for name in names:
249 try:
250 return nssfn(name)
251 except KeyError:
252 pass
253 raise KeyError("Unable to find user/group %r" % names)
256 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
257 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
260 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
261 """Setup a ldb in the private dir.
263 :param ldb: LDB file to import data into
264 :param ldif_path: Path of the LDIF file to load
265 :param subst_vars: Optional variables to subsitute in LDIF.
266 :param nocontrols: Optional list of controls, can be None for no controls
268 assert isinstance(ldif_path, str)
269 data = read_and_sub_file(ldif_path, subst_vars)
270 ldb.add_ldif(data,controls)
273 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
274 """Modify a ldb in the private dir.
276 :param ldb: LDB object.
277 :param ldif_path: LDIF file path.
278 :param subst_vars: Optional dictionary with substitution variables.
280 data = read_and_sub_file(ldif_path, subst_vars)
282 ldb.modify_ldif(data)
285 def setup_ldb(ldb, ldif_path, subst_vars):
286 """Import a LDIF a file into a LDB handle, optionally substituting variables.
288 :note: Either all LDIF data will be added or none (using transactions).
290 :param ldb: LDB file to import into.
291 :param ldif_path: Path to the LDIF file.
292 :param subst_vars: Dictionary with substitution variables.
294 assert ldb is not None
295 ldb.transaction_start()
296 try:
297 setup_add_ldif(ldb, ldif_path, subst_vars)
298 except:
299 ldb.transaction_cancel()
300 raise
301 ldb.transaction_commit()
304 def provision_paths_from_lp(lp, dnsdomain):
305 """Set the default paths for provisioning.
307 :param lp: Loadparm context.
308 :param dnsdomain: DNS Domain name
310 paths = ProvisionPaths()
311 paths.private_dir = lp.get("private dir")
312 paths.dns_keytab = "dns.keytab"
314 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
315 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
316 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
317 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
318 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
319 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
320 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
321 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
322 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
323 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
324 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
325 paths.phpldapadminconfig = os.path.join(paths.private_dir,
326 "phpldapadmin-config.php")
327 paths.ldapdir = os.path.join(paths.private_dir,
328 "ldap")
329 paths.slapdconf = os.path.join(paths.ldapdir,
330 "slapd.conf")
331 paths.slapdpid = os.path.join(paths.ldapdir,
332 "slapd.pid")
333 paths.modulesconf = os.path.join(paths.ldapdir,
334 "modules.conf")
335 paths.memberofconf = os.path.join(paths.ldapdir,
336 "memberof.conf")
337 paths.fedoradsinf = os.path.join(paths.ldapdir,
338 "fedorads.inf")
339 paths.fedoradspartitions = os.path.join(paths.ldapdir,
340 "fedorads-partitions.ldif")
341 paths.fedoradssasl = os.path.join(paths.ldapdir,
342 "fedorads-sasl.ldif")
343 paths.fedoradsdna = os.path.join(paths.ldapdir,
344 "fedorads-dna.ldif")
345 paths.fedoradspam = os.path.join(paths.ldapdir,
346 "fedorads-pam.ldif")
347 paths.fedoradsrefint = os.path.join(paths.ldapdir,
348 "fedorads-refint.ldif")
349 paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
350 "fedorads-linked-attributes.ldif")
351 paths.fedoradsindex = os.path.join(paths.ldapdir,
352 "fedorads-index.ldif")
353 paths.fedoradssamba = os.path.join(paths.ldapdir,
354 "fedorads-samba.ldif")
355 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
356 "mmr_serverids.conf")
357 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
358 "mmr_syncrepl.conf")
359 paths.olcdir = os.path.join(paths.ldapdir,
360 "slapd.d")
361 paths.olcseedldif = os.path.join(paths.ldapdir,
362 "olc_seed.ldif")
363 paths.hklm = "hklm.ldb"
364 paths.hkcr = "hkcr.ldb"
365 paths.hkcu = "hkcu.ldb"
366 paths.hku = "hku.ldb"
367 paths.hkpd = "hkpd.ldb"
368 paths.hkpt = "hkpt.ldb"
370 paths.sysvol = lp.get("path", "sysvol")
372 paths.netlogon = lp.get("path", "netlogon")
374 paths.smbconf = lp.configfile
376 return paths
379 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
380 serverrole=None, rootdn=None, domaindn=None, configdn=None,
381 schemadn=None, serverdn=None, sitename=None):
382 """Guess configuration settings to use."""
384 if hostname is None:
385 hostname = socket.gethostname().split(".")[0]
387 netbiosname = lp.get("netbios name")
388 if netbiosname is None:
389 netbiosname = hostname
390 assert netbiosname is not None
391 netbiosname = netbiosname.upper()
392 if not valid_netbios_name(netbiosname):
393 raise InvalidNetbiosName(netbiosname)
395 if dnsdomain is None:
396 dnsdomain = lp.get("realm")
397 assert dnsdomain is not None
398 dnsdomain = dnsdomain.lower()
400 if serverrole is None:
401 serverrole = lp.get("server role")
402 assert serverrole is not None
403 serverrole = serverrole.lower()
405 realm = dnsdomain.upper()
407 if lp.get("realm").upper() != realm:
408 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
410 if serverrole == "domain controller":
411 if domain is None:
412 domain = lp.get("workgroup")
413 assert domain is not None
414 domain = domain.upper()
416 if lp.get("workgroup").upper() != domain:
417 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
419 if domaindn is None:
420 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
421 else:
422 domain = netbiosname
423 if domaindn is None:
424 domaindn = "DC=" + netbiosname
426 if not valid_netbios_name(domain):
427 raise InvalidNetbiosName(domain)
429 if hostname.upper() == realm:
430 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
431 if netbiosname == realm:
432 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
433 if domain == realm:
434 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
436 if rootdn is None:
437 rootdn = domaindn
439 if configdn is None:
440 configdn = "CN=Configuration," + rootdn
441 if schemadn is None:
442 schemadn = "CN=Schema," + configdn
444 if sitename is None:
445 sitename=DEFAULTSITE
447 names = ProvisionNames()
448 names.rootdn = rootdn
449 names.domaindn = domaindn
450 names.configdn = configdn
451 names.schemadn = schemadn
452 names.ldapmanagerdn = "CN=Manager," + rootdn
453 names.dnsdomain = dnsdomain
454 names.domain = domain
455 names.realm = realm
456 names.netbiosname = netbiosname
457 names.hostname = hostname
458 names.sitename = sitename
459 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
461 return names
464 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
465 targetdir, sid_generator):
466 """Create a new smb.conf file based on a couple of basic settings.
468 assert smbconf is not None
469 if hostname is None:
470 hostname = socket.gethostname().split(".")[0]
471 netbiosname = hostname.upper()
473 if serverrole is None:
474 serverrole = "standalone"
476 assert serverrole in ("domain controller", "member server", "standalone")
477 if serverrole == "domain controller":
478 smbconfsuffix = "dc"
479 elif serverrole == "member server":
480 smbconfsuffix = "member"
481 elif serverrole == "standalone":
482 smbconfsuffix = "standalone"
484 if sid_generator is None:
485 sid_generator = "internal"
487 assert domain is not None
488 domain = domain.upper()
490 assert realm is not None
491 realm = realm.upper()
493 default_lp = param.LoadParm()
494 #Load non-existant file
495 if os.path.exists(smbconf):
496 default_lp.load(smbconf)
498 if targetdir is not None:
499 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
500 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
502 default_lp.set("lock dir", os.path.abspath(targetdir))
503 else:
504 privatedir_line = ""
505 lockdir_line = ""
507 if sid_generator == "internal":
508 sid_generator_line = ""
509 else:
510 sid_generator_line = "sid generator = " + sid_generator
512 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
513 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
515 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
516 smbconf, {
517 "NETBIOS_NAME": netbiosname,
518 "DOMAIN": domain,
519 "REALM": realm,
520 "SERVERROLE": serverrole,
521 "NETLOGONPATH": netlogon,
522 "SYSVOLPATH": sysvol,
523 "SIDGENERATOR_LINE": sid_generator_line,
524 "PRIVATEDIR_LINE": privatedir_line,
525 "LOCKDIR_LINE": lockdir_line
529 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
530 users_gid, wheel_gid):
531 """setup reasonable name mappings for sam names to unix names.
533 :param samdb: SamDB object.
534 :param idmap: IDmap db object.
535 :param sid: The domain sid.
536 :param domaindn: The domain DN.
537 :param root_uid: uid of the UNIX root user.
538 :param nobody_uid: uid of the UNIX nobody user.
539 :param users_gid: gid of the UNIX users group.
540 :param wheel_gid: gid of the UNIX wheel group."""
542 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
543 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
545 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
546 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
548 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
549 provision_backend, names, schema,
550 serverrole,
551 erase=False):
552 """Setup the partitions for the SAM database.
554 Alternatively, provision() may call this, and then populate the database.
556 :note: This will wipe the Sam Database!
558 :note: This function always removes the local SAM LDB file. The erase
559 parameter controls whether to erase the existing data, which
560 may not be stored locally but in LDAP.
563 assert session_info is not None
565 old_partitions = None
566 new_partitions = None
568 # We use options=["modules:"] to stop the modules loading - we
569 # just want to wipe and re-initialise the database, not start it up
571 try:
572 os.unlink(samdb_path)
573 except OSError:
574 pass
576 samdb = Ldb(url=samdb_path, session_info=session_info,
577 lp=lp, options=["modules:"])
579 #Add modules to the list to activate them by default
580 #beware often order is important
582 # Some Known ordering constraints:
583 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
584 # - objectclass must be before password_hash, because password_hash checks
585 # that the objectclass is of type person (filled in by objectclass
586 # module when expanding the objectclass list)
587 # - partition must be last
588 # - each partition has its own module list then
589 modules_list = ["resolve_oids",
590 "rootdse",
591 "lazy_commit",
592 "paged_results",
593 "ranged_results",
594 "anr",
595 "server_sort",
596 "asq",
597 "extended_dn_store",
598 "extended_dn_in",
599 "rdn_name",
600 "objectclass",
601 "descriptor",
602 "acl",
603 "samldb",
604 "password_hash",
605 "operational",
606 "kludge_acl",
607 "instancetype"]
608 tdb_modules_list = [
609 "subtree_rename",
610 "subtree_delete",
611 "linked_attributes",
612 "extended_dn_out_ldb"]
613 modules_list2 = ["show_deleted",
614 "schema_load",
615 "new_partition",
616 "partition"]
618 ldap_backend_line = "# No LDAP backend"
619 if provision_backend.type is not "ldb":
620 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
622 if provision_backend.ldap_backend_type == "fedora-ds":
623 backend_modules = ["nsuniqueid", "paged_searches"]
624 # We can handle linked attributes here, as we don't have directory-side subtree operations
625 tdb_modules_list = ["extended_dn_out_fds"]
626 elif provision_backend.ldap_backend_type == "openldap":
627 backend_modules = ["entryuuid", "paged_searches"]
628 # OpenLDAP handles subtree renames, so we don't want to do any of these things
629 tdb_modules_list = ["extended_dn_out_openldap"]
631 elif serverrole == "domain controller":
632 tdb_modules_list.insert(0, "repl_meta_data")
633 backend_modules = []
634 else:
635 backend_modules = ["objectguid"]
637 if tdb_modules_list is None:
638 tdb_modules_list_as_string = ""
639 else:
640 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
642 samdb.transaction_start()
643 try:
644 message("Setting up sam.ldb partitions and settings")
645 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
646 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
647 "SCHEMADN_MOD2": ",objectguid",
648 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
649 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
650 "SCHEMADN_MOD": "schema_data",
651 "CONFIGDN_MOD": "naming_fsmo",
652 "DOMAINDN_MOD": "pdc_fsmo",
653 "MODULES_LIST": ",".join(modules_list),
654 "TDB_MODULES_LIST": tdb_modules_list_as_string,
655 "MODULES_LIST2": ",".join(modules_list2),
656 "BACKEND_MOD": ",".join(backend_modules),
657 "LDAP_BACKEND_LINE": ldap_backend_line,
661 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
663 message("Setting up sam.ldb rootDSE")
664 setup_samdb_rootdse(samdb, setup_path, names)
666 except:
667 samdb.transaction_cancel()
668 raise
670 samdb.transaction_commit()
673 def secretsdb_self_join(secretsdb, domain,
674 netbiosname, domainsid, machinepass,
675 realm=None, dnsdomain=None,
676 keytab_path=None,
677 key_version_number=1,
678 secure_channel_type=SEC_CHAN_WKSTA):
679 """Add domain join-specific bits to a secrets database.
681 :param secretsdb: Ldb Handle to the secrets database
682 :param machinepass: Machine password
684 attrs=["whenChanged",
685 "secret",
686 "priorSecret",
687 "priorChanged",
688 "krb5Keytab",
689 "privateKeytab"]
692 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
693 msg["secureChannelType"] = str(secure_channel_type)
694 msg["flatname"] = [domain]
695 msg["objectClass"] = ["top", "primaryDomain"]
696 if realm is not None:
697 if dnsdomain is None:
698 dnsdomain = realm.lower()
699 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
700 msg["realm"] = realm
701 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
702 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
703 msg["privateKeytab"] = ["secrets.keytab"];
706 msg["secret"] = [machinepass]
707 msg["samAccountName"] = ["%s$" % netbiosname]
708 msg["secureChannelType"] = [str(secure_channel_type)]
709 msg["objectSid"] = [ndr_pack(domainsid)]
711 res = secretsdb.search(base="cn=Primary Domains",
712 attrs=attrs,
713 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
714 scope=SCOPE_ONELEVEL)
716 for del_msg in res:
717 if del_msg.dn is not msg.dn:
718 secretsdb.delete(del_msg.dn)
720 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
722 if len(res) == 1:
723 msg["priorSecret"] = res[0]["secret"]
724 msg["priorWhenChanged"] = res[0]["whenChanged"]
726 if res["privateKeytab"] is not None:
727 msg["privateKeytab"] = res[0]["privateKeytab"]
729 if res["krb5Keytab"] is not None:
730 msg["krb5Keytab"] = res[0]["krb5Keytab"]
732 for el in msg:
733 el.set_flags(ldb.FLAG_MOD_REPLACE)
734 secretsdb.modify(msg)
735 else:
736 secretsdb.add(msg)
739 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
740 dns_keytab_path, dnspass):
741 """Add DNS specific bits to a secrets database.
743 :param secretsdb: Ldb Handle to the secrets database
744 :param setup_path: Setup path function
745 :param machinepass: Machine password
747 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
748 "REALM": realm,
749 "DNSDOMAIN": dnsdomain,
750 "DNS_KEYTAB": dns_keytab_path,
751 "DNSPASS_B64": b64encode(dnspass),
755 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
756 """Setup the secrets database.
758 :param path: Path to the secrets database.
759 :param setup_path: Get the path to a setup file.
760 :param session_info: Session info.
761 :param credentials: Credentials
762 :param lp: Loadparm context
763 :return: LDB handle for the created secrets database
765 if os.path.exists(path):
766 os.unlink(path)
767 secrets_ldb = Ldb(path, session_info=session_info,
768 lp=lp)
769 secrets_ldb.erase()
770 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
771 secrets_ldb = Ldb(path, session_info=session_info,
772 lp=lp)
773 secrets_ldb.transaction_start()
774 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
776 if backend_credentials is not None and backend_credentials.authentication_requested():
777 if backend_credentials.get_bind_dn() is not None:
778 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
779 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
780 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
782 else:
783 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
784 "LDAPADMINUSER": backend_credentials.get_username(),
785 "LDAPADMINREALM": backend_credentials.get_realm(),
786 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
789 return secrets_ldb
791 def setup_privileges(path, setup_path, session_info, lp):
792 """Setup the privileges database.
794 :param path: Path to the privileges database.
795 :param setup_path: Get the path to a setup file.
796 :param session_info: Session info.
797 :param credentials: Credentials
798 :param lp: Loadparm context
799 :return: LDB handle for the created secrets database
801 if os.path.exists(path):
802 os.unlink(path)
803 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
804 privilege_ldb.erase()
805 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
808 def setup_registry(path, setup_path, session_info, lp):
809 """Setup the registry.
811 :param path: Path to the registry database
812 :param setup_path: Function that returns the path to a setup.
813 :param session_info: Session information
814 :param credentials: Credentials
815 :param lp: Loadparm context
817 reg = registry.Registry()
818 hive = registry.open_ldb(path, session_info=session_info,
819 lp_ctx=lp)
820 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
821 provision_reg = setup_path("provision.reg")
822 assert os.path.exists(provision_reg)
823 reg.diff_apply(provision_reg)
826 def setup_idmapdb(path, setup_path, session_info, lp):
827 """Setup the idmap database.
829 :param path: path to the idmap database
830 :param setup_path: Function that returns a path to a setup file
831 :param session_info: Session information
832 :param credentials: Credentials
833 :param lp: Loadparm context
835 if os.path.exists(path):
836 os.unlink(path)
838 idmap_ldb = IDmapDB(path, session_info=session_info,
839 lp=lp)
841 idmap_ldb.erase()
842 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
843 return idmap_ldb
846 def setup_samdb_rootdse(samdb, setup_path, names):
847 """Setup the SamDB rootdse.
849 :param samdb: Sam Database handle
850 :param setup_path: Obtain setup path
852 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
853 "SCHEMADN": names.schemadn,
854 "NETBIOSNAME": names.netbiosname,
855 "DNSDOMAIN": names.dnsdomain,
856 "REALM": names.realm,
857 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
858 "DOMAINDN": names.domaindn,
859 "ROOTDN": names.rootdn,
860 "CONFIGDN": names.configdn,
861 "SERVERDN": names.serverdn,
865 def setup_self_join(samdb, names,
866 machinepass, dnspass,
867 domainsid, invocationid, setup_path,
868 policyguid, policyguid_dc, domainControllerFunctionality,
869 ntdsguid):
870 """Join a host to its own domain."""
871 assert isinstance(invocationid, str)
872 if ntdsguid is not None:
873 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
874 else:
875 ntdsguid_line = ""
876 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
877 "CONFIGDN": names.configdn,
878 "SCHEMADN": names.schemadn,
879 "DOMAINDN": names.domaindn,
880 "SERVERDN": names.serverdn,
881 "INVOCATIONID": invocationid,
882 "NETBIOSNAME": names.netbiosname,
883 "DEFAULTSITE": names.sitename,
884 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
885 "MACHINEPASS_B64": b64encode(machinepass),
886 "DNSPASS_B64": b64encode(dnspass),
887 "REALM": names.realm,
888 "DOMAIN": names.domain,
889 "DNSDOMAIN": names.dnsdomain,
890 "SAMBA_VERSION_STRING": version,
891 "NTDSGUID": ntdsguid_line,
892 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
894 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
895 "POLICYGUID": policyguid,
896 "POLICYGUID_DC": policyguid_dc,
897 "DNSDOMAIN": names.dnsdomain,
898 "DOMAINSID": str(domainsid),
899 "DOMAINDN": names.domaindn})
901 # add the NTDSGUID based SPNs
902 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
903 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
904 expression="", scope=SCOPE_BASE)
905 assert isinstance(names.ntdsguid, str)
907 # Setup fSMORoleOwner entries to point at the newly created DC entry
908 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
909 "DOMAIN": names.domain,
910 "DNSDOMAIN": names.dnsdomain,
911 "DOMAINDN": names.domaindn,
912 "CONFIGDN": names.configdn,
913 "SCHEMADN": names.schemadn,
914 "DEFAULTSITE": names.sitename,
915 "SERVERDN": names.serverdn,
916 "NETBIOSNAME": names.netbiosname,
917 "NTDSGUID": names.ntdsguid
921 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
922 names, message,
923 domainsid, domainguid, policyguid, policyguid_dc,
924 fill, adminpass, krbtgtpass,
925 machinepass, invocationid, dnspass, ntdsguid,
926 serverrole, dom_for_fun_level=None,
927 schema=None):
928 """Setup a complete SAM Database.
930 :note: This will wipe the main SAM database file!
933 # ATTENTION: Do NOT change these default values without discussion with the
934 # team and/or release manager. They have a big impact on the whole program!
935 domainControllerFunctionality = DS_DC_FUNCTION_2008
937 if dom_for_fun_level is None:
938 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
939 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
940 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
942 if dom_for_fun_level > domainControllerFunctionality:
943 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!")
945 domainFunctionality = dom_for_fun_level
946 forestFunctionality = dom_for_fun_level
948 # Also wipes the database
949 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
950 provision_backend=provision_backend, session_info=session_info,
951 names=names,
952 serverrole=serverrole, schema=schema)
954 if (schema == None):
955 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
957 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
958 samdb = Ldb(session_info=session_info,
959 credentials=provision_backend.credentials, lp=lp)
961 message("Pre-loading the Samba 4 and AD schema")
963 # Load the schema from the one we computed earlier
964 samdb.set_schema_from_ldb(schema.ldb)
966 # And now we can connect to the DB - the schema won't be loaded from the DB
967 samdb.connect(path)
969 if fill == FILL_DRS:
970 return samdb
972 samdb.transaction_start()
973 try:
974 # Set the domain functionality levels onto the database.
975 # Various module (the password_hash module in particular) need
976 # to know what level of AD we are emulating.
978 # These will be fixed into the database via the database
979 # modifictions below, but we need them set from the start.
980 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
981 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
982 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
984 samdb.set_domain_sid(str(domainsid))
985 if serverrole == "domain controller":
986 samdb.set_invocation_id(invocationid)
988 message("Adding DomainDN: %s" % names.domaindn)
990 #impersonate domain admin
991 admin_session_info = admin_session(lp, str(domainsid))
992 samdb.set_session_info(admin_session_info)
993 if domainguid is not None:
994 domainguid_line = "objectGUID: %s\n-" % domainguid
995 else:
996 domainguid_line = ""
998 descr = get_domain_descriptor(domainsid)
999 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1000 "DOMAINDN": names.domaindn,
1001 "DOMAINGUID": domainguid_line,
1002 "DESCRIPTOR": descr
1006 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1007 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1008 "DOMAINSID": str(domainsid),
1009 "SCHEMADN": names.schemadn,
1010 "NETBIOSNAME": names.netbiosname,
1011 "DEFAULTSITE": names.sitename,
1012 "CONFIGDN": names.configdn,
1013 "SERVERDN": names.serverdn,
1014 "POLICYGUID": policyguid,
1015 "DOMAINDN": names.domaindn,
1016 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1017 "SAMBA_VERSION_STRING": version
1020 message("Adding configuration container")
1021 descr = get_config_descriptor(domainsid);
1022 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1023 "CONFIGDN": names.configdn,
1024 "DESCRIPTOR": descr,
1026 message("Modifying configuration container")
1027 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1028 "CONFIGDN": names.configdn,
1029 "SCHEMADN": names.schemadn,
1032 # The LDIF here was created when the Schema object was constructed
1033 message("Setting up sam.ldb schema")
1034 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1035 samdb.modify_ldif(schema.schema_dn_modify)
1036 samdb.write_prefixes_from_schema()
1037 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1038 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1039 {"SCHEMADN": names.schemadn})
1041 message("Setting up sam.ldb configuration data")
1042 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1043 "CONFIGDN": names.configdn,
1044 "NETBIOSNAME": names.netbiosname,
1045 "DEFAULTSITE": names.sitename,
1046 "DNSDOMAIN": names.dnsdomain,
1047 "DOMAIN": names.domain,
1048 "SCHEMADN": names.schemadn,
1049 "DOMAINDN": names.domaindn,
1050 "SERVERDN": names.serverdn,
1051 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1054 message("Setting up display specifiers")
1055 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1056 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1057 check_all_substituted(display_specifiers_ldif)
1058 samdb.add_ldif(display_specifiers_ldif)
1060 message("Adding users container")
1061 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1062 "DOMAINDN": names.domaindn})
1063 message("Modifying users container")
1064 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1065 "DOMAINDN": names.domaindn})
1066 message("Adding computers container")
1067 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1068 "DOMAINDN": names.domaindn})
1069 message("Modifying computers container")
1070 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1071 "DOMAINDN": names.domaindn})
1072 message("Setting up sam.ldb data")
1073 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1074 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1075 "DOMAINDN": names.domaindn,
1076 "NETBIOSNAME": names.netbiosname,
1077 "DEFAULTSITE": names.sitename,
1078 "CONFIGDN": names.configdn,
1079 "SERVERDN": names.serverdn,
1080 "POLICYGUID_DC": policyguid_dc
1083 if fill == FILL_FULL:
1084 message("Setting up sam.ldb users and groups")
1085 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1086 "DOMAINDN": names.domaindn,
1087 "DOMAINSID": str(domainsid),
1088 "CONFIGDN": names.configdn,
1089 "ADMINPASS_B64": b64encode(adminpass),
1090 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1093 if serverrole == "domain controller":
1094 message("Setting up self join")
1095 setup_self_join(samdb, names=names, invocationid=invocationid,
1096 dnspass=dnspass,
1097 machinepass=machinepass,
1098 domainsid=domainsid, policyguid=policyguid,
1099 policyguid_dc=policyguid_dc,
1100 setup_path=setup_path,
1101 domainControllerFunctionality=domainControllerFunctionality,
1102 ntdsguid=ntdsguid)
1104 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1105 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1106 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1107 assert isinstance(names.ntdsguid, str)
1109 except:
1110 samdb.transaction_cancel()
1111 raise
1113 samdb.transaction_commit()
1114 return samdb
1117 FILL_FULL = "FULL"
1118 FILL_NT4SYNC = "NT4SYNC"
1119 FILL_DRS = "DRS"
1122 def provision(setup_dir, message, session_info,
1123 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1124 realm=None,
1125 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1126 serverdn=None,
1127 domain=None, hostname=None, hostip=None, hostip6=None,
1128 domainsid=None, adminpass=None, ldapadminpass=None,
1129 krbtgtpass=None, domainguid=None,
1130 policyguid=None, policyguid_dc=None, invocationid=None,
1131 machinepass=None, ntdsguid=None,
1132 dnspass=None, root=None, nobody=None, users=None,
1133 wheel=None, backup=None, aci=None, serverrole=None,
1134 dom_for_fun_level=None,
1135 ldap_backend_extra_port=None, backend_type=None,
1136 sitename=None,
1137 ol_mmr_urls=None, ol_olc=None,
1138 setup_ds_path=None, slapd_path=None, nosync=False,
1139 ldap_dryrun_mode=False):
1140 """Provision samba4
1142 :note: caution, this wipes all existing data!
1145 def setup_path(file):
1146 return os.path.join(setup_dir, file)
1148 if domainsid is None:
1149 domainsid = security.random_sid()
1150 else:
1151 domainsid = security.dom_sid(domainsid)
1153 # create/adapt the group policy GUIDs
1154 if policyguid is None:
1155 policyguid = str(uuid.uuid4())
1156 policyguid = policyguid.upper()
1157 if policyguid_dc is None:
1158 policyguid_dc = str(uuid.uuid4())
1159 policyguid_dc = policyguid_dc.upper()
1161 if adminpass is None:
1162 adminpass = glue.generate_random_str(12)
1163 if krbtgtpass is None:
1164 krbtgtpass = glue.generate_random_str(12)
1165 if machinepass is None:
1166 machinepass = glue.generate_random_str(12)
1167 if dnspass is None:
1168 dnspass = glue.generate_random_str(12)
1169 if ldapadminpass is None:
1170 #Make a new, random password between Samba and it's LDAP server
1171 ldapadminpass=glue.generate_random_str(12)
1173 if backend_type is None:
1174 backend_type = "ldb"
1176 sid_generator = "internal"
1177 if backend_type == "fedora-ds":
1178 sid_generator = "backend"
1180 root_uid = findnss_uid([root or "root"])
1181 nobody_uid = findnss_uid([nobody or "nobody"])
1182 users_gid = findnss_gid([users or "users"])
1183 if wheel is None:
1184 wheel_gid = findnss_gid(["wheel", "adm"])
1185 else:
1186 wheel_gid = findnss_gid([wheel])
1188 if targetdir is not None:
1189 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1190 os.makedirs(os.path.join(targetdir, "etc"))
1191 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1192 elif smbconf is None:
1193 smbconf = param.default_path()
1195 # only install a new smb.conf if there isn't one there already
1196 if not os.path.exists(smbconf):
1197 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1198 targetdir, sid_generator)
1200 lp = param.LoadParm()
1201 lp.load(smbconf)
1203 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1204 dnsdomain=realm, serverrole=serverrole,
1205 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1206 serverdn=serverdn, sitename=sitename)
1208 paths = provision_paths_from_lp(lp, names.dnsdomain)
1210 if hostip is None:
1211 try:
1212 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1213 except socket.gaierror, (socket.EAI_NODATA, msg):
1214 hostip = None
1216 if hostip6 is None:
1217 try:
1218 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1219 except socket.gaierror, (socket.EAI_NODATA, msg):
1220 hostip6 = None
1222 if serverrole is None:
1223 serverrole = lp.get("server role")
1225 assert serverrole in ("domain controller", "member server", "standalone")
1226 if invocationid is None and serverrole == "domain controller":
1227 invocationid = str(uuid.uuid4())
1229 if not os.path.exists(paths.private_dir):
1230 os.mkdir(paths.private_dir)
1232 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1234 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1236 if backend_type == "fedora-ds":
1237 provision_backend = FDSBackend(backend_type,
1238 paths=paths, setup_path=setup_path,
1239 lp=lp, credentials=credentials,
1240 names=names,
1241 message=message, hostname=hostname,
1242 root=root, schema=schema,
1243 ldapadminpass=ldapadminpass,
1244 ldap_backend_extra_port=ldap_backend_extra_port,
1245 ol_mmr_urls=ol_mmr_urls,
1246 slapd_path=slapd_path,
1247 setup_ds_path=setup_ds_path,
1248 ldap_dryrun_mode=ldap_dryrun_mode,
1249 domainsid=domainsid)
1250 elif backend_type == "openldap":
1251 provision_backend = OpenLDAPBackend(backend_type,
1252 paths=paths, setup_path=setup_path,
1253 lp=lp, credentials=credentials,
1254 names=names,
1255 message=message, hostname=hostname,
1256 root=root, schema=schema,
1257 ldapadminpass=ldapadminpass,
1258 ldap_backend_extra_port=ldap_backend_extra_port,
1259 ol_mmr_urls=ol_mmr_urls,
1260 slapd_path=slapd_path,
1261 setup_ds_path=setup_ds_path,
1262 ldap_dryrun_mode=ldap_dryrun_mode,
1263 domainsid=domainsid)
1264 else:
1265 provision_backend = ProvisionBackend(backend_type,
1266 paths=paths, setup_path=setup_path,
1267 lp=lp, credentials=credentials,
1268 names=names,
1269 message=message, hostname=hostname,
1270 root=root, schema=schema,
1271 ldapadminpass=ldapadminpass,
1272 ldap_backend_extra_port=ldap_backend_extra_port,
1273 ol_mmr_urls=ol_mmr_urls,
1274 slapd_path=slapd_path,
1275 setup_ds_path=setup_ds_path,
1276 ldap_dryrun_mode=ldap_dryrun_mode,
1277 domainsid=domainsid)
1279 provision_backend.start()
1281 # only install a new shares config db if there is none
1282 if not os.path.exists(paths.shareconf):
1283 message("Setting up share.ldb")
1284 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1285 lp=lp)
1286 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1289 message("Setting up secrets.ldb")
1290 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1291 session_info=session_info,
1292 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1294 message("Setting up the registry")
1295 setup_registry(paths.hklm, setup_path, session_info,
1296 lp=lp)
1298 message("Setting up the privileges database")
1299 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1301 message("Setting up idmap db")
1302 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1303 lp=lp)
1305 message("Setting up SAM db")
1306 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1307 provision_backend, lp, names,
1308 message,
1309 domainsid=domainsid,
1310 schema=schema, domainguid=domainguid,
1311 policyguid=policyguid, policyguid_dc=policyguid_dc,
1312 fill=samdb_fill,
1313 adminpass=adminpass, krbtgtpass=krbtgtpass,
1314 invocationid=invocationid,
1315 machinepass=machinepass, dnspass=dnspass,
1316 ntdsguid=ntdsguid, serverrole=serverrole,
1317 dom_for_fun_level=dom_for_fun_level)
1319 if serverrole == "domain controller":
1320 if paths.netlogon is None:
1321 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1322 message("Please either remove %s or see the template at %s" %
1323 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1324 assert(paths.netlogon is not None)
1326 if paths.sysvol is None:
1327 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1328 message("Please either remove %s or see the template at %s" %
1329 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1330 assert(paths.sysvol is not None)
1332 # Set up group policies (domain policy and domain controller policy)
1334 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1335 "{" + policyguid + "}")
1336 os.makedirs(policy_path, 0755)
1337 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1338 "[General]\r\nVersion=65543")
1339 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1340 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1342 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1343 "{" + policyguid_dc + "}")
1344 os.makedirs(policy_path_dc, 0755)
1345 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1346 "[General]\r\nVersion=2")
1347 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1348 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1350 if not os.path.isdir(paths.netlogon):
1351 os.makedirs(paths.netlogon, 0755)
1353 if samdb_fill == FILL_FULL:
1354 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1355 root_uid=root_uid, nobody_uid=nobody_uid,
1356 users_gid=users_gid, wheel_gid=wheel_gid)
1358 message("Setting up sam.ldb rootDSE marking as synchronized")
1359 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1361 # Only make a zone file on the first DC, it should be replicated with DNS replication
1362 if serverrole == "domain controller":
1363 secretsdb_self_join(secrets_ldb, domain=domain,
1364 realm=names.realm,
1365 dnsdomain=names.dnsdomain,
1366 netbiosname=names.netbiosname,
1367 domainsid=domainsid,
1368 machinepass=machinepass,
1369 secure_channel_type=SEC_CHAN_BDC)
1371 secretsdb_setup_dns(secrets_ldb, setup_path,
1372 realm=names.realm, dnsdomain=names.dnsdomain,
1373 dns_keytab_path=paths.dns_keytab,
1374 dnspass=dnspass)
1376 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1377 assert isinstance(domainguid, str)
1379 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1380 hostip=hostip,
1381 hostip6=hostip6, hostname=names.hostname,
1382 realm=names.realm,
1383 domainguid=domainguid, ntdsguid=names.ntdsguid)
1385 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1386 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1388 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1389 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1390 keytab_name=paths.dns_keytab)
1391 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1392 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1394 create_krb5_conf(paths.krb5conf, setup_path,
1395 dnsdomain=names.dnsdomain, hostname=names.hostname,
1396 realm=names.realm)
1397 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1399 provision_backend.post_setup()
1400 provision_backend.shutdown()
1402 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1403 ldapi_url)
1405 #Now commit the secrets.ldb to disk
1406 secrets_ldb.transaction_commit()
1408 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1410 message("Once the above files are installed, your Samba4 server will be ready to use")
1411 message("Server Role: %s" % serverrole)
1412 message("Hostname: %s" % names.hostname)
1413 message("NetBIOS Domain: %s" % names.domain)
1414 message("DNS Domain: %s" % names.dnsdomain)
1415 message("DOMAIN SID: %s" % str(domainsid))
1416 if samdb_fill == FILL_FULL:
1417 message("Admin password: %s" % adminpass)
1418 if provision_backend.type is not "ldb":
1419 if provision_backend.credentials.get_bind_dn() is not None:
1420 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1421 else:
1422 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1424 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1426 if provision_backend.slapd_command_escaped is not None:
1427 # now display slapd_command_file.txt to show how slapd must be started next time
1428 message("Use later the following commandline to start slapd, then Samba:")
1429 message(provision_backend.slapd_command_escaped)
1430 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1433 result = ProvisionResult()
1434 result.domaindn = domaindn
1435 result.paths = paths
1436 result.lp = lp
1437 result.samdb = samdb
1438 return result
1442 def provision_become_dc(setup_dir=None,
1443 smbconf=None, targetdir=None, realm=None,
1444 rootdn=None, domaindn=None, schemadn=None,
1445 configdn=None, serverdn=None,
1446 domain=None, hostname=None, domainsid=None,
1447 adminpass=None, krbtgtpass=None, domainguid=None,
1448 policyguid=None, policyguid_dc=None, invocationid=None,
1449 machinepass=None,
1450 dnspass=None, root=None, nobody=None, users=None,
1451 wheel=None, backup=None, serverrole=None,
1452 ldap_backend=None, ldap_backend_type=None,
1453 sitename=None, debuglevel=1):
1455 def message(text):
1456 """print a message if quiet is not set."""
1457 print text
1459 glue.set_debug_level(debuglevel)
1461 return provision(setup_dir, message, system_session(), None,
1462 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1463 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1464 configdn=configdn, serverdn=serverdn, domain=domain,
1465 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1466 machinepass=machinepass, serverrole="domain controller",
1467 sitename=sitename)
1470 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1471 """Create a PHP LDAP admin configuration file.
1473 :param path: Path to write the configuration to.
1474 :param setup_path: Function to generate setup paths.
1476 setup_file(setup_path("phpldapadmin-config.php"), path,
1477 {"S4_LDAPI_URI": ldapi_uri})
1480 def create_zone_file(path, setup_path, dnsdomain,
1481 hostip, hostip6, hostname, realm, domainguid,
1482 ntdsguid):
1483 """Write out a DNS zone file, from the info in the current database.
1485 :param path: Path of the new zone file.
1486 :param setup_path: Setup path function.
1487 :param dnsdomain: DNS Domain name
1488 :param domaindn: DN of the Domain
1489 :param hostip: Local IPv4 IP
1490 :param hostip6: Local IPv6 IP
1491 :param hostname: Local hostname
1492 :param realm: Realm name
1493 :param domainguid: GUID of the domain.
1494 :param ntdsguid: GUID of the hosts nTDSDSA record.
1496 assert isinstance(domainguid, str)
1498 if hostip6 is not None:
1499 hostip6_base_line = " IN AAAA " + hostip6
1500 hostip6_host_line = hostname + " IN AAAA " + hostip6
1501 else:
1502 hostip6_base_line = ""
1503 hostip6_host_line = ""
1505 if hostip is not None:
1506 hostip_base_line = " IN A " + hostip
1507 hostip_host_line = hostname + " IN A " + hostip
1508 else:
1509 hostip_base_line = ""
1510 hostip_host_line = ""
1512 setup_file(setup_path("provision.zone"), path, {
1513 "HOSTNAME": hostname,
1514 "DNSDOMAIN": dnsdomain,
1515 "REALM": realm,
1516 "HOSTIP_BASE_LINE": hostip_base_line,
1517 "HOSTIP_HOST_LINE": hostip_host_line,
1518 "DOMAINGUID": domainguid,
1519 "DATESTRING": time.strftime("%Y%m%d%H"),
1520 "DEFAULTSITE": DEFAULTSITE,
1521 "NTDSGUID": ntdsguid,
1522 "HOSTIP6_BASE_LINE": hostip6_base_line,
1523 "HOSTIP6_HOST_LINE": hostip6_host_line,
1527 def create_named_conf(path, setup_path, realm, dnsdomain,
1528 private_dir):
1529 """Write out a file containing zone statements suitable for inclusion in a
1530 named.conf file (including GSS-TSIG configuration).
1532 :param path: Path of the new named.conf file.
1533 :param setup_path: Setup path function.
1534 :param realm: Realm name
1535 :param dnsdomain: DNS Domain name
1536 :param private_dir: Path to private directory
1537 :param keytab_name: File name of DNS keytab file
1540 setup_file(setup_path("named.conf"), path, {
1541 "DNSDOMAIN": dnsdomain,
1542 "REALM": realm,
1543 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1544 "PRIVATE_DIR": private_dir
1547 def create_named_txt(path, setup_path, realm, dnsdomain,
1548 private_dir, keytab_name):
1549 """Write out a file containing zone statements suitable for inclusion in a
1550 named.conf file (including GSS-TSIG configuration).
1552 :param path: Path of the new named.conf file.
1553 :param setup_path: Setup path function.
1554 :param realm: Realm name
1555 :param dnsdomain: DNS Domain name
1556 :param private_dir: Path to private directory
1557 :param keytab_name: File name of DNS keytab file
1560 setup_file(setup_path("named.txt"), path, {
1561 "DNSDOMAIN": dnsdomain,
1562 "REALM": realm,
1563 "DNS_KEYTAB": keytab_name,
1564 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1565 "PRIVATE_DIR": private_dir
1568 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1569 """Write out a file containing zone statements suitable for inclusion in a
1570 named.conf file (including GSS-TSIG configuration).
1572 :param path: Path of the new named.conf file.
1573 :param setup_path: Setup path function.
1574 :param dnsdomain: DNS Domain name
1575 :param hostname: Local hostname
1576 :param realm: Realm name
1579 setup_file(setup_path("krb5.conf"), path, {
1580 "DNSDOMAIN": dnsdomain,
1581 "HOSTNAME": hostname,
1582 "REALM": realm,