s4: regroup gpo modification in one function, set acl on files accordingly with ACL...
[Samba/cd1.git] / source4 / scripting / python / samba / provision.py
blobc7db82e6f14fc643d77dbcf232b4cbfc18fff0b2
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.misc import setntacl,dsacl2fsacl
51 from samba.ndr import ndr_pack,ndr_unpack
52 import urllib
53 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
54 from ms_display_specifiers import read_ms_ldif
55 from schema import Schema
56 from provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
57 from provisionexceptions import ProvisioningError, InvalidNetbiosName
58 from signal import SIGTERM
59 from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
60 __docformat__ = "restructuredText"
62 def find_setup_dir():
63 """Find the setup directory used by provision."""
64 dirname = os.path.dirname(__file__)
65 if "/site-packages/" in dirname:
66 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
67 for suffix in ["share/setup", "share/samba/setup", "setup"]:
68 ret = os.path.join(prefix, suffix)
69 if os.path.isdir(ret):
70 return ret
71 # In source tree
72 ret = os.path.join(dirname, "../../../setup")
73 if os.path.isdir(ret):
74 return ret
75 raise Exception("Unable to find setup directory.")
77 # descriptors of the naming contexts
78 # hard coded at this point, but will probably be changed when
79 # we enable different fsmo roles
81 def get_config_descriptor(domain_sid):
82 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
83 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
84 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
85 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
86 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
87 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
88 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
89 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
90 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
91 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
92 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
93 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
94 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
95 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
96 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
97 sec = security.descriptor.from_sddl(sddl, domain_sid)
98 return ndr_pack(sec)
100 def get_domain_descriptor(domain_sid):
101 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
102 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
103 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
104 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
105 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
106 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
107 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
108 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
109 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
110 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
111 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
112 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
113 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
114 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
115 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
116 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
117 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
119 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
120 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
121 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
122 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
123 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
124 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
125 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
126 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
127 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
128 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
129 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
130 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
131 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
132 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
133 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
134 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
135 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
136 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
137 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
138 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
139 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
140 "(A;;RPRC;;;RU)" \
141 "(A;CI;LC;;;RU)" \
142 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
143 "(A;;RP;;;WD)" \
144 "(A;;RPLCLORC;;;ED)" \
145 "(A;;RPLCLORC;;;AU)" \
146 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
147 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
148 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
149 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
150 sec = security.descriptor.from_sddl(sddl, domain_sid)
151 return ndr_pack(sec)
153 DEFAULTSITE = "Default-First-Site-Name"
155 # Exception classes
157 class ProvisionPaths(object):
158 def __init__(self):
159 self.shareconf = None
160 self.hklm = None
161 self.hkcu = None
162 self.hkcr = None
163 self.hku = None
164 self.hkpd = None
165 self.hkpt = None
166 self.samdb = None
167 self.idmapdb = None
168 self.secrets = None
169 self.keytab = None
170 self.dns_keytab = None
171 self.dns = None
172 self.winsdb = None
173 self.private_dir = None
174 self.ldapdir = None
175 self.slapdconf = None
176 self.modulesconf = None
177 self.memberofconf = None
178 self.olmmron = None
179 self.olmmrserveridsconf = None
180 self.olmmrsyncreplconf = None
181 self.olcdir = None
182 self.olslapd = None
183 self.olcseedldif = None
186 class ProvisionNames(object):
187 def __init__(self):
188 self.rootdn = None
189 self.domaindn = None
190 self.configdn = None
191 self.schemadn = None
192 self.ldapmanagerdn = None
193 self.dnsdomain = None
194 self.realm = None
195 self.netbiosname = None
196 self.domain = None
197 self.hostname = None
198 self.sitename = None
199 self.smbconf = None
202 class ProvisionResult(object):
203 def __init__(self):
204 self.paths = None
205 self.domaindn = None
206 self.lp = None
207 self.samdb = None
209 def check_install(lp, session_info, credentials):
210 """Check whether the current install seems ok.
212 :param lp: Loadparm context
213 :param session_info: Session information
214 :param credentials: Credentials
216 if lp.get("realm") == "":
217 raise Exception("Realm empty")
218 ldb = Ldb(lp.get("sam database"), session_info=session_info,
219 credentials=credentials, lp=lp)
220 if len(ldb.search("(cn=Administrator)")) != 1:
221 raise ProvisioningError("No administrator account found")
224 def findnss(nssfn, names):
225 """Find a user or group from a list of possibilities.
227 :param nssfn: NSS Function to try (should raise KeyError if not found)
228 :param names: Names to check.
229 :return: Value return by first names list.
231 for name in names:
232 try:
233 return nssfn(name)
234 except KeyError:
235 pass
236 raise KeyError("Unable to find user/group %r" % names)
239 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
240 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
243 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
244 """Setup a ldb in the private dir.
246 :param ldb: LDB file to import data into
247 :param ldif_path: Path of the LDIF file to load
248 :param subst_vars: Optional variables to subsitute in LDIF.
249 :param nocontrols: Optional list of controls, can be None for no controls
251 assert isinstance(ldif_path, str)
252 data = read_and_sub_file(ldif_path, subst_vars)
253 ldb.add_ldif(data,controls)
256 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
257 """Modify a ldb in the private dir.
259 :param ldb: LDB object.
260 :param ldif_path: LDIF file path.
261 :param subst_vars: Optional dictionary with substitution variables.
263 data = read_and_sub_file(ldif_path, subst_vars)
265 ldb.modify_ldif(data)
268 def setup_ldb(ldb, ldif_path, subst_vars):
269 """Import a LDIF a file into a LDB handle, optionally substituting variables.
271 :note: Either all LDIF data will be added or none (using transactions).
273 :param ldb: LDB file to import into.
274 :param ldif_path: Path to the LDIF file.
275 :param subst_vars: Dictionary with substitution variables.
277 assert ldb is not None
278 ldb.transaction_start()
279 try:
280 setup_add_ldif(ldb, ldif_path, subst_vars)
281 except:
282 ldb.transaction_cancel()
283 raise
284 ldb.transaction_commit()
287 def provision_paths_from_lp(lp, dnsdomain):
288 """Set the default paths for provisioning.
290 :param lp: Loadparm context.
291 :param dnsdomain: DNS Domain name
293 paths = ProvisionPaths()
294 paths.private_dir = lp.get("private dir")
295 paths.dns_keytab = "dns.keytab"
297 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
298 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
299 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
300 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
301 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
302 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
303 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
304 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
305 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
306 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
307 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
308 paths.phpldapadminconfig = os.path.join(paths.private_dir,
309 "phpldapadmin-config.php")
310 paths.ldapdir = os.path.join(paths.private_dir,
311 "ldap")
312 paths.slapdconf = os.path.join(paths.ldapdir,
313 "slapd.conf")
314 paths.slapdpid = os.path.join(paths.ldapdir,
315 "slapd.pid")
316 paths.modulesconf = os.path.join(paths.ldapdir,
317 "modules.conf")
318 paths.memberofconf = os.path.join(paths.ldapdir,
319 "memberof.conf")
320 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
321 "mmr_serverids.conf")
322 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
323 "mmr_syncrepl.conf")
324 paths.olcdir = os.path.join(paths.ldapdir,
325 "slapd.d")
326 paths.olcseedldif = os.path.join(paths.ldapdir,
327 "olc_seed.ldif")
328 paths.hklm = "hklm.ldb"
329 paths.hkcr = "hkcr.ldb"
330 paths.hkcu = "hkcu.ldb"
331 paths.hku = "hku.ldb"
332 paths.hkpd = "hkpd.ldb"
333 paths.hkpt = "hkpt.ldb"
335 paths.sysvol = lp.get("path", "sysvol")
337 paths.netlogon = lp.get("path", "netlogon")
339 paths.smbconf = lp.configfile
341 return paths
344 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
345 serverrole=None, rootdn=None, domaindn=None, configdn=None,
346 schemadn=None, serverdn=None, sitename=None):
347 """Guess configuration settings to use."""
349 if hostname is None:
350 hostname = socket.gethostname().split(".")[0]
352 netbiosname = lp.get("netbios name")
353 if netbiosname is None:
354 netbiosname = hostname
355 assert netbiosname is not None
356 netbiosname = netbiosname.upper()
357 if not valid_netbios_name(netbiosname):
358 raise InvalidNetbiosName(netbiosname)
360 if dnsdomain is None:
361 dnsdomain = lp.get("realm")
362 if dnsdomain is None or dnsdomain == "":
363 raise ProvisioningError("guess_names: 'realm' not specified in supplied smb.conf!")
365 dnsdomain = dnsdomain.lower()
367 if serverrole is None:
368 serverrole = lp.get("server role")
369 if serverrole is None:
370 raise ProvisioningError("guess_names: 'server role' not specified in supplied smb.conf!")
372 serverrole = serverrole.lower()
374 realm = dnsdomain.upper()
376 if lp.get("realm").upper() != realm:
377 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
379 if lp.get("server role").lower() != serverrole:
380 raise ProvisioningError("guess_names: server role '%s' in smb.conf must match chosen server role '%s'!", lp.get("server role").upper(), serverrole)
382 if serverrole == "domain controller":
383 if domain is None:
384 domain = lp.get("workgroup")
385 if domain is None:
386 raise ProvisioningError("guess_names: 'workgroup' not specified in supplied smb.conf!")
387 domain = domain.upper()
389 if lp.get("workgroup").upper() != domain:
390 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
392 if domaindn is None:
393 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
394 else:
395 domain = netbiosname
396 if domaindn is None:
397 domaindn = "DC=" + netbiosname
399 if not valid_netbios_name(domain):
400 raise InvalidNetbiosName(domain)
402 if hostname.upper() == realm:
403 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
404 if netbiosname == realm:
405 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
406 if domain == realm:
407 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
409 if rootdn is None:
410 rootdn = domaindn
412 if configdn is None:
413 configdn = "CN=Configuration," + rootdn
414 if schemadn is None:
415 schemadn = "CN=Schema," + configdn
417 if sitename is None:
418 sitename=DEFAULTSITE
420 names = ProvisionNames()
421 names.rootdn = rootdn
422 names.domaindn = domaindn
423 names.configdn = configdn
424 names.schemadn = schemadn
425 names.ldapmanagerdn = "CN=Manager," + rootdn
426 names.dnsdomain = dnsdomain
427 names.domain = domain
428 names.realm = realm
429 names.netbiosname = netbiosname
430 names.hostname = hostname
431 names.sitename = sitename
432 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
434 return names
437 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
438 targetdir, sid_generator):
439 """Create a new smb.conf file based on a couple of basic settings.
441 assert smbconf is not None
442 if hostname is None:
443 hostname = socket.gethostname().split(".")[0]
444 netbiosname = hostname.upper()
446 if serverrole is None:
447 serverrole = "standalone"
449 assert serverrole in ("domain controller", "member server", "standalone")
450 if serverrole == "domain controller":
451 smbconfsuffix = "dc"
452 elif serverrole == "member server":
453 smbconfsuffix = "member"
454 elif serverrole == "standalone":
455 smbconfsuffix = "standalone"
457 if sid_generator is None:
458 sid_generator = "internal"
460 assert domain is not None
461 domain = domain.upper()
463 assert realm is not None
464 realm = realm.upper()
466 default_lp = param.LoadParm()
467 #Load non-existant file
468 if os.path.exists(smbconf):
469 default_lp.load(smbconf)
471 if targetdir is not None:
472 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
473 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
475 default_lp.set("lock dir", os.path.abspath(targetdir))
476 else:
477 privatedir_line = ""
478 lockdir_line = ""
480 if sid_generator == "internal":
481 sid_generator_line = ""
482 else:
483 sid_generator_line = "sid generator = " + sid_generator
485 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
486 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
488 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
489 smbconf, {
490 "NETBIOS_NAME": netbiosname,
491 "DOMAIN": domain,
492 "REALM": realm,
493 "SERVERROLE": serverrole,
494 "NETLOGONPATH": netlogon,
495 "SYSVOLPATH": sysvol,
496 "SIDGENERATOR_LINE": sid_generator_line,
497 "PRIVATEDIR_LINE": privatedir_line,
498 "LOCKDIR_LINE": lockdir_line
502 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
503 users_gid, wheel_gid):
504 """setup reasonable name mappings for sam names to unix names.
506 :param samdb: SamDB object.
507 :param idmap: IDmap db object.
508 :param sid: The domain sid.
509 :param domaindn: The domain DN.
510 :param root_uid: uid of the UNIX root user.
511 :param nobody_uid: uid of the UNIX nobody user.
512 :param users_gid: gid of the UNIX users group.
513 :param wheel_gid: gid of the UNIX wheel group."""
515 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
516 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
518 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
519 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
521 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
522 provision_backend, names, schema,
523 serverrole,
524 erase=False):
525 """Setup the partitions for the SAM database.
527 Alternatively, provision() may call this, and then populate the database.
529 :note: This will wipe the Sam Database!
531 :note: This function always removes the local SAM LDB file. The erase
532 parameter controls whether to erase the existing data, which
533 may not be stored locally but in LDAP.
536 assert session_info is not None
538 old_partitions = None
539 new_partitions = None
541 # We use options=["modules:"] to stop the modules loading - we
542 # just want to wipe and re-initialise the database, not start it up
544 try:
545 os.unlink(samdb_path)
546 except OSError:
547 pass
549 samdb = Ldb(url=samdb_path, session_info=session_info,
550 lp=lp, options=["modules:"])
552 ldap_backend_line = "# No LDAP backend"
553 if provision_backend.type is not "ldb":
554 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
556 samdb.transaction_start()
557 try:
558 message("Setting up sam.ldb partitions and settings")
559 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
560 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
561 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
562 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
563 "LDAP_BACKEND_LINE": ldap_backend_line,
567 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
568 "BACKEND_TYPE": provision_backend.type,
569 "SERVER_ROLE": serverrole
572 message("Setting up sam.ldb rootDSE")
573 setup_samdb_rootdse(samdb, setup_path, names)
575 except:
576 samdb.transaction_cancel()
577 raise
579 samdb.transaction_commit()
582 def secretsdb_self_join(secretsdb, domain,
583 netbiosname, machinepass, domainsid=None,
584 realm=None, dnsdomain=None,
585 keytab_path=None,
586 key_version_number=1,
587 secure_channel_type=SEC_CHAN_WKSTA):
588 """Add domain join-specific bits to a secrets database.
590 :param secretsdb: Ldb Handle to the secrets database
591 :param machinepass: Machine password
593 attrs=["whenChanged",
594 "secret",
595 "priorSecret",
596 "priorChanged",
597 "krb5Keytab",
598 "privateKeytab"]
601 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
602 msg["secureChannelType"] = str(secure_channel_type)
603 msg["flatname"] = [domain]
604 msg["objectClass"] = ["top", "primaryDomain"]
605 if realm is not None:
606 if dnsdomain is None:
607 dnsdomain = realm.lower()
608 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
609 msg["realm"] = realm
610 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
611 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
612 msg["privateKeytab"] = ["secrets.keytab"];
615 msg["secret"] = [machinepass]
616 msg["samAccountName"] = ["%s$" % netbiosname]
617 msg["secureChannelType"] = [str(secure_channel_type)]
618 if domainsid is not None:
619 msg["objectSid"] = [ndr_pack(domainsid)]
621 res = secretsdb.search(base="cn=Primary Domains",
622 attrs=attrs,
623 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
624 scope=SCOPE_ONELEVEL)
626 for del_msg in res:
627 if del_msg.dn is not msg.dn:
628 secretsdb.delete(del_msg.dn)
630 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
632 if len(res) == 1:
633 msg["priorSecret"] = res[0]["secret"]
634 msg["priorWhenChanged"] = res[0]["whenChanged"]
636 if res["privateKeytab"] is not None:
637 msg["privateKeytab"] = res[0]["privateKeytab"]
639 if res["krb5Keytab"] is not None:
640 msg["krb5Keytab"] = res[0]["krb5Keytab"]
642 for el in msg:
643 el.set_flags(ldb.FLAG_MOD_REPLACE)
644 secretsdb.modify(msg)
645 else:
646 secretsdb.add(msg)
649 def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
650 dns_keytab_path, dnspass):
651 """Add DNS specific bits to a secrets database.
653 :param secretsdb: Ldb Handle to the secrets database
654 :param setup_path: Setup path function
655 :param machinepass: Machine password
657 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
658 "REALM": realm,
659 "DNSDOMAIN": dnsdomain,
660 "DNS_KEYTAB": dns_keytab_path,
661 "DNSPASS_B64": b64encode(dnspass),
665 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
666 """Setup the secrets database.
668 :param path: Path to the secrets database.
669 :param setup_path: Get the path to a setup file.
670 :param session_info: Session info.
671 :param credentials: Credentials
672 :param lp: Loadparm context
673 :return: LDB handle for the created secrets database
675 if os.path.exists(path):
676 os.unlink(path)
677 secrets_ldb = Ldb(path, session_info=session_info,
678 lp=lp)
679 secrets_ldb.erase()
680 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
681 secrets_ldb = Ldb(path, session_info=session_info,
682 lp=lp)
683 secrets_ldb.transaction_start()
684 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
686 if backend_credentials is not None and backend_credentials.authentication_requested():
687 if backend_credentials.get_bind_dn() is not None:
688 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
689 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
690 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
692 else:
693 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
694 "LDAPADMINUSER": backend_credentials.get_username(),
695 "LDAPADMINREALM": backend_credentials.get_realm(),
696 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
699 return secrets_ldb
701 def setup_privileges(path, setup_path, session_info, lp):
702 """Setup the privileges database.
704 :param path: Path to the privileges database.
705 :param setup_path: Get the path to a setup file.
706 :param session_info: Session info.
707 :param credentials: Credentials
708 :param lp: Loadparm context
709 :return: LDB handle for the created secrets database
711 if os.path.exists(path):
712 os.unlink(path)
713 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
714 privilege_ldb.erase()
715 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
718 def setup_registry(path, setup_path, session_info, lp):
719 """Setup the registry.
721 :param path: Path to the registry database
722 :param setup_path: Function that returns the path to a setup.
723 :param session_info: Session information
724 :param credentials: Credentials
725 :param lp: Loadparm context
727 reg = registry.Registry()
728 hive = registry.open_ldb(path, session_info=session_info,
729 lp_ctx=lp)
730 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
731 provision_reg = setup_path("provision.reg")
732 assert os.path.exists(provision_reg)
733 reg.diff_apply(provision_reg)
736 def setup_idmapdb(path, setup_path, session_info, lp):
737 """Setup the idmap database.
739 :param path: path to the idmap database
740 :param setup_path: Function that returns a path to a setup file
741 :param session_info: Session information
742 :param credentials: Credentials
743 :param lp: Loadparm context
745 if os.path.exists(path):
746 os.unlink(path)
748 idmap_ldb = IDmapDB(path, session_info=session_info,
749 lp=lp)
751 idmap_ldb.erase()
752 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
753 return idmap_ldb
756 def setup_samdb_rootdse(samdb, setup_path, names):
757 """Setup the SamDB rootdse.
759 :param samdb: Sam Database handle
760 :param setup_path: Obtain setup path
762 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
763 "SCHEMADN": names.schemadn,
764 "NETBIOSNAME": names.netbiosname,
765 "DNSDOMAIN": names.dnsdomain,
766 "REALM": names.realm,
767 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
768 "DOMAINDN": names.domaindn,
769 "ROOTDN": names.rootdn,
770 "CONFIGDN": names.configdn,
771 "SERVERDN": names.serverdn,
775 def setup_self_join(samdb, names,
776 machinepass, dnspass,
777 domainsid, invocationid, setup_path,
778 policyguid, policyguid_dc, domainControllerFunctionality,
779 ntdsguid):
780 """Join a host to its own domain."""
781 assert isinstance(invocationid, str)
782 if ntdsguid is not None:
783 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
784 else:
785 ntdsguid_line = ""
786 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
787 "CONFIGDN": names.configdn,
788 "SCHEMADN": names.schemadn,
789 "DOMAINDN": names.domaindn,
790 "SERVERDN": names.serverdn,
791 "INVOCATIONID": invocationid,
792 "NETBIOSNAME": names.netbiosname,
793 "DEFAULTSITE": names.sitename,
794 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
795 "MACHINEPASS_B64": b64encode(machinepass),
796 "REALM": names.realm,
797 "DOMAIN": names.domain,
798 "DOMAINSID": str(domainsid),
799 "DNSDOMAIN": names.dnsdomain,
800 "SAMBA_VERSION_STRING": version,
801 "NTDSGUID": ntdsguid_line,
802 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
804 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
805 "POLICYGUID": policyguid,
806 "POLICYGUID_DC": policyguid_dc,
807 "DNSDOMAIN": names.dnsdomain,
808 "DOMAINSID": str(domainsid),
809 "DOMAINDN": names.domaindn})
811 # add the NTDSGUID based SPNs
812 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
813 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
814 expression="", scope=SCOPE_BASE)
815 assert isinstance(names.ntdsguid, str)
817 # Setup fSMORoleOwner entries to point at the newly created DC entry
818 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
819 "DOMAIN": names.domain,
820 "DNSDOMAIN": names.dnsdomain,
821 "DOMAINDN": names.domaindn,
822 "CONFIGDN": names.configdn,
823 "SCHEMADN": names.schemadn,
824 "DEFAULTSITE": names.sitename,
825 "SERVERDN": names.serverdn,
826 "NETBIOSNAME": names.netbiosname,
827 "NTDSGUID": names.ntdsguid,
828 "DNSPASS_B64": b64encode(dnspass),
831 def set_gpo_acl(path,acl,setfileacl):
832 if setfileacl:
833 setntacl(path,acl)
834 for root, dirs, files in os.walk(path, topdown=False):
835 for name in files:
836 setntacl(os.path.join(root, name),acl)
837 for name in dirs:
838 setntacl(os.path.join(root, name),acl)
840 def setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid,setfileacl):
841 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
842 "{" + policyguid + "}")
843 os.makedirs(policy_path, 0755)
844 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
845 "[General]\r\nVersion=65543")
846 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
847 os.makedirs(os.path.join(policy_path, "USER"), 0755)
849 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
850 "{" + policyguid_dc + "}")
851 os.makedirs(policy_path_dc, 0755)
852 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
853 "[General]\r\nVersion=2")
854 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
855 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
856 # call setntacl ...
857 res = samdb.search(base="CN={%s},CN=Policies,CN=System,%s"%(policyguid,names.domaindn),
858 attrs=["nTSecurityDescriptor"],
859 expression="", scope=SCOPE_BASE)
860 assert(len(res) > 0)
861 acl = ndr_unpack(security.descriptor,str(res[0]["nTSecurityDescriptor"])).as_sddl(security.dom_sid("S-1-5-21-1"))
862 set_gpo_acl(policy_path_dc,dsacl2fsacl(acl),setfileacl)
864 res = samdb.search(base="CN={%s},CN=Policies,CN=System,%s"%(policyguid_dc,names.domaindn),
865 attrs=["nTSecurityDescriptor"],
866 expression="", scope=SCOPE_BASE)
867 assert(len(res) > 0)
868 acl = ndr_unpack(security.descriptor,str(res[0]["nTSecurityDescriptor"])).as_sddl(security.dom_sid("S-1-5-21-1"))
869 set_gpo_acl(policy_path,dsacl2fsacl(acl),setfileacl)
872 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
873 names, message,
874 domainsid, domainguid, policyguid, policyguid_dc,
875 fill, adminpass, krbtgtpass,
876 machinepass, invocationid, dnspass, ntdsguid,
877 serverrole, dom_for_fun_level=None,
878 schema=None):
879 """Setup a complete SAM Database.
881 :note: This will wipe the main SAM database file!
884 # ATTENTION: Do NOT change these default values without discussion with the
885 # team and/or release manager. They have a big impact on the whole program!
886 domainControllerFunctionality = DS_DC_FUNCTION_2008
888 if dom_for_fun_level is None:
889 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
890 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
891 message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
893 if dom_for_fun_level > domainControllerFunctionality:
894 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!")
896 domainFunctionality = dom_for_fun_level
897 forestFunctionality = dom_for_fun_level
899 # Also wipes the database
900 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
901 provision_backend=provision_backend, session_info=session_info,
902 names=names,
903 serverrole=serverrole, schema=schema)
905 if (schema == None):
906 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
908 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
909 samdb = Ldb(session_info=session_info,
910 credentials=provision_backend.credentials, lp=lp)
912 message("Pre-loading the Samba 4 and AD schema")
914 # Load the schema from the one we computed earlier
915 samdb.set_schema_from_ldb(schema.ldb)
917 # And now we can connect to the DB - the schema won't be loaded from the DB
918 samdb.connect(path)
920 if fill == FILL_DRS:
921 return samdb
923 samdb.transaction_start()
924 try:
925 # Set the domain functionality levels onto the database.
926 # Various module (the password_hash module in particular) need
927 # to know what level of AD we are emulating.
929 # These will be fixed into the database via the database
930 # modifictions below, but we need them set from the start.
931 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
932 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
933 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
935 samdb.set_domain_sid(str(domainsid))
936 samdb.set_invocation_id(invocationid)
938 message("Adding DomainDN: %s" % names.domaindn)
940 #impersonate domain admin
941 admin_session_info = admin_session(lp, str(domainsid))
942 samdb.set_session_info(admin_session_info)
943 if domainguid is not None:
944 domainguid_line = "objectGUID: %s\n-" % domainguid
945 else:
946 domainguid_line = ""
948 descr = b64encode(get_domain_descriptor(domainsid))
949 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
950 "DOMAINDN": names.domaindn,
951 "DOMAINGUID": domainguid_line,
952 "DESCRIPTOR": descr
956 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
957 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
958 "DOMAINSID": str(domainsid),
959 "SCHEMADN": names.schemadn,
960 "NETBIOSNAME": names.netbiosname,
961 "DEFAULTSITE": names.sitename,
962 "CONFIGDN": names.configdn,
963 "SERVERDN": names.serverdn,
964 "POLICYGUID": policyguid,
965 "DOMAINDN": names.domaindn,
966 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
967 "SAMBA_VERSION_STRING": version
970 message("Adding configuration container")
971 descr = b64encode(get_config_descriptor(domainsid))
972 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
973 "CONFIGDN": names.configdn,
974 "DESCRIPTOR": descr,
977 # The LDIF here was created when the Schema object was constructed
978 message("Setting up sam.ldb schema")
979 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
980 samdb.modify_ldif(schema.schema_dn_modify)
981 samdb.write_prefixes_from_schema()
982 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
983 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
984 {"SCHEMADN": names.schemadn})
986 message("Reopening sam.ldb with new schema");
987 samdb.transaction_commit()
988 samdb = Ldb(session_info=admin_session_info,
989 credentials=provision_backend.credentials, lp=lp)
990 samdb.connect(path)
991 samdb.transaction_start()
992 samdb.set_invocation_id(invocationid)
994 message("Setting up sam.ldb configuration data")
995 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
996 "CONFIGDN": names.configdn,
997 "NETBIOSNAME": names.netbiosname,
998 "DEFAULTSITE": names.sitename,
999 "DNSDOMAIN": names.dnsdomain,
1000 "DOMAIN": names.domain,
1001 "SCHEMADN": names.schemadn,
1002 "DOMAINDN": names.domaindn,
1003 "SERVERDN": names.serverdn,
1004 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1007 message("Setting up display specifiers")
1008 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1009 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1010 check_all_substituted(display_specifiers_ldif)
1011 samdb.add_ldif(display_specifiers_ldif)
1013 message("Adding users container")
1014 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1015 "DOMAINDN": names.domaindn})
1016 message("Modifying users container")
1017 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1018 "DOMAINDN": names.domaindn})
1019 message("Adding computers container")
1020 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1021 "DOMAINDN": names.domaindn})
1022 message("Modifying computers container")
1023 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1024 "DOMAINDN": names.domaindn})
1025 message("Setting up sam.ldb data")
1026 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1027 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1028 "DOMAINDN": names.domaindn,
1029 "NETBIOSNAME": names.netbiosname,
1030 "DEFAULTSITE": names.sitename,
1031 "CONFIGDN": names.configdn,
1032 "SERVERDN": names.serverdn,
1033 "POLICYGUID_DC": policyguid_dc
1036 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1037 "DOMAINDN": names.domaindn})
1039 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1040 "CONFIGDN": names.configdn,
1041 "SCHEMADN": names.schemadn})
1042 if fill == FILL_FULL:
1043 message("Setting up sam.ldb users and groups")
1044 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1045 "DOMAINDN": names.domaindn,
1046 "DOMAINSID": str(domainsid),
1047 "CONFIGDN": names.configdn,
1048 "ADMINPASS_B64": b64encode(adminpass),
1049 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1052 message("Setting up self join")
1053 setup_self_join(samdb, names=names, invocationid=invocationid,
1054 dnspass=dnspass,
1055 machinepass=machinepass,
1056 domainsid=domainsid, policyguid=policyguid,
1057 policyguid_dc=policyguid_dc,
1058 setup_path=setup_path,
1059 domainControllerFunctionality=domainControllerFunctionality,
1060 ntdsguid=ntdsguid)
1062 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1063 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1064 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1065 assert isinstance(names.ntdsguid, str)
1067 except:
1068 samdb.transaction_cancel()
1069 raise
1071 samdb.transaction_commit()
1072 return samdb
1075 FILL_FULL = "FULL"
1076 FILL_NT4SYNC = "NT4SYNC"
1077 FILL_DRS = "DRS"
1080 def provision(setup_dir, message, session_info,
1081 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1082 realm=None,
1083 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1084 serverdn=None,
1085 domain=None, hostname=None, hostip=None, hostip6=None,
1086 domainsid=None, adminpass=None, ldapadminpass=None,
1087 krbtgtpass=None, domainguid=None,
1088 policyguid=None, policyguid_dc=None, invocationid=None,
1089 machinepass=None, ntdsguid=None,
1090 dnspass=None, root=None, nobody=None, users=None,
1091 wheel=None, backup=None, aci=None, serverrole=None,
1092 dom_for_fun_level=None,
1093 ldap_backend_extra_port=None, backend_type=None,
1094 sitename=None,
1095 ol_mmr_urls=None, ol_olc=None,
1096 setup_ds_path=None, slapd_path=None, nosync=False,
1097 ldap_dryrun_mode=False,setfileacl=False):
1098 """Provision samba4
1100 :note: caution, this wipes all existing data!
1103 def setup_path(file):
1104 return os.path.join(setup_dir, file)
1106 if domainsid is None:
1107 domainsid = security.random_sid()
1108 else:
1109 domainsid = security.dom_sid(domainsid)
1111 # create/adapt the group policy GUIDs
1112 if policyguid is None:
1113 policyguid = str(uuid.uuid4())
1114 policyguid = policyguid.upper()
1115 if policyguid_dc is None:
1116 policyguid_dc = str(uuid.uuid4())
1117 policyguid_dc = policyguid_dc.upper()
1119 if adminpass is None:
1120 adminpass = glue.generate_random_str(12)
1121 if krbtgtpass is None:
1122 krbtgtpass = glue.generate_random_str(12)
1123 if machinepass is None:
1124 machinepass = glue.generate_random_str(12)
1125 if dnspass is None:
1126 dnspass = glue.generate_random_str(12)
1127 if ldapadminpass is None:
1128 #Make a new, random password between Samba and it's LDAP server
1129 ldapadminpass=glue.generate_random_str(12)
1131 if backend_type is None:
1132 backend_type = "ldb"
1134 sid_generator = "internal"
1135 if backend_type == "fedora-ds":
1136 sid_generator = "backend"
1138 root_uid = findnss_uid([root or "root"])
1139 nobody_uid = findnss_uid([nobody or "nobody"])
1140 users_gid = findnss_gid([users or "users"])
1141 if wheel is None:
1142 wheel_gid = findnss_gid(["wheel", "adm"])
1143 else:
1144 wheel_gid = findnss_gid([wheel])
1146 if targetdir is not None:
1147 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1148 os.makedirs(os.path.join(targetdir, "etc"))
1149 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1150 elif smbconf is None:
1151 smbconf = param.default_path()
1153 # only install a new smb.conf if there isn't one there already
1154 if not os.path.exists(smbconf):
1155 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1156 targetdir, sid_generator)
1158 lp = param.LoadParm()
1159 lp.load(smbconf)
1161 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1162 dnsdomain=realm, serverrole=serverrole,
1163 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1164 serverdn=serverdn, sitename=sitename)
1166 paths = provision_paths_from_lp(lp, names.dnsdomain)
1168 if hostip is None:
1169 try:
1170 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1171 except socket.gaierror, (socket.EAI_NODATA, msg):
1172 hostip = None
1174 if hostip6 is None:
1175 try:
1176 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1177 except socket.gaierror, (socket.EAI_NODATA, msg):
1178 hostip6 = None
1180 if serverrole is None:
1181 serverrole = lp.get("server role")
1183 assert serverrole in ("domain controller", "member server", "standalone")
1184 if invocationid is None:
1185 invocationid = str(uuid.uuid4())
1187 if not os.path.exists(paths.private_dir):
1188 os.mkdir(paths.private_dir)
1189 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1190 os.mkdir(os.path.join(paths.private_dir,"tls"))
1192 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1194 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1196 if backend_type == "ldb":
1197 provision_backend = LDBBackend(backend_type,
1198 paths=paths, setup_path=setup_path,
1199 lp=lp, credentials=credentials,
1200 names=names,
1201 message=message)
1202 elif backend_type == "existing":
1203 provision_backend = ExistingBackend(backend_type,
1204 paths=paths, setup_path=setup_path,
1205 lp=lp, credentials=credentials,
1206 names=names,
1207 message=message)
1208 elif backend_type == "fedora-ds":
1209 provision_backend = FDSBackend(backend_type,
1210 paths=paths, setup_path=setup_path,
1211 lp=lp, credentials=credentials,
1212 names=names,
1213 message=message,
1214 domainsid=domainsid,
1215 schema=schema,
1216 hostname=hostname,
1217 ldapadminpass=ldapadminpass,
1218 slapd_path=slapd_path,
1219 ldap_backend_extra_port=ldap_backend_extra_port,
1220 ldap_dryrun_mode=ldap_dryrun_mode,
1221 root=root,
1222 setup_ds_path=setup_ds_path)
1223 elif backend_type == "openldap":
1224 provision_backend = OpenLDAPBackend(backend_type,
1225 paths=paths, setup_path=setup_path,
1226 lp=lp, credentials=credentials,
1227 names=names,
1228 message=message,
1229 domainsid=domainsid,
1230 schema=schema,
1231 hostname=hostname,
1232 ldapadminpass=ldapadminpass,
1233 slapd_path=slapd_path,
1234 ldap_backend_extra_port=ldap_backend_extra_port,
1235 ldap_dryrun_mode=ldap_dryrun_mode,
1236 ol_mmr_urls=ol_mmr_urls,
1237 nosync=nosync)
1238 else:
1239 raise ProvisioningError("Unknown LDAP backend type selected")
1241 provision_backend.init()
1242 provision_backend.start()
1244 # only install a new shares config db if there is none
1245 if not os.path.exists(paths.shareconf):
1246 message("Setting up share.ldb")
1247 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1248 lp=lp)
1249 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1252 message("Setting up secrets.ldb")
1253 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1254 session_info=session_info,
1255 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1257 message("Setting up the registry")
1258 setup_registry(paths.hklm, setup_path, session_info,
1259 lp=lp)
1261 message("Setting up the privileges database")
1262 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1264 message("Setting up idmap db")
1265 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1266 lp=lp)
1268 message("Setting up SAM db")
1269 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1270 provision_backend, lp, names,
1271 message,
1272 domainsid=domainsid,
1273 schema=schema, domainguid=domainguid,
1274 policyguid=policyguid, policyguid_dc=policyguid_dc,
1275 fill=samdb_fill,
1276 adminpass=adminpass, krbtgtpass=krbtgtpass,
1277 invocationid=invocationid,
1278 machinepass=machinepass, dnspass=dnspass,
1279 ntdsguid=ntdsguid, serverrole=serverrole,
1280 dom_for_fun_level=dom_for_fun_level)
1282 if serverrole == "domain controller":
1283 if paths.netlogon is None:
1284 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1285 message("Please either remove %s or see the template at %s" %
1286 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1287 assert(paths.netlogon is not None)
1289 if paths.sysvol is None:
1290 message("Existing smb.conf does not have a [sysvol] 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.sysvol is not None)
1295 # Set up group policies (domain policy and domain controller policy)
1296 setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid,setfileacl)
1298 if not os.path.isdir(paths.netlogon):
1299 os.makedirs(paths.netlogon, 0755)
1301 if samdb_fill == FILL_FULL:
1302 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1303 root_uid=root_uid, nobody_uid=nobody_uid,
1304 users_gid=users_gid, wheel_gid=wheel_gid)
1306 message("Setting up sam.ldb rootDSE marking as synchronized")
1307 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1309 secretsdb_self_join(secrets_ldb, domain=names.domain,
1310 realm=names.realm,
1311 dnsdomain=names.dnsdomain,
1312 netbiosname=names.netbiosname,
1313 domainsid=domainsid,
1314 machinepass=machinepass,
1315 secure_channel_type=SEC_CHAN_BDC)
1317 if serverrole == "domain controller":
1318 secretsdb_setup_dns(secrets_ldb, setup_path,
1319 realm=names.realm, dnsdomain=names.dnsdomain,
1320 dns_keytab_path=paths.dns_keytab,
1321 dnspass=dnspass)
1323 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1324 assert isinstance(domainguid, str)
1326 # Only make a zone file on the first DC, it should be replicated
1327 # with DNS replication
1328 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1329 hostip=hostip,
1330 hostip6=hostip6, hostname=names.hostname,
1331 realm=names.realm,
1332 domainguid=domainguid, ntdsguid=names.ntdsguid)
1334 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1335 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1337 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1338 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1339 keytab_name=paths.dns_keytab)
1340 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1341 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1343 create_krb5_conf(paths.krb5conf, setup_path,
1344 dnsdomain=names.dnsdomain, hostname=names.hostname,
1345 realm=names.realm)
1346 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1348 provision_backend.post_setup()
1349 provision_backend.shutdown()
1351 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1352 ldapi_url)
1354 #Now commit the secrets.ldb to disk
1355 secrets_ldb.transaction_commit()
1357 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1359 message("Once the above files are installed, your Samba4 server will be ready to use")
1360 message("Server Role: %s" % serverrole)
1361 message("Hostname: %s" % names.hostname)
1362 message("NetBIOS Domain: %s" % names.domain)
1363 message("DNS Domain: %s" % names.dnsdomain)
1364 message("DOMAIN SID: %s" % str(domainsid))
1365 if samdb_fill == FILL_FULL:
1366 message("Admin password: %s" % adminpass)
1367 if provision_backend.type is not "ldb":
1368 if provision_backend.credentials.get_bind_dn() is not None:
1369 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1370 else:
1371 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1373 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1375 if provision_backend.slapd_command_escaped is not None:
1376 # now display slapd_command_file.txt to show how slapd must be started next time
1377 message("Use later the following commandline to start slapd, then Samba:")
1378 message(provision_backend.slapd_command_escaped)
1379 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1382 result = ProvisionResult()
1383 result.domaindn = domaindn
1384 result.paths = paths
1385 result.lp = lp
1386 result.samdb = samdb
1387 return result
1391 def provision_become_dc(setup_dir=None,
1392 smbconf=None, targetdir=None, realm=None,
1393 rootdn=None, domaindn=None, schemadn=None,
1394 configdn=None, serverdn=None,
1395 domain=None, hostname=None, domainsid=None,
1396 adminpass=None, krbtgtpass=None, domainguid=None,
1397 policyguid=None, policyguid_dc=None, invocationid=None,
1398 machinepass=None,
1399 dnspass=None, root=None, nobody=None, users=None,
1400 wheel=None, backup=None, serverrole=None,
1401 ldap_backend=None, ldap_backend_type=None,
1402 sitename=None, debuglevel=1):
1404 def message(text):
1405 """print a message if quiet is not set."""
1406 print text
1408 glue.set_debug_level(debuglevel)
1410 return provision(setup_dir, message, system_session(), None,
1411 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1412 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1413 configdn=configdn, serverdn=serverdn, domain=domain,
1414 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1415 machinepass=machinepass, serverrole="domain controller",
1416 sitename=sitename)
1419 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1420 """Create a PHP LDAP admin configuration file.
1422 :param path: Path to write the configuration to.
1423 :param setup_path: Function to generate setup paths.
1425 setup_file(setup_path("phpldapadmin-config.php"), path,
1426 {"S4_LDAPI_URI": ldapi_uri})
1429 def create_zone_file(path, setup_path, dnsdomain,
1430 hostip, hostip6, hostname, realm, domainguid,
1431 ntdsguid):
1432 """Write out a DNS zone file, from the info in the current database.
1434 :param path: Path of the new zone file.
1435 :param setup_path: Setup path function.
1436 :param dnsdomain: DNS Domain name
1437 :param domaindn: DN of the Domain
1438 :param hostip: Local IPv4 IP
1439 :param hostip6: Local IPv6 IP
1440 :param hostname: Local hostname
1441 :param realm: Realm name
1442 :param domainguid: GUID of the domain.
1443 :param ntdsguid: GUID of the hosts nTDSDSA record.
1445 assert isinstance(domainguid, str)
1447 if hostip6 is not None:
1448 hostip6_base_line = " IN AAAA " + hostip6
1449 hostip6_host_line = hostname + " IN AAAA " + hostip6
1450 else:
1451 hostip6_base_line = ""
1452 hostip6_host_line = ""
1454 if hostip is not None:
1455 hostip_base_line = " IN A " + hostip
1456 hostip_host_line = hostname + " IN A " + hostip
1457 else:
1458 hostip_base_line = ""
1459 hostip_host_line = ""
1461 setup_file(setup_path("provision.zone"), path, {
1462 "HOSTNAME": hostname,
1463 "DNSDOMAIN": dnsdomain,
1464 "REALM": realm,
1465 "HOSTIP_BASE_LINE": hostip_base_line,
1466 "HOSTIP_HOST_LINE": hostip_host_line,
1467 "DOMAINGUID": domainguid,
1468 "DATESTRING": time.strftime("%Y%m%d%H"),
1469 "DEFAULTSITE": DEFAULTSITE,
1470 "NTDSGUID": ntdsguid,
1471 "HOSTIP6_BASE_LINE": hostip6_base_line,
1472 "HOSTIP6_HOST_LINE": hostip6_host_line,
1476 def create_named_conf(path, setup_path, realm, dnsdomain,
1477 private_dir):
1478 """Write out a file containing zone statements suitable for inclusion in a
1479 named.conf file (including GSS-TSIG configuration).
1481 :param path: Path of the new named.conf file.
1482 :param setup_path: Setup path function.
1483 :param realm: Realm name
1484 :param dnsdomain: DNS Domain name
1485 :param private_dir: Path to private directory
1486 :param keytab_name: File name of DNS keytab file
1489 setup_file(setup_path("named.conf"), path, {
1490 "DNSDOMAIN": dnsdomain,
1491 "REALM": realm,
1492 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1493 "PRIVATE_DIR": private_dir
1496 def create_named_txt(path, setup_path, realm, dnsdomain,
1497 private_dir, keytab_name):
1498 """Write out a file containing zone statements suitable for inclusion in a
1499 named.conf file (including GSS-TSIG configuration).
1501 :param path: Path of the new named.conf file.
1502 :param setup_path: Setup path function.
1503 :param realm: Realm name
1504 :param dnsdomain: DNS Domain name
1505 :param private_dir: Path to private directory
1506 :param keytab_name: File name of DNS keytab file
1509 setup_file(setup_path("named.txt"), path, {
1510 "DNSDOMAIN": dnsdomain,
1511 "REALM": realm,
1512 "DNS_KEYTAB": keytab_name,
1513 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1514 "PRIVATE_DIR": private_dir
1517 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1518 """Write out a file containing zone statements suitable for inclusion in a
1519 named.conf file (including GSS-TSIG configuration).
1521 :param path: Path of the new named.conf file.
1522 :param setup_path: Setup path function.
1523 :param dnsdomain: DNS Domain name
1524 :param hostname: Local hostname
1525 :param realm: Realm name
1528 setup_file(setup_path("krb5.conf"), path, {
1529 "DNSDOMAIN": dnsdomain,
1530 "HOSTNAME": hostname,
1531 "REALM": realm,