Added security descriptor for the domain NC to provisioning.
[Samba/fernandojvsilva.git] / source4 / scripting / python / samba / provision.py
blob188c5909f6e6a17ef4c37684569557bfcf847360
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.sambadn = None
210 self.ldapmanagerdn = None
211 self.dnsdomain = None
212 self.realm = None
213 self.netbiosname = None
214 self.domain = None
215 self.hostname = None
216 self.sitename = None
217 self.smbconf = None
220 class ProvisionResult(object):
221 def __init__(self):
222 self.paths = None
223 self.domaindn = None
224 self.lp = None
225 self.samdb = None
227 def check_install(lp, session_info, credentials):
228 """Check whether the current install seems ok.
230 :param lp: Loadparm context
231 :param session_info: Session information
232 :param credentials: Credentials
234 if lp.get("realm") == "":
235 raise Exception("Realm empty")
236 ldb = Ldb(lp.get("sam database"), session_info=session_info,
237 credentials=credentials, lp=lp)
238 if len(ldb.search("(cn=Administrator)")) != 1:
239 raise ProvisioningError("No administrator account found")
242 def findnss(nssfn, names):
243 """Find a user or group from a list of possibilities.
245 :param nssfn: NSS Function to try (should raise KeyError if not found)
246 :param names: Names to check.
247 :return: Value return by first names list.
249 for name in names:
250 try:
251 return nssfn(name)
252 except KeyError:
253 pass
254 raise KeyError("Unable to find user/group %r" % names)
257 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
258 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
261 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
262 """Setup a ldb in the private dir.
264 :param ldb: LDB file to import data into
265 :param ldif_path: Path of the LDIF file to load
266 :param subst_vars: Optional variables to subsitute in LDIF.
267 :param nocontrols: Optional list of controls, can be None for no controls
269 assert isinstance(ldif_path, str)
270 data = read_and_sub_file(ldif_path, subst_vars)
271 ldb.add_ldif(data,controls)
274 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
275 """Modify a ldb in the private dir.
277 :param ldb: LDB object.
278 :param ldif_path: LDIF file path.
279 :param subst_vars: Optional dictionary with substitution variables.
281 data = read_and_sub_file(ldif_path, subst_vars)
283 ldb.modify_ldif(data)
286 def setup_ldb(ldb, ldif_path, subst_vars):
287 """Import a LDIF a file into a LDB handle, optionally substituting variables.
289 :note: Either all LDIF data will be added or none (using transactions).
291 :param ldb: LDB file to import into.
292 :param ldif_path: Path to the LDIF file.
293 :param subst_vars: Dictionary with substitution variables.
295 assert ldb is not None
296 ldb.transaction_start()
297 try:
298 setup_add_ldif(ldb, ldif_path, subst_vars)
299 except:
300 ldb.transaction_cancel()
301 raise
302 ldb.transaction_commit()
305 def provision_paths_from_lp(lp, dnsdomain):
306 """Set the default paths for provisioning.
308 :param lp: Loadparm context.
309 :param dnsdomain: DNS Domain name
311 paths = ProvisionPaths()
312 paths.private_dir = lp.get("private dir")
313 paths.dns_keytab = "dns.keytab"
315 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
316 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
317 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
318 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
319 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
320 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
321 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
322 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
323 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
324 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
325 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
326 paths.phpldapadminconfig = os.path.join(paths.private_dir,
327 "phpldapadmin-config.php")
328 paths.ldapdir = os.path.join(paths.private_dir,
329 "ldap")
330 paths.slapdconf = os.path.join(paths.ldapdir,
331 "slapd.conf")
332 paths.slapdpid = os.path.join(paths.ldapdir,
333 "slapd.pid")
334 paths.modulesconf = os.path.join(paths.ldapdir,
335 "modules.conf")
336 paths.memberofconf = os.path.join(paths.ldapdir,
337 "memberof.conf")
338 paths.fedoradsinf = os.path.join(paths.ldapdir,
339 "fedorads.inf")
340 paths.fedoradspartitions = os.path.join(paths.ldapdir,
341 "fedorads-partitions.ldif")
342 paths.fedoradssasl = os.path.join(paths.ldapdir,
343 "fedorads-sasl.ldif")
344 paths.fedoradsdna = os.path.join(paths.ldapdir,
345 "fedorads-dna.ldif")
346 paths.fedoradspam = os.path.join(paths.ldapdir,
347 "fedorads-pam.ldif")
348 paths.fedoradsrefint = os.path.join(paths.ldapdir,
349 "fedorads-refint.ldif")
350 paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
351 "fedorads-linked-attributes.ldif")
352 paths.fedoradsindex = os.path.join(paths.ldapdir,
353 "fedorads-index.ldif")
354 paths.fedoradssamba = os.path.join(paths.ldapdir,
355 "fedorads-samba.ldif")
356 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
357 "mmr_serverids.conf")
358 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
359 "mmr_syncrepl.conf")
360 paths.olcdir = os.path.join(paths.ldapdir,
361 "slapd.d")
362 paths.olcseedldif = os.path.join(paths.ldapdir,
363 "olc_seed.ldif")
364 paths.hklm = "hklm.ldb"
365 paths.hkcr = "hkcr.ldb"
366 paths.hkcu = "hkcu.ldb"
367 paths.hku = "hku.ldb"
368 paths.hkpd = "hkpd.ldb"
369 paths.hkpt = "hkpt.ldb"
371 paths.sysvol = lp.get("path", "sysvol")
373 paths.netlogon = lp.get("path", "netlogon")
375 paths.smbconf = lp.configfile
377 return paths
380 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
381 serverrole=None, rootdn=None, domaindn=None, configdn=None,
382 schemadn=None, serverdn=None, sitename=None, sambadn=None):
383 """Guess configuration settings to use."""
385 if hostname is None:
386 hostname = socket.gethostname().split(".")[0]
388 netbiosname = lp.get("netbios name")
389 if netbiosname is None:
390 netbiosname = hostname
391 assert netbiosname is not None
392 netbiosname = netbiosname.upper()
393 if not valid_netbios_name(netbiosname):
394 raise InvalidNetbiosName(netbiosname)
396 if dnsdomain is None:
397 dnsdomain = lp.get("realm")
398 assert dnsdomain is not None
399 dnsdomain = dnsdomain.lower()
401 if serverrole is None:
402 serverrole = lp.get("server role")
403 assert serverrole is not None
404 serverrole = serverrole.lower()
406 realm = dnsdomain.upper()
408 if lp.get("realm").upper() != realm:
409 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
411 if serverrole == "domain controller":
412 if domain is None:
413 domain = lp.get("workgroup")
414 assert domain is not None
415 domain = domain.upper()
417 if lp.get("workgroup").upper() != domain:
418 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
420 if domaindn is None:
421 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
422 else:
423 domain = netbiosname
424 if domaindn is None:
425 domaindn = "DC=" + netbiosname
427 if not valid_netbios_name(domain):
428 raise InvalidNetbiosName(domain)
430 if hostname.upper() == realm:
431 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
432 if netbiosname == realm:
433 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
434 if domain == realm:
435 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
437 if rootdn is None:
438 rootdn = domaindn
440 if configdn is None:
441 configdn = "CN=Configuration," + rootdn
442 if schemadn is None:
443 schemadn = "CN=Schema," + configdn
444 if sambadn is None:
445 sambadn = "CN=Samba"
447 if sitename is None:
448 sitename=DEFAULTSITE
450 names = ProvisionNames()
451 names.rootdn = rootdn
452 names.domaindn = domaindn
453 names.configdn = configdn
454 names.schemadn = schemadn
455 names.sambadn = sambadn
456 names.ldapmanagerdn = "CN=Manager," + rootdn
457 names.dnsdomain = dnsdomain
458 names.domain = domain
459 names.realm = realm
460 names.netbiosname = netbiosname
461 names.hostname = hostname
462 names.sitename = sitename
463 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
465 return names
468 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
469 targetdir, sid_generator):
470 """Create a new smb.conf file based on a couple of basic settings.
472 assert smbconf is not None
473 if hostname is None:
474 hostname = socket.gethostname().split(".")[0]
475 netbiosname = hostname.upper()
477 if serverrole is None:
478 serverrole = "standalone"
480 assert serverrole in ("domain controller", "member server", "standalone")
481 if serverrole == "domain controller":
482 smbconfsuffix = "dc"
483 elif serverrole == "member server":
484 smbconfsuffix = "member"
485 elif serverrole == "standalone":
486 smbconfsuffix = "standalone"
488 if sid_generator is None:
489 sid_generator = "internal"
491 assert domain is not None
492 domain = domain.upper()
494 assert realm is not None
495 realm = realm.upper()
497 default_lp = param.LoadParm()
498 #Load non-existant file
499 if os.path.exists(smbconf):
500 default_lp.load(smbconf)
502 if targetdir is not None:
503 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
504 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
506 default_lp.set("lock dir", os.path.abspath(targetdir))
507 else:
508 privatedir_line = ""
509 lockdir_line = ""
511 if sid_generator == "internal":
512 sid_generator_line = ""
513 else:
514 sid_generator_line = "sid generator = " + sid_generator
516 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
517 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
519 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
520 smbconf, {
521 "NETBIOS_NAME": netbiosname,
522 "DOMAIN": domain,
523 "REALM": realm,
524 "SERVERROLE": serverrole,
525 "NETLOGONPATH": netlogon,
526 "SYSVOLPATH": sysvol,
527 "SIDGENERATOR_LINE": sid_generator_line,
528 "PRIVATEDIR_LINE": privatedir_line,
529 "LOCKDIR_LINE": lockdir_line
533 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
534 users_gid, wheel_gid):
535 """setup reasonable name mappings for sam names to unix names.
537 :param samdb: SamDB object.
538 :param idmap: IDmap db object.
539 :param sid: The domain sid.
540 :param domaindn: The domain DN.
541 :param root_uid: uid of the UNIX root user.
542 :param nobody_uid: uid of the UNIX nobody user.
543 :param users_gid: gid of the UNIX users group.
544 :param wheel_gid: gid of the UNIX wheel group."""
546 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
547 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
549 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
550 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
552 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
553 provision_backend, names, schema,
554 serverrole,
555 erase=False):
556 """Setup the partitions for the SAM database.
558 Alternatively, provision() may call this, and then populate the database.
560 :note: This will wipe the Sam Database!
562 :note: This function always removes the local SAM LDB file. The erase
563 parameter controls whether to erase the existing data, which
564 may not be stored locally but in LDAP.
567 assert session_info is not None
569 old_partitions = None
570 new_partitions = None
572 # We use options=["modules:"] to stop the modules loading - we
573 # just want to wipe and re-initialise the database, not start it up
575 try:
576 os.unlink(samdb_path)
577 except OSError:
578 pass
580 samdb = Ldb(url=samdb_path, session_info=session_info,
581 lp=lp, options=["modules:"])
583 #Add modules to the list to activate them by default
584 #beware often order is important
586 # Some Known ordering constraints:
587 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
588 # - objectclass must be before password_hash, because password_hash checks
589 # that the objectclass is of type person (filled in by objectclass
590 # module when expanding the objectclass list)
591 # - partition must be last
592 # - each partition has its own module list then
593 modules_list = ["resolve_oids",
594 "rootdse",
595 "lazy_commit",
596 "acl",
597 "paged_results",
598 "ranged_results",
599 "anr",
600 "server_sort",
601 "asq",
602 "extended_dn_store",
603 "extended_dn_in",
604 "rdn_name",
605 "objectclass",
606 "descriptor",
607 "samldb",
608 "password_hash",
609 "operational",
610 "kludge_acl",
611 "instancetype"]
612 tdb_modules_list = [
613 "subtree_rename",
614 "subtree_delete",
615 "linked_attributes",
616 "extended_dn_out_ldb"]
617 modules_list2 = ["show_deleted",
618 "schema_load",
619 "new_partition",
620 "partition"]
622 ldap_backend_line = "# No LDAP backend"
623 if provision_backend.type is not "ldb":
624 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
626 if provision_backend.ldap_backend_type == "fedora-ds":
627 backend_modules = ["nsuniqueid", "paged_searches"]
628 # We can handle linked attributes here, as we don't have directory-side subtree operations
629 tdb_modules_list = ["extended_dn_out_fds"]
630 elif ldap_backend.ldap_backend_type == "openldap":
631 backend_modules = ["entryuuid", "paged_searches"]
632 # OpenLDAP handles subtree renames, so we don't want to do any of these things
633 tdb_modules_list = ["extended_dn_out_openldap"]
635 elif serverrole == "domain controller":
636 tdb_modules_list.insert(0, "repl_meta_data")
637 backend_modules = []
638 else:
639 backend_modules = ["objectguid"]
641 if tdb_modules_list is None:
642 tdb_modules_list_as_string = ""
643 else:
644 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
646 samdb.transaction_start()
647 try:
648 message("Setting up sam.ldb partitions and settings")
649 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
650 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
651 "SCHEMADN_MOD2": ",objectguid",
652 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
653 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
654 "SCHEMADN_MOD": "schema_data",
655 "CONFIGDN_MOD": "naming_fsmo",
656 "DOMAINDN_MOD": "pdc_fsmo",
657 "MODULES_LIST": ",".join(modules_list),
658 "TDB_MODULES_LIST": tdb_modules_list_as_string,
659 "MODULES_LIST2": ",".join(modules_list2),
660 "BACKEND_MOD": ",".join(backend_modules),
661 "LDAP_BACKEND_LINE": ldap_backend_line,
665 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
667 message("Setting up sam.ldb rootDSE")
668 setup_samdb_rootdse(samdb, setup_path, names)
670 except:
671 samdb.transaction_cancel()
672 raise
674 samdb.transaction_commit()
677 def secretsdb_self_join(secretsdb, domain,
678 netbiosname, domainsid, machinepass,
679 realm=None, dnsdomain=None,
680 keytab_path=None,
681 key_version_number=1,
682 secure_channel_type=SEC_CHAN_WKSTA):
683 """Add domain join-specific bits to a secrets database.
685 :param secretsdb: Ldb Handle to the secrets database
686 :param machinepass: Machine password
688 attrs=["whenChanged",
689 "secret",
690 "priorSecret",
691 "priorChanged",
692 "krb5Keytab",
693 "privateKeytab"]
696 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
697 msg["secureChannelType"] = str(secure_channel_type)
698 msg["flatname"] = [domain]
699 msg["objectClass"] = ["top", "primaryDomain"]
700 if realm is not None:
701 if dnsdomain is None:
702 dnsdomain = realm.lower()
703 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
704 msg["realm"] = realm
705 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
706 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
707 msg["privateKeytab"] = ["secrets.keytab"];
710 msg["secret"] = [machinepass]
711 msg["samAccountName"] = ["%s$" % netbiosname]
712 msg["secureChannelType"] = [str(secure_channel_type)]
713 msg["objectSid"] = [ndr_pack(domainsid)]
715 res = secretsdb.search(base="cn=Primary Domains",
716 attrs=attrs,
717 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
718 scope=SCOPE_ONELEVEL)
720 for del_msg in res:
721 if del_msg.dn is not msg.dn:
722 secretsdb.delete(del_msg.dn)
724 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
726 if len(res) == 1:
727 msg["priorSecret"] = res[0]["secret"]
728 msg["priorWhenChanged"] = res[0]["whenChanged"]
730 if res["privateKeytab"] is not None:
731 msg["privateKeytab"] = res[0]["privateKeytab"]
733 if res["krb5Keytab"] is not None:
734 msg["krb5Keytab"] = res[0]["krb5Keytab"]
736 for el in msg:
737 el.set_flags(ldb.FLAG_MOD_REPLACE)
738 secretsdb.modify(msg)
739 else:
740 secretsdb.add(msg)
743 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
744 dns_keytab_path, dnspass):
745 """Add DNS specific bits to a secrets database.
747 :param secretsdb: Ldb Handle to the secrets database
748 :param setup_path: Setup path function
749 :param machinepass: Machine password
751 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
752 "REALM": realm,
753 "DNSDOMAIN": dnsdomain,
754 "DNS_KEYTAB": dns_keytab_path,
755 "DNSPASS_B64": b64encode(dnspass),
759 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
760 """Setup the secrets database.
762 :param path: Path to the secrets database.
763 :param setup_path: Get the path to a setup file.
764 :param session_info: Session info.
765 :param credentials: Credentials
766 :param lp: Loadparm context
767 :return: LDB handle for the created secrets database
769 if os.path.exists(path):
770 os.unlink(path)
771 secrets_ldb = Ldb(path, session_info=session_info,
772 lp=lp)
773 secrets_ldb.erase()
774 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
775 secrets_ldb = Ldb(path, session_info=session_info,
776 lp=lp)
777 secrets_ldb.transaction_start()
778 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
780 if backend_credentials is not None and backend_credentials.authentication_requested():
781 if backend_credentials.get_bind_dn() is not None:
782 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
783 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
784 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
786 else:
787 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
788 "LDAPADMINUSER": backend_credentials.get_username(),
789 "LDAPADMINREALM": backend_credentials.get_realm(),
790 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
793 return secrets_ldb
795 def setup_privileges(path, setup_path, session_info, lp):
796 """Setup the privileges database.
798 :param path: Path to the privileges database.
799 :param setup_path: Get the path to a setup file.
800 :param session_info: Session info.
801 :param credentials: Credentials
802 :param lp: Loadparm context
803 :return: LDB handle for the created secrets database
805 if os.path.exists(path):
806 os.unlink(path)
807 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
808 privilege_ldb.erase()
809 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
812 def setup_registry(path, setup_path, session_info, lp):
813 """Setup the registry.
815 :param path: Path to the registry database
816 :param setup_path: Function that returns the path to a setup.
817 :param session_info: Session information
818 :param credentials: Credentials
819 :param lp: Loadparm context
821 reg = registry.Registry()
822 hive = registry.open_ldb(path, session_info=session_info,
823 lp_ctx=lp)
824 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
825 provision_reg = setup_path("provision.reg")
826 assert os.path.exists(provision_reg)
827 reg.diff_apply(provision_reg)
830 def setup_idmapdb(path, setup_path, session_info, lp):
831 """Setup the idmap database.
833 :param path: path to the idmap database
834 :param setup_path: Function that returns a path to a setup file
835 :param session_info: Session information
836 :param credentials: Credentials
837 :param lp: Loadparm context
839 if os.path.exists(path):
840 os.unlink(path)
842 idmap_ldb = IDmapDB(path, session_info=session_info,
843 lp=lp)
845 idmap_ldb.erase()
846 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
847 return idmap_ldb
850 def setup_samdb_rootdse(samdb, setup_path, names):
851 """Setup the SamDB rootdse.
853 :param samdb: Sam Database handle
854 :param setup_path: Obtain setup path
856 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
857 "SCHEMADN": names.schemadn,
858 "NETBIOSNAME": names.netbiosname,
859 "DNSDOMAIN": names.dnsdomain,
860 "REALM": names.realm,
861 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
862 "DOMAINDN": names.domaindn,
863 "ROOTDN": names.rootdn,
864 "CONFIGDN": names.configdn,
865 "SERVERDN": names.serverdn,
869 def setup_self_join(samdb, names,
870 machinepass, dnspass,
871 domainsid, invocationid, setup_path,
872 policyguid, policyguid_dc, domainControllerFunctionality,
873 ntdsguid):
874 """Join a host to its own domain."""
875 assert isinstance(invocationid, str)
876 if ntdsguid is not None:
877 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
878 else:
879 ntdsguid_line = ""
880 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
881 "CONFIGDN": names.configdn,
882 "SCHEMADN": names.schemadn,
883 "DOMAINDN": names.domaindn,
884 "SERVERDN": names.serverdn,
885 "INVOCATIONID": invocationid,
886 "NETBIOSNAME": names.netbiosname,
887 "DEFAULTSITE": names.sitename,
888 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
889 "MACHINEPASS_B64": b64encode(machinepass),
890 "DNSPASS_B64": b64encode(dnspass),
891 "REALM": names.realm,
892 "DOMAIN": names.domain,
893 "DNSDOMAIN": names.dnsdomain,
894 "SAMBA_VERSION_STRING": version,
895 "NTDSGUID": ntdsguid_line,
896 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
898 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
899 "POLICYGUID": policyguid,
900 "POLICYGUID_DC": policyguid_dc,
901 "DNSDOMAIN": names.dnsdomain,
902 "DOMAINSID": str(domainsid),
903 "DOMAINDN": names.domaindn})
905 # add the NTDSGUID based SPNs
906 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
907 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
908 expression="", scope=SCOPE_BASE)
909 assert isinstance(names.ntdsguid, str)
911 # Setup fSMORoleOwner entries to point at the newly created DC entry
912 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
913 "DOMAIN": names.domain,
914 "DNSDOMAIN": names.dnsdomain,
915 "DOMAINDN": names.domaindn,
916 "CONFIGDN": names.configdn,
917 "SCHEMADN": names.schemadn,
918 "DEFAULTSITE": names.sitename,
919 "SERVERDN": names.serverdn,
920 "NETBIOSNAME": names.netbiosname,
921 "NTDSGUID": names.ntdsguid
925 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
926 names, message,
927 domainsid, domainguid, policyguid, policyguid_dc,
928 fill, adminpass, krbtgtpass,
929 machinepass, invocationid, dnspass, ntdsguid,
930 serverrole, dom_for_fun_level=None,
931 schema=None):
932 """Setup a complete SAM Database.
934 :note: This will wipe the main SAM database file!
937 # ATTENTION: Do NOT change these default values without discussion with the
938 # team and/or release manager. They have a big impact on the whole program!
939 domainControllerFunctionality = DS_DC_FUNCTION_2008
941 if dom_for_fun_level is None:
942 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
943 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
944 raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
946 if dom_for_fun_level > domainControllerFunctionality:
947 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!")
949 domainFunctionality = dom_for_fun_level
950 forestFunctionality = dom_for_fun_level
952 # Also wipes the database
953 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
954 provision_backend=provision_backend, session_info=session_info,
955 names=names,
956 serverrole=serverrole, schema=schema)
958 if (schema == None):
959 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
960 sambadn=names.sambadn)
962 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
963 samdb = Ldb(session_info=session_info,
964 credentials=provision_backend.credentials, lp=lp)
966 message("Pre-loading the Samba 4 and AD schema")
968 # Load the schema from the one we computed earlier
969 samdb.set_schema_from_ldb(schema.ldb)
971 # And now we can connect to the DB - the schema won't be loaded from the DB
972 samdb.connect(path)
974 if fill == FILL_DRS:
975 return samdb
977 samdb.transaction_start()
978 try:
979 # Set the domain functionality levels onto the database.
980 # Various module (the password_hash module in particular) need
981 # to know what level of AD we are emulating.
983 # These will be fixed into the database via the database
984 # modifictions below, but we need them set from the start.
985 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
986 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
987 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
989 samdb.set_domain_sid(str(domainsid))
990 if serverrole == "domain controller":
991 samdb.set_invocation_id(invocationid)
993 message("Adding DomainDN: %s" % names.domaindn)
995 #impersonate domain admin
996 admin_session_info = admin_session(lp, str(domainsid))
997 samdb.set_session_info(admin_session_info)
998 if domainguid is not None:
999 domainguid_line = "objectGUID: %s\n-" % domainguid
1000 else:
1001 domainguid_line = ""
1003 descr = get_domain_descriptor(domainsid)
1004 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1005 "DOMAINDN": names.domaindn,
1006 "DOMAINGUID": domainguid_line,
1007 "DESCRIPTOR": descr
1011 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1012 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1013 "DOMAINSID": str(domainsid),
1014 "SCHEMADN": names.schemadn,
1015 "NETBIOSNAME": names.netbiosname,
1016 "DEFAULTSITE": names.sitename,
1017 "CONFIGDN": names.configdn,
1018 "SERVERDN": names.serverdn,
1019 "POLICYGUID": policyguid,
1020 "DOMAINDN": names.domaindn,
1021 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1022 "SAMBA_VERSION_STRING": version
1025 message("Adding configuration container")
1026 descr = get_config_descriptor(domainsid);
1027 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1028 "CONFIGDN": names.configdn,
1029 "DESCRIPTOR": descr,
1031 message("Modifying configuration container")
1032 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1033 "CONFIGDN": names.configdn,
1034 "SCHEMADN": names.schemadn,
1037 # The LDIF here was created when the Schema object was constructed
1038 message("Setting up sam.ldb schema")
1039 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
1040 samdb.modify_ldif(schema.schema_dn_modify)
1041 samdb.write_prefixes_from_schema()
1042 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
1043 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1044 {"SCHEMADN": names.schemadn})
1046 message("Setting up sam.ldb configuration data")
1047 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1048 "CONFIGDN": names.configdn,
1049 "NETBIOSNAME": names.netbiosname,
1050 "DEFAULTSITE": names.sitename,
1051 "DNSDOMAIN": names.dnsdomain,
1052 "DOMAIN": names.domain,
1053 "SCHEMADN": names.schemadn,
1054 "DOMAINDN": names.domaindn,
1055 "SERVERDN": names.serverdn,
1056 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1059 message("Setting up display specifiers")
1060 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1061 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1062 check_all_substituted(display_specifiers_ldif)
1063 samdb.add_ldif(display_specifiers_ldif)
1065 message("Adding users container")
1066 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1067 "DOMAINDN": names.domaindn})
1068 message("Modifying users container")
1069 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1070 "DOMAINDN": names.domaindn})
1071 message("Adding computers container")
1072 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1073 "DOMAINDN": names.domaindn})
1074 message("Modifying computers container")
1075 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1076 "DOMAINDN": names.domaindn})
1077 message("Setting up sam.ldb data")
1078 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1079 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1080 "DOMAINDN": names.domaindn,
1081 "NETBIOSNAME": names.netbiosname,
1082 "DEFAULTSITE": names.sitename,
1083 "CONFIGDN": names.configdn,
1084 "SERVERDN": names.serverdn,
1085 "POLICYGUID_DC": policyguid_dc
1088 if fill == FILL_FULL:
1089 message("Setting up sam.ldb users and groups")
1090 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1091 "DOMAINDN": names.domaindn,
1092 "DOMAINSID": str(domainsid),
1093 "CONFIGDN": names.configdn,
1094 "ADMINPASS_B64": b64encode(adminpass),
1095 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1098 if serverrole == "domain controller":
1099 message("Setting up self join")
1100 setup_self_join(samdb, names=names, invocationid=invocationid,
1101 dnspass=dnspass,
1102 machinepass=machinepass,
1103 domainsid=domainsid, policyguid=policyguid,
1104 policyguid_dc=policyguid_dc,
1105 setup_path=setup_path,
1106 domainControllerFunctionality=domainControllerFunctionality,
1107 ntdsguid=ntdsguid)
1109 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1110 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1111 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1112 assert isinstance(names.ntdsguid, str)
1114 except:
1115 samdb.transaction_cancel()
1116 raise
1118 samdb.transaction_commit()
1119 return samdb
1122 FILL_FULL = "FULL"
1123 FILL_NT4SYNC = "NT4SYNC"
1124 FILL_DRS = "DRS"
1127 def provision(setup_dir, message, session_info,
1128 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1129 realm=None,
1130 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1131 serverdn=None,
1132 domain=None, hostname=None, hostip=None, hostip6=None,
1133 domainsid=None, adminpass=None, ldapadminpass=None,
1134 krbtgtpass=None, domainguid=None,
1135 policyguid=None, policyguid_dc=None, invocationid=None,
1136 machinepass=None, ntdsguid=None,
1137 dnspass=None, root=None, nobody=None, users=None,
1138 wheel=None, backup=None, aci=None, serverrole=None,
1139 dom_for_fun_level=None,
1140 ldap_backend_extra_port=None, backend_type=None,
1141 sitename=None,
1142 ol_mmr_urls=None, ol_olc=None,
1143 setup_ds_path=None, slapd_path=None, nosync=False,
1144 ldap_dryrun_mode=False):
1145 """Provision samba4
1147 :note: caution, this wipes all existing data!
1150 def setup_path(file):
1151 return os.path.join(setup_dir, file)
1153 if domainsid is None:
1154 domainsid = security.random_sid()
1155 else:
1156 domainsid = security.dom_sid(domainsid)
1158 # create/adapt the group policy GUIDs
1159 if policyguid is None:
1160 policyguid = str(uuid.uuid4())
1161 policyguid = policyguid.upper()
1162 if policyguid_dc is None:
1163 policyguid_dc = str(uuid.uuid4())
1164 policyguid_dc = policyguid_dc.upper()
1166 if adminpass is None:
1167 adminpass = glue.generate_random_str(12)
1168 if krbtgtpass is None:
1169 krbtgtpass = glue.generate_random_str(12)
1170 if machinepass is None:
1171 machinepass = glue.generate_random_str(12)
1172 if dnspass is None:
1173 dnspass = glue.generate_random_str(12)
1174 if ldapadminpass is None:
1175 #Make a new, random password between Samba and it's LDAP server
1176 ldapadminpass=glue.generate_random_str(12)
1178 if backend_type is None:
1179 backend_type = "ldb"
1181 sid_generator = "internal"
1182 if backend_type == "fedora-ds":
1183 sid_generator = "backend"
1185 root_uid = findnss_uid([root or "root"])
1186 nobody_uid = findnss_uid([nobody or "nobody"])
1187 users_gid = findnss_gid([users or "users"])
1188 if wheel is None:
1189 wheel_gid = findnss_gid(["wheel", "adm"])
1190 else:
1191 wheel_gid = findnss_gid([wheel])
1193 if targetdir is not None:
1194 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1195 os.makedirs(os.path.join(targetdir, "etc"))
1196 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1197 elif smbconf is None:
1198 smbconf = param.default_path()
1200 # only install a new smb.conf if there isn't one there already
1201 if not os.path.exists(smbconf):
1202 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1203 targetdir, sid_generator)
1205 lp = param.LoadParm()
1206 lp.load(smbconf)
1208 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1209 dnsdomain=realm, serverrole=serverrole,
1210 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1211 serverdn=serverdn, sitename=sitename)
1213 paths = provision_paths_from_lp(lp, names.dnsdomain)
1215 if hostip is None:
1216 try:
1217 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1218 except socket.gaierror, (socket.EAI_NODATA, msg):
1219 hostip = None
1221 if hostip6 is None:
1222 try:
1223 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1224 except socket.gaierror, (socket.EAI_NODATA, msg):
1225 hostip6 = None
1227 if serverrole is None:
1228 serverrole = lp.get("server role")
1230 assert serverrole in ("domain controller", "member server", "standalone")
1231 if invocationid is None and serverrole == "domain controller":
1232 invocationid = str(uuid.uuid4())
1234 if not os.path.exists(paths.private_dir):
1235 os.mkdir(paths.private_dir)
1237 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1239 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1240 sambadn=names.sambadn)
1242 provision_backend = ProvisionBackend(backend_type,
1243 paths=paths, setup_path=setup_path,
1244 lp=lp, credentials=credentials,
1245 names=names,
1246 message=message, hostname=hostname,
1247 root=root, schema=schema,
1248 ldapadminpass=ldapadminpass,
1249 ldap_backend_extra_port=ldap_backend_extra_port,
1250 ol_mmr_urls=ol_mmr_urls,
1251 slapd_path=slapd_path,
1252 setup_ds_path=setup_ds_path,
1253 ldap_dryrun_mode=ldap_dryrun_mode,
1254 domainsid=domainsid)
1256 # only install a new shares config db if there is none
1257 if not os.path.exists(paths.shareconf):
1258 message("Setting up share.ldb")
1259 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1260 lp=lp)
1261 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1264 message("Setting up secrets.ldb")
1265 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1266 session_info=session_info,
1267 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1269 message("Setting up the registry")
1270 setup_registry(paths.hklm, setup_path, session_info,
1271 lp=lp)
1273 message("Setting up the privileges database")
1274 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1276 message("Setting up idmap db")
1277 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1278 lp=lp)
1280 message("Setting up SAM db")
1281 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1282 provision_backend, lp, names,
1283 message,
1284 domainsid=domainsid,
1285 schema=schema, domainguid=domainguid,
1286 policyguid=policyguid, policyguid_dc=policyguid_dc,
1287 fill=samdb_fill,
1288 adminpass=adminpass, krbtgtpass=krbtgtpass,
1289 invocationid=invocationid,
1290 machinepass=machinepass, dnspass=dnspass,
1291 ntdsguid=ntdsguid, serverrole=serverrole,
1292 dom_for_fun_level=dom_for_fun_level)
1294 if serverrole == "domain controller":
1295 if paths.netlogon is None:
1296 message("Existing smb.conf does not have a [netlogon] 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.netlogon is not None)
1301 if paths.sysvol is None:
1302 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1303 message("Please either remove %s or see the template at %s" %
1304 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1305 assert(paths.sysvol is not None)
1307 # Set up group policies (domain policy and domain controller policy)
1309 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1310 "{" + policyguid + "}")
1311 os.makedirs(policy_path, 0755)
1312 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1313 "[General]\r\nVersion=65543")
1314 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1315 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1317 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1318 "{" + policyguid_dc + "}")
1319 os.makedirs(policy_path_dc, 0755)
1320 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1321 "[General]\r\nVersion=2")
1322 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1323 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1325 if not os.path.isdir(paths.netlogon):
1326 os.makedirs(paths.netlogon, 0755)
1328 if samdb_fill == FILL_FULL:
1329 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1330 root_uid=root_uid, nobody_uid=nobody_uid,
1331 users_gid=users_gid, wheel_gid=wheel_gid)
1333 message("Setting up sam.ldb rootDSE marking as synchronized")
1334 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1336 # Only make a zone file on the first DC, it should be replicated with DNS replication
1337 if serverrole == "domain controller":
1338 secretsdb_self_join(secrets_ldb, domain=domain,
1339 realm=names.realm,
1340 dnsdomain=names.dnsdomain,
1341 netbiosname=names.netbiosname,
1342 domainsid=domainsid,
1343 machinepass=machinepass,
1344 secure_channel_type=SEC_CHAN_BDC)
1346 secretsdb_setup_dns(secrets_ldb, setup_path,
1347 realm=names.realm, dnsdomain=names.dnsdomain,
1348 dns_keytab_path=paths.dns_keytab,
1349 dnspass=dnspass)
1351 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1352 assert isinstance(domainguid, str)
1354 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1355 hostip=hostip,
1356 hostip6=hostip6, hostname=names.hostname,
1357 realm=names.realm,
1358 domainguid=domainguid, ntdsguid=names.ntdsguid)
1360 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1361 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1363 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1364 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1365 keytab_name=paths.dns_keytab)
1366 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1367 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1369 create_krb5_conf(paths.krb5conf, setup_path,
1370 dnsdomain=names.dnsdomain, hostname=names.hostname,
1371 realm=names.realm)
1372 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1374 if provision_backend.post_setup is not None:
1375 provision_backend.post_setup()
1377 if provision_backend.shutdown is not None:
1378 provision_backend.shutdown()
1380 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1381 ldapi_url)
1383 #Now commit the secrets.ldb to disk
1384 secrets_ldb.transaction_commit()
1386 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1388 message("Once the above files are installed, your Samba4 server will be ready to use")
1389 message("Server Role: %s" % serverrole)
1390 message("Hostname: %s" % names.hostname)
1391 message("NetBIOS Domain: %s" % names.domain)
1392 message("DNS Domain: %s" % names.dnsdomain)
1393 message("DOMAIN SID: %s" % str(domainsid))
1394 if samdb_fill == FILL_FULL:
1395 message("Admin password: %s" % adminpass)
1396 if provision_backend.type is not "ldb":
1397 if provision_backend.credentials.get_bind_dn() is not None:
1398 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1399 else:
1400 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1402 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1404 if provision_backend.slapd_command_escaped is not None:
1405 # now display slapd_command_file.txt to show how slapd must be started next time
1406 message("Use later the following commandline to start slapd, then Samba:")
1407 message(provision_backend.slapd_command_escaped)
1408 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1411 result = ProvisionResult()
1412 result.domaindn = domaindn
1413 result.paths = paths
1414 result.lp = lp
1415 result.samdb = samdb
1416 return result
1420 def provision_become_dc(setup_dir=None,
1421 smbconf=None, targetdir=None, realm=None,
1422 rootdn=None, domaindn=None, schemadn=None,
1423 configdn=None, serverdn=None,
1424 domain=None, hostname=None, domainsid=None,
1425 adminpass=None, krbtgtpass=None, domainguid=None,
1426 policyguid=None, policyguid_dc=None, invocationid=None,
1427 machinepass=None,
1428 dnspass=None, root=None, nobody=None, users=None,
1429 wheel=None, backup=None, serverrole=None,
1430 ldap_backend=None, ldap_backend_type=None,
1431 sitename=None, debuglevel=1):
1433 def message(text):
1434 """print a message if quiet is not set."""
1435 print text
1437 glue.set_debug_level(debuglevel)
1439 return provision(setup_dir, message, system_session(), None,
1440 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1441 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1442 configdn=configdn, serverdn=serverdn, domain=domain,
1443 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1444 machinepass=machinepass, serverrole="domain controller",
1445 sitename=sitename)
1448 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1449 """Create a PHP LDAP admin configuration file.
1451 :param path: Path to write the configuration to.
1452 :param setup_path: Function to generate setup paths.
1454 setup_file(setup_path("phpldapadmin-config.php"), path,
1455 {"S4_LDAPI_URI": ldapi_uri})
1458 def create_zone_file(path, setup_path, dnsdomain,
1459 hostip, hostip6, hostname, realm, domainguid,
1460 ntdsguid):
1461 """Write out a DNS zone file, from the info in the current database.
1463 :param path: Path of the new zone file.
1464 :param setup_path: Setup path function.
1465 :param dnsdomain: DNS Domain name
1466 :param domaindn: DN of the Domain
1467 :param hostip: Local IPv4 IP
1468 :param hostip6: Local IPv6 IP
1469 :param hostname: Local hostname
1470 :param realm: Realm name
1471 :param domainguid: GUID of the domain.
1472 :param ntdsguid: GUID of the hosts nTDSDSA record.
1474 assert isinstance(domainguid, str)
1476 if hostip6 is not None:
1477 hostip6_base_line = " IN AAAA " + hostip6
1478 hostip6_host_line = hostname + " IN AAAA " + hostip6
1479 else:
1480 hostip6_base_line = ""
1481 hostip6_host_line = ""
1483 if hostip is not None:
1484 hostip_base_line = " IN A " + hostip
1485 hostip_host_line = hostname + " IN A " + hostip
1486 else:
1487 hostip_base_line = ""
1488 hostip_host_line = ""
1490 setup_file(setup_path("provision.zone"), path, {
1491 "HOSTNAME": hostname,
1492 "DNSDOMAIN": dnsdomain,
1493 "REALM": realm,
1494 "HOSTIP_BASE_LINE": hostip_base_line,
1495 "HOSTIP_HOST_LINE": hostip_host_line,
1496 "DOMAINGUID": domainguid,
1497 "DATESTRING": time.strftime("%Y%m%d%H"),
1498 "DEFAULTSITE": DEFAULTSITE,
1499 "NTDSGUID": ntdsguid,
1500 "HOSTIP6_BASE_LINE": hostip6_base_line,
1501 "HOSTIP6_HOST_LINE": hostip6_host_line,
1505 def create_named_conf(path, setup_path, realm, dnsdomain,
1506 private_dir):
1507 """Write out a file containing zone statements suitable for inclusion in a
1508 named.conf file (including GSS-TSIG configuration).
1510 :param path: Path of the new named.conf file.
1511 :param setup_path: Setup path function.
1512 :param realm: Realm name
1513 :param dnsdomain: DNS Domain name
1514 :param private_dir: Path to private directory
1515 :param keytab_name: File name of DNS keytab file
1518 setup_file(setup_path("named.conf"), path, {
1519 "DNSDOMAIN": dnsdomain,
1520 "REALM": realm,
1521 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1522 "PRIVATE_DIR": private_dir
1525 def create_named_txt(path, setup_path, realm, dnsdomain,
1526 private_dir, keytab_name):
1527 """Write out a file containing zone statements suitable for inclusion in a
1528 named.conf file (including GSS-TSIG configuration).
1530 :param path: Path of the new named.conf file.
1531 :param setup_path: Setup path function.
1532 :param realm: Realm name
1533 :param dnsdomain: DNS Domain name
1534 :param private_dir: Path to private directory
1535 :param keytab_name: File name of DNS keytab file
1538 setup_file(setup_path("named.txt"), path, {
1539 "DNSDOMAIN": dnsdomain,
1540 "REALM": realm,
1541 "DNS_KEYTAB": keytab_name,
1542 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1543 "PRIVATE_DIR": private_dir
1546 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1547 """Write out a file containing zone statements suitable for inclusion in a
1548 named.conf file (including GSS-TSIG configuration).
1550 :param path: Path of the new named.conf file.
1551 :param setup_path: Setup path function.
1552 :param dnsdomain: DNS Domain name
1553 :param hostname: Local hostname
1554 :param realm: Realm name
1557 setup_file(setup_path("krb5.conf"), path, {
1558 "DNSDOMAIN": dnsdomain,
1559 "HOSTNAME": hostname,
1560 "REALM": realm,