s4:provision Remove unused 'sambadn' parameter
[Samba/ekacnet.git] / source4 / scripting / python / samba / provision.py
blob98f9298cf4ae86053cab17ee1446cf0b35ee4144
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
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 ldap_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 provision_backend = ProvisionBackend(backend_type,
1237 paths=paths, setup_path=setup_path,
1238 lp=lp, credentials=credentials,
1239 names=names,
1240 message=message, hostname=hostname,
1241 root=root, schema=schema,
1242 ldapadminpass=ldapadminpass,
1243 ldap_backend_extra_port=ldap_backend_extra_port,
1244 ol_mmr_urls=ol_mmr_urls,
1245 slapd_path=slapd_path,
1246 setup_ds_path=setup_ds_path,
1247 ldap_dryrun_mode=ldap_dryrun_mode,
1248 domainsid=domainsid)
1250 # only install a new shares config db if there is none
1251 if not os.path.exists(paths.shareconf):
1252 message("Setting up share.ldb")
1253 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1254 lp=lp)
1255 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1258 message("Setting up secrets.ldb")
1259 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1260 session_info=session_info,
1261 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1263 message("Setting up the registry")
1264 setup_registry(paths.hklm, setup_path, session_info,
1265 lp=lp)
1267 message("Setting up the privileges database")
1268 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1270 message("Setting up idmap db")
1271 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1272 lp=lp)
1274 message("Setting up SAM db")
1275 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1276 provision_backend, lp, names,
1277 message,
1278 domainsid=domainsid,
1279 schema=schema, domainguid=domainguid,
1280 policyguid=policyguid, policyguid_dc=policyguid_dc,
1281 fill=samdb_fill,
1282 adminpass=adminpass, krbtgtpass=krbtgtpass,
1283 invocationid=invocationid,
1284 machinepass=machinepass, dnspass=dnspass,
1285 ntdsguid=ntdsguid, serverrole=serverrole,
1286 dom_for_fun_level=dom_for_fun_level)
1288 if serverrole == "domain controller":
1289 if paths.netlogon is None:
1290 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1291 message("Please either remove %s or see the template at %s" %
1292 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1293 assert(paths.netlogon is not None)
1295 if paths.sysvol is None:
1296 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1297 message("Please either remove %s or see the template at %s" %
1298 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1299 assert(paths.sysvol is not None)
1301 # Set up group policies (domain policy and domain controller policy)
1303 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1304 "{" + policyguid + "}")
1305 os.makedirs(policy_path, 0755)
1306 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1307 "[General]\r\nVersion=65543")
1308 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1309 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1311 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1312 "{" + policyguid_dc + "}")
1313 os.makedirs(policy_path_dc, 0755)
1314 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1315 "[General]\r\nVersion=2")
1316 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1317 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1319 if not os.path.isdir(paths.netlogon):
1320 os.makedirs(paths.netlogon, 0755)
1322 if samdb_fill == FILL_FULL:
1323 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1324 root_uid=root_uid, nobody_uid=nobody_uid,
1325 users_gid=users_gid, wheel_gid=wheel_gid)
1327 message("Setting up sam.ldb rootDSE marking as synchronized")
1328 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1330 # Only make a zone file on the first DC, it should be replicated with DNS replication
1331 if serverrole == "domain controller":
1332 secretsdb_self_join(secrets_ldb, domain=domain,
1333 realm=names.realm,
1334 dnsdomain=names.dnsdomain,
1335 netbiosname=names.netbiosname,
1336 domainsid=domainsid,
1337 machinepass=machinepass,
1338 secure_channel_type=SEC_CHAN_BDC)
1340 secretsdb_setup_dns(secrets_ldb, setup_path,
1341 realm=names.realm, dnsdomain=names.dnsdomain,
1342 dns_keytab_path=paths.dns_keytab,
1343 dnspass=dnspass)
1345 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1346 assert isinstance(domainguid, str)
1348 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1349 hostip=hostip,
1350 hostip6=hostip6, hostname=names.hostname,
1351 realm=names.realm,
1352 domainguid=domainguid, ntdsguid=names.ntdsguid)
1354 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1355 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1357 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1358 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1359 keytab_name=paths.dns_keytab)
1360 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1361 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1363 create_krb5_conf(paths.krb5conf, setup_path,
1364 dnsdomain=names.dnsdomain, hostname=names.hostname,
1365 realm=names.realm)
1366 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1368 if provision_backend.post_setup is not None:
1369 provision_backend.post_setup()
1371 if provision_backend.shutdown is not None:
1372 provision_backend.shutdown()
1374 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1375 ldapi_url)
1377 #Now commit the secrets.ldb to disk
1378 secrets_ldb.transaction_commit()
1380 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1382 message("Once the above files are installed, your Samba4 server will be ready to use")
1383 message("Server Role: %s" % serverrole)
1384 message("Hostname: %s" % names.hostname)
1385 message("NetBIOS Domain: %s" % names.domain)
1386 message("DNS Domain: %s" % names.dnsdomain)
1387 message("DOMAIN SID: %s" % str(domainsid))
1388 if samdb_fill == FILL_FULL:
1389 message("Admin password: %s" % adminpass)
1390 if provision_backend.type is not "ldb":
1391 if provision_backend.credentials.get_bind_dn() is not None:
1392 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1393 else:
1394 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1396 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1398 if provision_backend.slapd_command_escaped is not None:
1399 # now display slapd_command_file.txt to show how slapd must be started next time
1400 message("Use later the following commandline to start slapd, then Samba:")
1401 message(provision_backend.slapd_command_escaped)
1402 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1405 result = ProvisionResult()
1406 result.domaindn = domaindn
1407 result.paths = paths
1408 result.lp = lp
1409 result.samdb = samdb
1410 return result
1414 def provision_become_dc(setup_dir=None,
1415 smbconf=None, targetdir=None, realm=None,
1416 rootdn=None, domaindn=None, schemadn=None,
1417 configdn=None, serverdn=None,
1418 domain=None, hostname=None, domainsid=None,
1419 adminpass=None, krbtgtpass=None, domainguid=None,
1420 policyguid=None, policyguid_dc=None, invocationid=None,
1421 machinepass=None,
1422 dnspass=None, root=None, nobody=None, users=None,
1423 wheel=None, backup=None, serverrole=None,
1424 ldap_backend=None, ldap_backend_type=None,
1425 sitename=None, debuglevel=1):
1427 def message(text):
1428 """print a message if quiet is not set."""
1429 print text
1431 glue.set_debug_level(debuglevel)
1433 return provision(setup_dir, message, system_session(), None,
1434 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1435 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1436 configdn=configdn, serverdn=serverdn, domain=domain,
1437 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1438 machinepass=machinepass, serverrole="domain controller",
1439 sitename=sitename)
1442 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1443 """Create a PHP LDAP admin configuration file.
1445 :param path: Path to write the configuration to.
1446 :param setup_path: Function to generate setup paths.
1448 setup_file(setup_path("phpldapadmin-config.php"), path,
1449 {"S4_LDAPI_URI": ldapi_uri})
1452 def create_zone_file(path, setup_path, dnsdomain,
1453 hostip, hostip6, hostname, realm, domainguid,
1454 ntdsguid):
1455 """Write out a DNS zone file, from the info in the current database.
1457 :param path: Path of the new zone file.
1458 :param setup_path: Setup path function.
1459 :param dnsdomain: DNS Domain name
1460 :param domaindn: DN of the Domain
1461 :param hostip: Local IPv4 IP
1462 :param hostip6: Local IPv6 IP
1463 :param hostname: Local hostname
1464 :param realm: Realm name
1465 :param domainguid: GUID of the domain.
1466 :param ntdsguid: GUID of the hosts nTDSDSA record.
1468 assert isinstance(domainguid, str)
1470 if hostip6 is not None:
1471 hostip6_base_line = " IN AAAA " + hostip6
1472 hostip6_host_line = hostname + " IN AAAA " + hostip6
1473 else:
1474 hostip6_base_line = ""
1475 hostip6_host_line = ""
1477 if hostip is not None:
1478 hostip_base_line = " IN A " + hostip
1479 hostip_host_line = hostname + " IN A " + hostip
1480 else:
1481 hostip_base_line = ""
1482 hostip_host_line = ""
1484 setup_file(setup_path("provision.zone"), path, {
1485 "HOSTNAME": hostname,
1486 "DNSDOMAIN": dnsdomain,
1487 "REALM": realm,
1488 "HOSTIP_BASE_LINE": hostip_base_line,
1489 "HOSTIP_HOST_LINE": hostip_host_line,
1490 "DOMAINGUID": domainguid,
1491 "DATESTRING": time.strftime("%Y%m%d%H"),
1492 "DEFAULTSITE": DEFAULTSITE,
1493 "NTDSGUID": ntdsguid,
1494 "HOSTIP6_BASE_LINE": hostip6_base_line,
1495 "HOSTIP6_HOST_LINE": hostip6_host_line,
1499 def create_named_conf(path, setup_path, realm, dnsdomain,
1500 private_dir):
1501 """Write out a file containing zone statements suitable for inclusion in a
1502 named.conf file (including GSS-TSIG configuration).
1504 :param path: Path of the new named.conf file.
1505 :param setup_path: Setup path function.
1506 :param realm: Realm name
1507 :param dnsdomain: DNS Domain name
1508 :param private_dir: Path to private directory
1509 :param keytab_name: File name of DNS keytab file
1512 setup_file(setup_path("named.conf"), path, {
1513 "DNSDOMAIN": dnsdomain,
1514 "REALM": realm,
1515 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1516 "PRIVATE_DIR": private_dir
1519 def create_named_txt(path, setup_path, realm, dnsdomain,
1520 private_dir, keytab_name):
1521 """Write out a file containing zone statements suitable for inclusion in a
1522 named.conf file (including GSS-TSIG configuration).
1524 :param path: Path of the new named.conf file.
1525 :param setup_path: Setup path function.
1526 :param realm: Realm name
1527 :param dnsdomain: DNS Domain name
1528 :param private_dir: Path to private directory
1529 :param keytab_name: File name of DNS keytab file
1532 setup_file(setup_path("named.txt"), path, {
1533 "DNSDOMAIN": dnsdomain,
1534 "REALM": realm,
1535 "DNS_KEYTAB": keytab_name,
1536 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1537 "PRIVATE_DIR": private_dir
1540 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1541 """Write out a file containing zone statements suitable for inclusion in a
1542 named.conf file (including GSS-TSIG configuration).
1544 :param path: Path of the new named.conf file.
1545 :param setup_path: Setup path function.
1546 :param dnsdomain: DNS Domain name
1547 :param hostname: Local hostname
1548 :param realm: Realm name
1551 setup_file(setup_path("krb5.conf"), path, {
1552 "DNSDOMAIN": dnsdomain,
1553 "HOSTNAME": hostname,
1554 "REALM": realm,