General cleanups of python code, hinted by pyflakes.
[Samba/eduardoll.git] / source4 / scripting / python / samba / provision.py
blob57c8e7f25b1bfc30f0663616596159bda30e7b7f
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 pwd
31 import grp
32 import time
33 import uuid
34 import socket
35 import param
36 import registry
37 import urllib
38 import shutil
40 import ldb
42 from samba.auth import system_session, admin_session
43 from samba import glue, version, Ldb, substitute_var, valid_netbios_name
44 from samba import check_all_substituted, read_and_sub_file, setup_file
45 from samba import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008
46 from samba.dcerpc import security
47 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
48 from samba.idmap import IDmapDB
49 from samba.ntacls import setntacl, dsacl2fsacl
50 from samba.ndr import ndr_pack,ndr_unpack
51 from samba.schema import Schema
52 from ms_display_specifiers import read_ms_ldif
53 from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
54 from provisionexceptions import ProvisioningError, InvalidNetbiosName
56 __docformat__ = "restructuredText"
58 def find_setup_dir():
59 """Find the setup directory used by provision."""
60 dirname = os.path.dirname(__file__)
61 if "/site-packages/" in dirname:
62 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
63 for suffix in ["share/setup", "share/samba/setup", "setup"]:
64 ret = os.path.join(prefix, suffix)
65 if os.path.isdir(ret):
66 return ret
67 # In source tree
68 ret = os.path.join(dirname, "../../../setup")
69 if os.path.isdir(ret):
70 return ret
71 raise Exception("Unable to find setup directory.")
73 # descriptors of the naming contexts
74 # hard coded at this point, but will probably be changed when
75 # we enable different fsmo roles
77 def get_config_descriptor(domain_sid):
78 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
79 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
85 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
86 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
87 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
88 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
89 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
90 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
91 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
92 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
93 sec = security.descriptor.from_sddl(sddl, domain_sid)
94 return ndr_pack(sec)
96 def get_domain_descriptor(domain_sid):
97 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
98 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
99 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
100 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
101 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
102 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
103 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
104 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
105 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
106 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
107 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
108 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
109 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
110 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
111 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
112 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
113 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
114 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
119 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
120 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
121 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
122 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
123 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
125 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
126 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
127 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
128 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
129 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
133 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
134 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
135 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
136 "(A;;RPRC;;;RU)" \
137 "(A;CI;LC;;;RU)" \
138 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
139 "(A;;RP;;;WD)" \
140 "(A;;RPLCLORC;;;ED)" \
141 "(A;;RPLCLORC;;;AU)" \
142 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
143 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
144 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
146 sec = security.descriptor.from_sddl(sddl, domain_sid)
147 return ndr_pack(sec)
149 DEFAULTSITE = "Default-First-Site-Name"
151 # Exception classes
153 class ProvisionPaths(object):
154 def __init__(self):
155 self.shareconf = None
156 self.hklm = None
157 self.hkcu = None
158 self.hkcr = None
159 self.hku = None
160 self.hkpd = None
161 self.hkpt = None
162 self.samdb = None
163 self.idmapdb = None
164 self.secrets = None
165 self.keytab = None
166 self.dns_keytab = None
167 self.dns = None
168 self.winsdb = None
169 self.private_dir = None
170 self.ldapdir = None
171 self.slapdconf = None
172 self.modulesconf = None
173 self.memberofconf = None
174 self.olmmron = None
175 self.olmmrserveridsconf = None
176 self.olmmrsyncreplconf = None
177 self.olcdir = None
178 self.olslapd = None
179 self.olcseedldif = None
182 class ProvisionNames(object):
183 def __init__(self):
184 self.rootdn = None
185 self.domaindn = None
186 self.configdn = None
187 self.schemadn = None
188 self.ldapmanagerdn = None
189 self.dnsdomain = None
190 self.realm = None
191 self.netbiosname = None
192 self.domain = None
193 self.hostname = None
194 self.sitename = None
195 self.smbconf = None
198 class ProvisionResult(object):
199 def __init__(self):
200 self.paths = None
201 self.domaindn = None
202 self.lp = None
203 self.samdb = None
205 def check_install(lp, session_info, credentials):
206 """Check whether the current install seems ok.
208 :param lp: Loadparm context
209 :param session_info: Session information
210 :param credentials: Credentials
212 if lp.get("realm") == "":
213 raise Exception("Realm empty")
214 ldb = Ldb(lp.get("sam database"), session_info=session_info,
215 credentials=credentials, lp=lp)
216 if len(ldb.search("(cn=Administrator)")) != 1:
217 raise ProvisioningError("No administrator account found")
220 def findnss(nssfn, names):
221 """Find a user or group from a list of possibilities.
223 :param nssfn: NSS Function to try (should raise KeyError if not found)
224 :param names: Names to check.
225 :return: Value return by first names list.
227 for name in names:
228 try:
229 return nssfn(name)
230 except KeyError:
231 pass
232 raise KeyError("Unable to find user/group %r" % names)
235 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
236 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
239 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
240 """Setup a ldb in the private dir.
242 :param ldb: LDB file to import data into
243 :param ldif_path: Path of the LDIF file to load
244 :param subst_vars: Optional variables to subsitute in LDIF.
245 :param nocontrols: Optional list of controls, can be None for no controls
247 assert isinstance(ldif_path, str)
248 data = read_and_sub_file(ldif_path, subst_vars)
249 ldb.add_ldif(data,controls)
252 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
253 """Modify a ldb in the private dir.
255 :param ldb: LDB object.
256 :param ldif_path: LDIF file path.
257 :param subst_vars: Optional dictionary with substitution variables.
259 data = read_and_sub_file(ldif_path, subst_vars)
261 ldb.modify_ldif(data)
264 def setup_ldb(ldb, ldif_path, subst_vars):
265 """Import a LDIF a file into a LDB handle, optionally substituting variables.
267 :note: Either all LDIF data will be added or none (using transactions).
269 :param ldb: LDB file to import into.
270 :param ldif_path: Path to the LDIF file.
271 :param subst_vars: Dictionary with substitution variables.
273 assert ldb is not None
274 ldb.transaction_start()
275 try:
276 setup_add_ldif(ldb, ldif_path, subst_vars)
277 except:
278 ldb.transaction_cancel()
279 raise
280 ldb.transaction_commit()
283 def provision_paths_from_lp(lp, dnsdomain):
284 """Set the default paths for provisioning.
286 :param lp: Loadparm context.
287 :param dnsdomain: DNS Domain name
289 paths = ProvisionPaths()
290 paths.private_dir = lp.get("private dir")
291 paths.dns_keytab = "dns.keytab"
293 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
294 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
295 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
296 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
297 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
298 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
299 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
300 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
301 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
302 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
303 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
304 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
305 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
306 paths.phpldapadminconfig = os.path.join(paths.private_dir,
307 "phpldapadmin-config.php")
308 paths.ldapdir = os.path.join(paths.private_dir,
309 "ldap")
310 paths.slapdconf = os.path.join(paths.ldapdir,
311 "slapd.conf")
312 paths.slapdpid = os.path.join(paths.ldapdir,
313 "slapd.pid")
314 paths.modulesconf = os.path.join(paths.ldapdir,
315 "modules.conf")
316 paths.memberofconf = os.path.join(paths.ldapdir,
317 "memberof.conf")
318 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
319 "mmr_serverids.conf")
320 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
321 "mmr_syncrepl.conf")
322 paths.olcdir = os.path.join(paths.ldapdir,
323 "slapd.d")
324 paths.olcseedldif = os.path.join(paths.ldapdir,
325 "olc_seed.ldif")
326 paths.hklm = "hklm.ldb"
327 paths.hkcr = "hkcr.ldb"
328 paths.hkcu = "hkcu.ldb"
329 paths.hku = "hku.ldb"
330 paths.hkpd = "hkpd.ldb"
331 paths.hkpt = "hkpt.ldb"
333 paths.sysvol = lp.get("path", "sysvol")
335 paths.netlogon = lp.get("path", "netlogon")
337 paths.smbconf = lp.configfile
339 return paths
342 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
343 serverrole=None, rootdn=None, domaindn=None, configdn=None,
344 schemadn=None, serverdn=None, sitename=None):
345 """Guess configuration settings to use."""
347 if hostname is None:
348 hostname = socket.gethostname().split(".")[0]
350 netbiosname = lp.get("netbios name")
351 if netbiosname is None:
352 netbiosname = hostname
353 assert netbiosname is not None
354 netbiosname = netbiosname.upper()
355 if not valid_netbios_name(netbiosname):
356 raise InvalidNetbiosName(netbiosname)
358 if dnsdomain is None:
359 dnsdomain = lp.get("realm")
360 if dnsdomain is None or dnsdomain == "":
361 raise ProvisioningError("guess_names: 'realm' not specified in supplied smb.conf!")
363 dnsdomain = dnsdomain.lower()
365 if serverrole is None:
366 serverrole = lp.get("server role")
367 if serverrole is None:
368 raise ProvisioningError("guess_names: 'server role' not specified in supplied smb.conf!")
370 serverrole = serverrole.lower()
372 realm = dnsdomain.upper()
374 if lp.get("realm").upper() != realm:
375 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
377 if lp.get("server role").lower() != serverrole:
378 raise ProvisioningError("guess_names: server role '%s' in smb.conf must match chosen server role '%s'!", lp.get("server role").upper(), serverrole)
380 if serverrole == "domain controller":
381 if domain is None:
382 domain = lp.get("workgroup")
383 if domain is None:
384 raise ProvisioningError("guess_names: 'workgroup' not specified in supplied smb.conf!")
385 domain = domain.upper()
387 if lp.get("workgroup").upper() != domain:
388 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
390 if domaindn is None:
391 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
392 else:
393 domain = netbiosname
394 if domaindn is None:
395 domaindn = "DC=" + netbiosname
397 if not valid_netbios_name(domain):
398 raise InvalidNetbiosName(domain)
400 if hostname.upper() == realm:
401 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
402 if netbiosname == realm:
403 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
404 if domain == realm:
405 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
407 if rootdn is None:
408 rootdn = domaindn
410 if configdn is None:
411 configdn = "CN=Configuration," + rootdn
412 if schemadn is None:
413 schemadn = "CN=Schema," + configdn
415 if sitename is None:
416 sitename=DEFAULTSITE
418 names = ProvisionNames()
419 names.rootdn = rootdn
420 names.domaindn = domaindn
421 names.configdn = configdn
422 names.schemadn = schemadn
423 names.ldapmanagerdn = "CN=Manager," + rootdn
424 names.dnsdomain = dnsdomain
425 names.domain = domain
426 names.realm = realm
427 names.netbiosname = netbiosname
428 names.hostname = hostname
429 names.sitename = sitename
430 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
432 return names
435 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
436 targetdir, sid_generator,eadb):
437 """Create a new smb.conf file based on a couple of basic settings.
439 assert smbconf is not None
440 if hostname is None:
441 hostname = socket.gethostname().split(".")[0]
442 netbiosname = hostname.upper()
444 if serverrole is None:
445 serverrole = "standalone"
447 assert serverrole in ("domain controller", "member server", "standalone")
448 if serverrole == "domain controller":
449 smbconfsuffix = "dc"
450 elif serverrole == "member server":
451 smbconfsuffix = "member"
452 elif serverrole == "standalone":
453 smbconfsuffix = "standalone"
455 if sid_generator is None:
456 sid_generator = "internal"
458 assert domain is not None
459 domain = domain.upper()
461 assert realm is not None
462 realm = realm.upper()
464 default_lp = param.LoadParm()
465 #Load non-existant file
466 if os.path.exists(smbconf):
467 default_lp.load(smbconf)
468 if eadb:
469 if targetdir is not None:
470 privdir = os.path.join(targetdir, "private")
471 else:
472 privdir = default_lp.get("private dir")
473 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
474 else:
475 posixeadb_line = ""
477 if targetdir is not None:
478 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
479 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
481 default_lp.set("lock dir", os.path.abspath(targetdir))
482 else:
483 privatedir_line = ""
484 lockdir_line = ""
486 if sid_generator == "internal":
487 sid_generator_line = ""
488 else:
489 sid_generator_line = "sid generator = " + sid_generator
491 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
492 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
494 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
495 smbconf, {
496 "NETBIOS_NAME": netbiosname,
497 "DOMAIN": domain,
498 "REALM": realm,
499 "SERVERROLE": serverrole,
500 "NETLOGONPATH": netlogon,
501 "SYSVOLPATH": sysvol,
502 "SIDGENERATOR_LINE": sid_generator_line,
503 "PRIVATEDIR_LINE": privatedir_line,
504 "LOCKDIR_LINE": lockdir_line,
505 "POSIXEADB_LINE": posixeadb_line
509 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
510 users_gid, wheel_gid):
511 """setup reasonable name mappings for sam names to unix names.
513 :param samdb: SamDB object.
514 :param idmap: IDmap db object.
515 :param sid: The domain sid.
516 :param domaindn: The domain DN.
517 :param root_uid: uid of the UNIX root user.
518 :param nobody_uid: uid of the UNIX nobody user.
519 :param users_gid: gid of the UNIX users group.
520 :param wheel_gid: gid of the UNIX wheel group."""
522 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
523 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
525 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
526 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
528 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
529 provision_backend, names, schema,
530 serverrole,
531 erase=False):
532 """Setup the partitions for the SAM database.
534 Alternatively, provision() may call this, and then populate the database.
536 :note: This will wipe the Sam Database!
538 :note: This function always removes the local SAM LDB file. The erase
539 parameter controls whether to erase the existing data, which
540 may not be stored locally but in LDAP.
543 assert session_info is not None
545 # We use options=["modules:"] to stop the modules loading - we
546 # just want to wipe and re-initialise the database, not start it up
548 try:
549 os.unlink(samdb_path)
550 except OSError:
551 pass
553 samdb = Ldb(url=samdb_path, session_info=session_info,
554 lp=lp, options=["modules:"])
556 ldap_backend_line = "# No LDAP backend"
557 if provision_backend.type is not "ldb":
558 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
560 samdb.transaction_start()
561 try:
562 message("Setting up sam.ldb partitions and settings")
563 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
564 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
565 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
566 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
567 "LDAP_BACKEND_LINE": ldap_backend_line,
571 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
572 "BACKEND_TYPE": provision_backend.type,
573 "SERVER_ROLE": serverrole
576 message("Setting up sam.ldb rootDSE")
577 setup_samdb_rootdse(samdb, setup_path, names)
579 except:
580 samdb.transaction_cancel()
581 raise
583 samdb.transaction_commit()
586 def secretsdb_self_join(secretsdb, domain,
587 netbiosname, machinepass, domainsid=None,
588 realm=None, dnsdomain=None,
589 keytab_path=None,
590 key_version_number=1,
591 secure_channel_type=SEC_CHAN_WKSTA):
592 """Add domain join-specific bits to a secrets database.
594 :param secretsdb: Ldb Handle to the secrets database
595 :param machinepass: Machine password
597 attrs=["whenChanged",
598 "secret",
599 "priorSecret",
600 "priorChanged",
601 "krb5Keytab",
602 "privateKeytab"]
605 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
606 msg["secureChannelType"] = str(secure_channel_type)
607 msg["flatname"] = [domain]
608 msg["objectClass"] = ["top", "primaryDomain"]
609 if realm is not None:
610 if dnsdomain is None:
611 dnsdomain = realm.lower()
612 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
613 msg["realm"] = realm
614 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
615 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
616 msg["privateKeytab"] = ["secrets.keytab"];
619 msg["secret"] = [machinepass]
620 msg["samAccountName"] = ["%s$" % netbiosname]
621 msg["secureChannelType"] = [str(secure_channel_type)]
622 if domainsid is not None:
623 msg["objectSid"] = [ndr_pack(domainsid)]
625 res = secretsdb.search(base="cn=Primary Domains",
626 attrs=attrs,
627 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
628 scope=ldb.SCOPE_ONELEVEL)
630 for del_msg in res:
631 if del_msg.dn is not msg.dn:
632 secretsdb.delete(del_msg.dn)
634 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
636 if len(res) == 1:
637 msg["priorSecret"] = res[0]["secret"]
638 msg["priorWhenChanged"] = res[0]["whenChanged"]
640 if res["privateKeytab"] is not None:
641 msg["privateKeytab"] = res[0]["privateKeytab"]
643 if res["krb5Keytab"] is not None:
644 msg["krb5Keytab"] = res[0]["krb5Keytab"]
646 for el in msg:
647 el.set_flags(ldb.FLAG_MOD_REPLACE)
648 secretsdb.modify(msg)
649 else:
650 secretsdb.add(msg)
653 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
654 realm, dnsdomain,
655 dns_keytab_path, dnspass):
656 """Add DNS specific bits to a secrets database.
658 :param secretsdb: Ldb Handle to the secrets database
659 :param setup_path: Setup path function
660 :param machinepass: Machine password
662 try:
663 os.unlink(os.path.join(private_dir, dns_keytab_path))
664 except OSError:
665 pass
667 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
668 "REALM": realm,
669 "DNSDOMAIN": dnsdomain,
670 "DNS_KEYTAB": dns_keytab_path,
671 "DNSPASS_B64": b64encode(dnspass),
675 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
676 """Setup the secrets database.
678 :param path: Path to the secrets database.
679 :param setup_path: Get the path to a setup file.
680 :param session_info: Session info.
681 :param credentials: Credentials
682 :param lp: Loadparm context
683 :return: LDB handle for the created secrets database
685 if os.path.exists(path):
686 os.unlink(path)
687 secrets_ldb = Ldb(path, session_info=session_info,
688 lp=lp)
689 secrets_ldb.erase()
690 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
691 secrets_ldb = Ldb(path, session_info=session_info,
692 lp=lp)
693 secrets_ldb.transaction_start()
694 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
696 if backend_credentials is not None and backend_credentials.authentication_requested():
697 if backend_credentials.get_bind_dn() is not None:
698 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
699 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
700 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
702 else:
703 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
704 "LDAPADMINUSER": backend_credentials.get_username(),
705 "LDAPADMINREALM": backend_credentials.get_realm(),
706 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
709 return secrets_ldb
711 def setup_privileges(path, setup_path, session_info, lp):
712 """Setup the privileges database.
714 :param path: Path to the privileges database.
715 :param setup_path: Get the path to a setup file.
716 :param session_info: Session info.
717 :param credentials: Credentials
718 :param lp: Loadparm context
719 :return: LDB handle for the created secrets database
721 if os.path.exists(path):
722 os.unlink(path)
723 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
724 privilege_ldb.erase()
725 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
728 def setup_registry(path, setup_path, session_info, lp):
729 """Setup the registry.
731 :param path: Path to the registry database
732 :param setup_path: Function that returns the path to a setup.
733 :param session_info: Session information
734 :param credentials: Credentials
735 :param lp: Loadparm context
737 reg = registry.Registry()
738 hive = registry.open_ldb(path, session_info=session_info,
739 lp_ctx=lp)
740 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
741 provision_reg = setup_path("provision.reg")
742 assert os.path.exists(provision_reg)
743 reg.diff_apply(provision_reg)
746 def setup_idmapdb(path, setup_path, session_info, lp):
747 """Setup the idmap database.
749 :param path: path to the idmap database
750 :param setup_path: Function that returns a path to a setup file
751 :param session_info: Session information
752 :param credentials: Credentials
753 :param lp: Loadparm context
755 if os.path.exists(path):
756 os.unlink(path)
758 idmap_ldb = IDmapDB(path, session_info=session_info,
759 lp=lp)
761 idmap_ldb.erase()
762 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
763 return idmap_ldb
766 def setup_samdb_rootdse(samdb, setup_path, names):
767 """Setup the SamDB rootdse.
769 :param samdb: Sam Database handle
770 :param setup_path: Obtain setup path
772 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
773 "SCHEMADN": names.schemadn,
774 "NETBIOSNAME": names.netbiosname,
775 "DNSDOMAIN": names.dnsdomain,
776 "REALM": names.realm,
777 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
778 "DOMAINDN": names.domaindn,
779 "ROOTDN": names.rootdn,
780 "CONFIGDN": names.configdn,
781 "SERVERDN": names.serverdn,
785 def setup_self_join(samdb, names,
786 machinepass, dnspass,
787 domainsid, invocationid, setup_path,
788 policyguid, policyguid_dc, domainControllerFunctionality,
789 ntdsguid):
790 """Join a host to its own domain."""
791 assert isinstance(invocationid, str)
792 if ntdsguid is not None:
793 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
794 else:
795 ntdsguid_line = ""
796 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
797 "CONFIGDN": names.configdn,
798 "SCHEMADN": names.schemadn,
799 "DOMAINDN": names.domaindn,
800 "SERVERDN": names.serverdn,
801 "INVOCATIONID": invocationid,
802 "NETBIOSNAME": names.netbiosname,
803 "DEFAULTSITE": names.sitename,
804 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
805 "MACHINEPASS_B64": b64encode(machinepass),
806 "REALM": names.realm,
807 "DOMAIN": names.domain,
808 "DOMAINSID": str(domainsid),
809 "DNSDOMAIN": names.dnsdomain,
810 "SAMBA_VERSION_STRING": version,
811 "NTDSGUID": ntdsguid_line,
812 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
814 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
815 "POLICYGUID": policyguid,
816 "POLICYGUID_DC": policyguid_dc,
817 "DNSDOMAIN": names.dnsdomain,
818 "DOMAINSID": str(domainsid),
819 "DOMAINDN": names.domaindn})
821 # add the NTDSGUID based SPNs
822 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
823 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
824 expression="", scope=ldb.SCOPE_BASE)
825 assert isinstance(names.ntdsguid, str)
827 # Setup fSMORoleOwner entries to point at the newly created DC entry
828 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
829 "DOMAIN": names.domain,
830 "DNSDOMAIN": names.dnsdomain,
831 "DOMAINDN": names.domaindn,
832 "CONFIGDN": names.configdn,
833 "SCHEMADN": names.schemadn,
834 "DEFAULTSITE": names.sitename,
835 "SERVERDN": names.serverdn,
836 "NETBIOSNAME": names.netbiosname,
837 "NTDSGUID": names.ntdsguid,
838 "DNSPASS_B64": b64encode(dnspass),
842 def setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid):
843 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
844 "{" + policyguid + "}")
845 os.makedirs(policy_path, 0755)
846 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
847 "[General]\r\nVersion=65543")
848 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
849 os.makedirs(os.path.join(policy_path, "USER"), 0755)
851 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
852 "{" + policyguid_dc + "}")
853 os.makedirs(policy_path_dc, 0755)
854 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
855 "[General]\r\nVersion=2")
856 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
857 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
860 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
861 names, message,
862 domainsid, domainguid, policyguid, policyguid_dc,
863 fill, adminpass, krbtgtpass,
864 machinepass, invocationid, dnspass, ntdsguid,
865 serverrole, dom_for_fun_level=None,
866 schema=None):
867 """Setup a complete SAM Database.
869 :note: This will wipe the main SAM database file!
872 # ATTENTION: Do NOT change these default values without discussion with the
873 # team and/or release manager. They have a big impact on the whole program!
874 domainControllerFunctionality = DS_DC_FUNCTION_2008
876 if dom_for_fun_level is None:
877 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
878 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
879 message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
881 if dom_for_fun_level > domainControllerFunctionality:
882 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!")
884 domainFunctionality = dom_for_fun_level
885 forestFunctionality = dom_for_fun_level
887 # Also wipes the database
888 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
889 provision_backend=provision_backend, session_info=session_info,
890 names=names,
891 serverrole=serverrole, schema=schema)
893 if (schema == None):
894 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
896 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
897 samdb = Ldb(session_info=session_info,
898 credentials=provision_backend.credentials, lp=lp)
900 message("Pre-loading the Samba 4 and AD schema")
902 # Load the schema from the one we computed earlier
903 samdb.set_schema_from_ldb(schema.ldb)
905 # And now we can connect to the DB - the schema won't be loaded from the DB
906 samdb.connect(path)
908 if fill == FILL_DRS:
909 return samdb
911 samdb.transaction_start()
912 try:
913 # Set the domain functionality levels onto the database.
914 # Various module (the password_hash module in particular) need
915 # to know what level of AD we are emulating.
917 # These will be fixed into the database via the database
918 # modifictions below, but we need them set from the start.
919 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
920 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
921 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
923 samdb.set_domain_sid(str(domainsid))
924 samdb.set_invocation_id(invocationid)
926 message("Adding DomainDN: %s" % names.domaindn)
928 #impersonate domain admin
929 admin_session_info = admin_session(lp, str(domainsid))
930 samdb.set_session_info(admin_session_info)
931 if domainguid is not None:
932 domainguid_line = "objectGUID: %s\n-" % domainguid
933 else:
934 domainguid_line = ""
936 descr = b64encode(get_domain_descriptor(domainsid))
937 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
938 "DOMAINDN": names.domaindn,
939 "DOMAINGUID": domainguid_line,
940 "DESCRIPTOR": descr
944 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
945 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
946 "DOMAINSID": str(domainsid),
947 "SCHEMADN": names.schemadn,
948 "NETBIOSNAME": names.netbiosname,
949 "DEFAULTSITE": names.sitename,
950 "CONFIGDN": names.configdn,
951 "SERVERDN": names.serverdn,
952 "POLICYGUID": policyguid,
953 "DOMAINDN": names.domaindn,
954 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
955 "SAMBA_VERSION_STRING": version
958 message("Adding configuration container")
959 descr = b64encode(get_config_descriptor(domainsid))
960 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
961 "CONFIGDN": names.configdn,
962 "DESCRIPTOR": descr,
965 # The LDIF here was created when the Schema object was constructed
966 message("Setting up sam.ldb schema")
967 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
968 samdb.modify_ldif(schema.schema_dn_modify)
969 samdb.write_prefixes_from_schema()
970 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
971 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
972 {"SCHEMADN": names.schemadn})
974 message("Reopening sam.ldb with new schema");
975 samdb.transaction_commit()
976 samdb = Ldb(session_info=admin_session_info,
977 credentials=provision_backend.credentials, lp=lp)
978 samdb.connect(path)
979 samdb.transaction_start()
980 samdb.set_invocation_id(invocationid)
982 message("Setting up sam.ldb configuration data")
983 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
984 "CONFIGDN": names.configdn,
985 "NETBIOSNAME": names.netbiosname,
986 "DEFAULTSITE": names.sitename,
987 "DNSDOMAIN": names.dnsdomain,
988 "DOMAIN": names.domain,
989 "SCHEMADN": names.schemadn,
990 "DOMAINDN": names.domaindn,
991 "SERVERDN": names.serverdn,
992 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
995 message("Setting up display specifiers")
996 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
997 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
998 check_all_substituted(display_specifiers_ldif)
999 samdb.add_ldif(display_specifiers_ldif)
1001 message("Adding users container")
1002 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1003 "DOMAINDN": names.domaindn})
1004 message("Modifying users container")
1005 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1006 "DOMAINDN": names.domaindn})
1007 message("Adding computers container")
1008 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1009 "DOMAINDN": names.domaindn})
1010 message("Modifying computers container")
1011 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1012 "DOMAINDN": names.domaindn})
1013 message("Setting up sam.ldb data")
1014 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1015 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1016 "DOMAINDN": names.domaindn,
1017 "NETBIOSNAME": names.netbiosname,
1018 "DEFAULTSITE": names.sitename,
1019 "CONFIGDN": names.configdn,
1020 "SERVERDN": names.serverdn,
1021 "POLICYGUID_DC": policyguid_dc
1024 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1025 "DOMAINDN": names.domaindn})
1027 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1028 "CONFIGDN": names.configdn,
1029 "SCHEMADN": names.schemadn})
1030 if fill == FILL_FULL:
1031 message("Setting up sam.ldb users and groups")
1032 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1033 "DOMAINDN": names.domaindn,
1034 "DOMAINSID": str(domainsid),
1035 "CONFIGDN": names.configdn,
1036 "ADMINPASS_B64": b64encode(adminpass),
1037 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1040 message("Setting up self join")
1041 setup_self_join(samdb, names=names, invocationid=invocationid,
1042 dnspass=dnspass,
1043 machinepass=machinepass,
1044 domainsid=domainsid, policyguid=policyguid,
1045 policyguid_dc=policyguid_dc,
1046 setup_path=setup_path,
1047 domainControllerFunctionality=domainControllerFunctionality,
1048 ntdsguid=ntdsguid)
1050 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1051 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1052 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1053 assert isinstance(names.ntdsguid, str)
1055 except:
1056 samdb.transaction_cancel()
1057 raise
1059 samdb.transaction_commit()
1060 return samdb
1063 FILL_FULL = "FULL"
1064 FILL_NT4SYNC = "NT4SYNC"
1065 FILL_DRS = "DRS"
1066 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1067 POLICIES_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)(A;OICI;0x001301bf;;;PA)"
1069 def set_gpo_acl(path,acl,lp,domsid):
1070 setntacl(lp,path,acl,domsid)
1071 for root, dirs, files in os.walk(path, topdown=False):
1072 for name in files:
1073 setntacl(lp,os.path.join(root, name),acl,domsid)
1074 for name in dirs:
1075 setntacl(lp,os.path.join(root, name),acl,domsid)
1077 def setsysvolacl(samdb,names,netlogon,sysvol,gid,domainsid,lp):
1078 canchown = 1
1079 try:
1080 os.chown(sysvol,-1,gid)
1081 except:
1082 canchown = 0
1084 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1085 for root, dirs, files in os.walk(sysvol, topdown=False):
1086 for name in files:
1087 if canchown:
1088 os.chown(os.path.join(root, name),-1,gid)
1089 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1090 for name in dirs:
1091 if canchown:
1092 os.chown(os.path.join(root, name),-1,gid)
1093 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1095 # Set ACL for GPO
1096 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies")
1097 set_gpo_acl(policy_path,dsacl2fsacl(POLICIES_ACL,str(domainsid)),lp,str(domainsid))
1098 res = samdb.search(base="CN=Policies,CN=System,%s"%(names.domaindn),
1099 attrs=["cn","nTSecurityDescriptor"],
1100 expression="", scope=ldb.SCOPE_ONELEVEL)
1101 for policy in res:
1102 acl = ndr_unpack(security.descriptor,str(policy["nTSecurityDescriptor"])).as_sddl()
1103 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies",
1104 str(policy["cn"]))
1105 set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
1109 def provision(setup_dir, message, session_info,
1110 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1111 realm=None,
1112 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1113 serverdn=None,
1114 domain=None, hostname=None, hostip=None, hostip6=None,
1115 domainsid=None, adminpass=None, ldapadminpass=None,
1116 krbtgtpass=None, domainguid=None,
1117 policyguid=None, policyguid_dc=None, invocationid=None,
1118 machinepass=None, ntdsguid=None,
1119 dnspass=None, root=None, nobody=None, users=None,
1120 wheel=None, backup=None, aci=None, serverrole=None,
1121 dom_for_fun_level=None,
1122 ldap_backend_extra_port=None, backend_type=None,
1123 sitename=None,
1124 ol_mmr_urls=None, ol_olc=None,
1125 setup_ds_path=None, slapd_path=None, nosync=False,
1126 ldap_dryrun_mode=False,useeadb=False):
1127 """Provision samba4
1129 :note: caution, this wipes all existing data!
1132 def setup_path(file):
1133 return os.path.join(setup_dir, file)
1135 if domainsid is None:
1136 domainsid = security.random_sid()
1137 else:
1138 domainsid = security.dom_sid(domainsid)
1140 # create/adapt the group policy GUIDs
1141 if policyguid is None:
1142 policyguid = str(uuid.uuid4())
1143 policyguid = policyguid.upper()
1144 if policyguid_dc is None:
1145 policyguid_dc = str(uuid.uuid4())
1146 policyguid_dc = policyguid_dc.upper()
1148 if adminpass is None:
1149 adminpass = glue.generate_random_password(12, 32)
1150 if krbtgtpass is None:
1151 krbtgtpass = glue.generate_random_password(128, 255)
1152 if machinepass is None:
1153 machinepass = glue.generate_random_password(128, 255)
1154 if dnspass is None:
1155 dnspass = glue.generate_random_password(128, 255)
1156 if ldapadminpass is None:
1157 #Make a new, random password between Samba and it's LDAP server
1158 ldapadminpass=glue.generate_random_password(128, 255)
1160 if backend_type is None:
1161 backend_type = "ldb"
1163 sid_generator = "internal"
1164 if backend_type == "fedora-ds":
1165 sid_generator = "backend"
1167 root_uid = findnss_uid([root or "root"])
1168 nobody_uid = findnss_uid([nobody or "nobody"])
1169 users_gid = findnss_gid([users or "users"])
1170 if wheel is None:
1171 wheel_gid = findnss_gid(["wheel", "adm"])
1172 else:
1173 wheel_gid = findnss_gid([wheel])
1174 try:
1175 bind_gid = findnss_gid(["bind", "named"])
1176 except KeyError:
1177 bind_gid = None
1179 if targetdir is not None:
1180 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1181 os.makedirs(os.path.join(targetdir, "etc"))
1182 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1183 elif smbconf is None:
1184 smbconf = param.default_path()
1186 # only install a new smb.conf if there isn't one there already
1187 if os.path.exists(smbconf):
1188 # if Samba Team members can't figure out the weird errors
1189 # loading an empty smb.conf gives, then we need to be smarter.
1190 # Pretend it just didn't exist --abartlet
1191 data = open(smbconf, 'r').read()
1192 data = data.lstrip()
1193 if data is None or data == "":
1194 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1195 targetdir, sid_generator, useeadb)
1196 else:
1197 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1198 targetdir, sid_generator, useeadb)
1200 lp = param.LoadParm()
1201 lp.load(smbconf)
1203 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1204 dnsdomain=realm, serverrole=serverrole,
1205 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1206 serverdn=serverdn, sitename=sitename)
1208 paths = provision_paths_from_lp(lp, names.dnsdomain)
1210 paths.bind_gid = bind_gid
1212 if hostip is None:
1213 try:
1214 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP):
1215 if hostip is None:
1216 hostip = ip[-1][0]
1217 if hostip.startswith('127.0.0.') and (not ip[-1][0].startswith('127.0.0.')):
1218 hostip = ip[-1][0]
1219 except socket.gaierror, (socket.EAI_NODATA, msg):
1220 hostip = None
1222 if hostip6 is None:
1223 try:
1224 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1225 if hostip6 is None:
1226 hostip6 = ip[-1][0]
1227 if hostip6 == '::1' and ip[-1][0] != '::1':
1228 hostip6 = ip[-1][0]
1229 except socket.gaierror, (socket.EAI_NODATA, msg):
1230 hostip6 = None
1232 if serverrole is None:
1233 serverrole = lp.get("server role")
1235 assert serverrole in ("domain controller", "member server", "standalone")
1236 if invocationid is None:
1237 invocationid = str(uuid.uuid4())
1239 if not os.path.exists(paths.private_dir):
1240 os.mkdir(paths.private_dir)
1241 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1242 os.mkdir(os.path.join(paths.private_dir,"tls"))
1244 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1246 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1248 if backend_type == "ldb":
1249 provision_backend = LDBBackend(backend_type,
1250 paths=paths, setup_path=setup_path,
1251 lp=lp, credentials=credentials,
1252 names=names,
1253 message=message)
1254 elif backend_type == "existing":
1255 provision_backend = ExistingBackend(backend_type,
1256 paths=paths, setup_path=setup_path,
1257 lp=lp, credentials=credentials,
1258 names=names,
1259 message=message)
1260 elif backend_type == "fedora-ds":
1261 provision_backend = FDSBackend(backend_type,
1262 paths=paths, setup_path=setup_path,
1263 lp=lp, credentials=credentials,
1264 names=names,
1265 message=message,
1266 domainsid=domainsid,
1267 schema=schema,
1268 hostname=hostname,
1269 ldapadminpass=ldapadminpass,
1270 slapd_path=slapd_path,
1271 ldap_backend_extra_port=ldap_backend_extra_port,
1272 ldap_dryrun_mode=ldap_dryrun_mode,
1273 root=root,
1274 setup_ds_path=setup_ds_path)
1275 elif backend_type == "openldap":
1276 provision_backend = OpenLDAPBackend(backend_type,
1277 paths=paths, setup_path=setup_path,
1278 lp=lp, credentials=credentials,
1279 names=names,
1280 message=message,
1281 domainsid=domainsid,
1282 schema=schema,
1283 hostname=hostname,
1284 ldapadminpass=ldapadminpass,
1285 slapd_path=slapd_path,
1286 ldap_backend_extra_port=ldap_backend_extra_port,
1287 ldap_dryrun_mode=ldap_dryrun_mode,
1288 ol_mmr_urls=ol_mmr_urls,
1289 nosync=nosync)
1290 else:
1291 raise ProvisioningError("Unknown LDAP backend type selected")
1293 provision_backend.init()
1294 provision_backend.start()
1296 # only install a new shares config db if there is none
1297 if not os.path.exists(paths.shareconf):
1298 message("Setting up share.ldb")
1299 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1300 lp=lp)
1301 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1304 message("Setting up secrets.ldb")
1305 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1306 session_info=session_info,
1307 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1309 message("Setting up the registry")
1310 setup_registry(paths.hklm, setup_path, session_info,
1311 lp=lp)
1313 message("Setting up the privileges database")
1314 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1316 message("Setting up idmap db")
1317 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1318 lp=lp)
1320 message("Setting up SAM db")
1321 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1322 provision_backend, lp, names,
1323 message,
1324 domainsid=domainsid,
1325 schema=schema, domainguid=domainguid,
1326 policyguid=policyguid, policyguid_dc=policyguid_dc,
1327 fill=samdb_fill,
1328 adminpass=adminpass, krbtgtpass=krbtgtpass,
1329 invocationid=invocationid,
1330 machinepass=machinepass, dnspass=dnspass,
1331 ntdsguid=ntdsguid, serverrole=serverrole,
1332 dom_for_fun_level=dom_for_fun_level)
1334 if serverrole == "domain controller":
1335 if paths.netlogon is None:
1336 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1337 message("Please either remove %s or see the template at %s" %
1338 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1339 assert(paths.netlogon is not None)
1341 if paths.sysvol is None:
1342 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1343 message("Please either remove %s or see the template at %s" %
1344 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1345 assert(paths.sysvol is not None)
1348 if not os.path.isdir(paths.netlogon):
1349 os.makedirs(paths.netlogon, 0755)
1351 if samdb_fill == FILL_FULL:
1352 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1353 root_uid=root_uid, nobody_uid=nobody_uid,
1354 users_gid=users_gid, wheel_gid=wheel_gid)
1356 if serverrole == "domain controller":
1357 # Set up group policies (domain policy and domain controller policy)
1358 setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid)
1359 setsysvolacl(samdb,names,paths.netlogon,paths.sysvol,wheel_gid,domainsid,lp)
1361 message("Setting up sam.ldb rootDSE marking as synchronized")
1362 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1364 secretsdb_self_join(secrets_ldb, domain=names.domain,
1365 realm=names.realm,
1366 dnsdomain=names.dnsdomain,
1367 netbiosname=names.netbiosname,
1368 domainsid=domainsid,
1369 machinepass=machinepass,
1370 secure_channel_type=SEC_CHAN_BDC)
1372 if serverrole == "domain controller":
1373 secretsdb_setup_dns(secrets_ldb, setup_path,
1374 paths.private_dir,
1375 realm=names.realm, dnsdomain=names.dnsdomain,
1376 dns_keytab_path=paths.dns_keytab,
1377 dnspass=dnspass)
1379 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1380 assert isinstance(domainguid, str)
1382 # Only make a zone file on the first DC, it should be replicated
1383 # with DNS replication
1384 create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain=names.dnsdomain,
1385 hostip=hostip,
1386 hostip6=hostip6, hostname=names.hostname,
1387 realm=names.realm,
1388 domainguid=domainguid, ntdsguid=names.ntdsguid)
1390 create_named_conf(paths, setup_path, realm=names.realm,
1391 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1393 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1394 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1395 keytab_name=paths.dns_keytab)
1396 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1397 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1399 create_krb5_conf(paths.krb5conf, setup_path,
1400 dnsdomain=names.dnsdomain, hostname=names.hostname,
1401 realm=names.realm)
1402 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1404 if serverrole == "domain controller":
1405 create_dns_update_list(lp, message, paths, setup_path)
1407 provision_backend.post_setup()
1408 provision_backend.shutdown()
1410 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1411 ldapi_url)
1413 #Now commit the secrets.ldb to disk
1414 secrets_ldb.transaction_commit()
1416 # the commit creates the dns.keytab, now chown it
1417 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1418 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1419 try:
1420 os.chmod(dns_keytab_path, 0640)
1421 os.chown(dns_keytab_path, -1, paths.bind_gid)
1422 except OSError:
1423 message("Failed to chown %s to bind gid %u" % (dns_keytab_path, paths.bind_gid))
1426 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1428 message("Once the above files are installed, your Samba4 server will be ready to use")
1429 message("Server Role: %s" % serverrole)
1430 message("Hostname: %s" % names.hostname)
1431 message("NetBIOS Domain: %s" % names.domain)
1432 message("DNS Domain: %s" % names.dnsdomain)
1433 message("DOMAIN SID: %s" % str(domainsid))
1434 if samdb_fill == FILL_FULL:
1435 message("Admin password: %s" % adminpass)
1436 if provision_backend.type is not "ldb":
1437 if provision_backend.credentials.get_bind_dn() is not None:
1438 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1439 else:
1440 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1442 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1444 if provision_backend.slapd_command_escaped is not None:
1445 # now display slapd_command_file.txt to show how slapd must be started next time
1446 message("Use later the following commandline to start slapd, then Samba:")
1447 message(provision_backend.slapd_command_escaped)
1448 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1451 result = ProvisionResult()
1452 result.domaindn = domaindn
1453 result.paths = paths
1454 result.lp = lp
1455 result.samdb = samdb
1456 return result
1460 def provision_become_dc(setup_dir=None,
1461 smbconf=None, targetdir=None, realm=None,
1462 rootdn=None, domaindn=None, schemadn=None,
1463 configdn=None, serverdn=None,
1464 domain=None, hostname=None, domainsid=None,
1465 adminpass=None, krbtgtpass=None, domainguid=None,
1466 policyguid=None, policyguid_dc=None, invocationid=None,
1467 machinepass=None,
1468 dnspass=None, root=None, nobody=None, users=None,
1469 wheel=None, backup=None, serverrole=None,
1470 ldap_backend=None, ldap_backend_type=None,
1471 sitename=None, debuglevel=1):
1473 def message(text):
1474 """print a message if quiet is not set."""
1475 print text
1477 glue.set_debug_level(debuglevel)
1479 return provision(setup_dir, message, system_session(), None,
1480 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1481 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1482 configdn=configdn, serverdn=serverdn, domain=domain,
1483 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1484 machinepass=machinepass, serverrole="domain controller",
1485 sitename=sitename)
1488 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1489 """Create a PHP LDAP admin configuration file.
1491 :param path: Path to write the configuration to.
1492 :param setup_path: Function to generate setup paths.
1494 setup_file(setup_path("phpldapadmin-config.php"), path,
1495 {"S4_LDAPI_URI": ldapi_uri})
1498 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1499 hostip, hostip6, hostname, realm, domainguid,
1500 ntdsguid):
1501 """Write out a DNS zone file, from the info in the current database.
1503 :param paths: paths object
1504 :param setup_path: Setup path function.
1505 :param dnsdomain: DNS Domain name
1506 :param domaindn: DN of the Domain
1507 :param hostip: Local IPv4 IP
1508 :param hostip6: Local IPv6 IP
1509 :param hostname: Local hostname
1510 :param realm: Realm name
1511 :param domainguid: GUID of the domain.
1512 :param ntdsguid: GUID of the hosts nTDSDSA record.
1514 assert isinstance(domainguid, str)
1516 if hostip6 is not None:
1517 hostip6_base_line = " IN AAAA " + hostip6
1518 hostip6_host_line = hostname + " IN AAAA " + hostip6
1519 else:
1520 hostip6_base_line = ""
1521 hostip6_host_line = ""
1523 if hostip is not None:
1524 hostip_base_line = " IN A " + hostip
1525 hostip_host_line = hostname + " IN A " + hostip
1526 else:
1527 hostip_base_line = ""
1528 hostip_host_line = ""
1530 dns_dir = os.path.dirname(paths.dns)
1532 try:
1533 shutil.rmtree(dns_dir, True)
1534 except OSError:
1535 pass
1537 os.mkdir(dns_dir, 0775)
1539 # we need to freeze the zone while we update the contents
1540 if targetdir is None:
1541 rndc = ' '.join(lp.get("rndc command"))
1542 os.system(rndc + " freeze " + lp.get("realm"))
1544 setup_file(setup_path("provision.zone"), paths.dns, {
1545 "HOSTNAME": hostname,
1546 "DNSDOMAIN": dnsdomain,
1547 "REALM": realm,
1548 "HOSTIP_BASE_LINE": hostip_base_line,
1549 "HOSTIP_HOST_LINE": hostip_host_line,
1550 "DOMAINGUID": domainguid,
1551 "DATESTRING": time.strftime("%Y%m%d%H"),
1552 "DEFAULTSITE": DEFAULTSITE,
1553 "NTDSGUID": ntdsguid,
1554 "HOSTIP6_BASE_LINE": hostip6_base_line,
1555 "HOSTIP6_HOST_LINE": hostip6_host_line,
1558 # note that we use no variable substitution on this file
1559 # the substitution is done at runtime by samba_dnsupdate
1560 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1562 if paths.bind_gid is not None:
1563 try:
1564 os.chown(dns_dir, -1, paths.bind_gid)
1565 os.chown(paths.dns, -1, paths.bind_gid)
1566 # chmod needed to cope with umask
1567 os.chmod(dns_dir, 0775)
1568 os.chmod(paths.dns, 0664)
1569 except OSError:
1570 message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1572 if targetdir is None:
1573 os.system(rndc + " unfreeze " + lp.get("realm"))
1576 def create_dns_update_list(lp, message, paths, setup_path):
1577 """Write out a dns_update_list file"""
1578 # note that we use no variable substitution on this file
1579 # the substitution is done at runtime by samba_dnsupdate
1580 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1583 def create_named_conf(paths, setup_path, realm, dnsdomain,
1584 private_dir):
1585 """Write out a file containing zone statements suitable for inclusion in a
1586 named.conf file (including GSS-TSIG configuration).
1588 :param paths: all paths
1589 :param setup_path: Setup path function.
1590 :param realm: Realm name
1591 :param dnsdomain: DNS Domain name
1592 :param private_dir: Path to private directory
1593 :param keytab_name: File name of DNS keytab file
1596 setup_file(setup_path("named.conf"), paths.namedconf, {
1597 "DNSDOMAIN": dnsdomain,
1598 "REALM": realm,
1599 "ZONE_FILE": paths.dns,
1600 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1601 "NAMED_CONF": paths.namedconf,
1602 "NAMED_CONF_UPDATE": paths.namedconf_update
1605 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1607 def create_named_txt(path, setup_path, realm, dnsdomain,
1608 private_dir, keytab_name):
1609 """Write out a file containing zone statements suitable for inclusion in a
1610 named.conf file (including GSS-TSIG configuration).
1612 :param path: Path of the new named.conf file.
1613 :param setup_path: Setup path function.
1614 :param realm: Realm name
1615 :param dnsdomain: DNS Domain name
1616 :param private_dir: Path to private directory
1617 :param keytab_name: File name of DNS keytab file
1620 setup_file(setup_path("named.txt"), path, {
1621 "DNSDOMAIN": dnsdomain,
1622 "REALM": realm,
1623 "DNS_KEYTAB": keytab_name,
1624 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1625 "PRIVATE_DIR": private_dir
1628 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1629 """Write out a file containing zone statements suitable for inclusion in a
1630 named.conf file (including GSS-TSIG configuration).
1632 :param path: Path of the new named.conf file.
1633 :param setup_path: Setup path function.
1634 :param dnsdomain: DNS Domain name
1635 :param hostname: Local hostname
1636 :param realm: Realm name
1639 setup_file(setup_path("krb5.conf"), path, {
1640 "DNSDOMAIN": dnsdomain,
1641 "HOSTNAME": hostname,
1642 "REALM": realm,