s3-lsa: Fix static list of luids in our privileges implementation.
[Samba/ekacnet.git] / source4 / scripting / python / samba / provision.py
blob089d4e3e010316b5a3aa148c972cabc2ed23c832
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
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 import samba
44 from samba import version, Ldb, substitute_var, valid_netbios_name
45 from samba import check_all_substituted, read_and_sub_file, setup_file
46 from samba.dsdb import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008
47 from samba.dcerpc import security
48 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
49 from samba.idmap import IDmapDB
50 from samba.ntacls import setntacl, dsacl2fsacl
51 from samba.ndr import ndr_pack,ndr_unpack
52 from samba.schema import Schema
53 from samba.samdb import SamDB
54 from ms_display_specifiers import read_ms_ldif
55 from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
57 __docformat__ = "restructuredText"
59 def find_setup_dir():
60 """Find the setup directory used by provision."""
61 dirname = os.path.dirname(__file__)
62 if "/site-packages/" in dirname:
63 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
64 for suffix in ["share/setup", "share/samba/setup", "setup"]:
65 ret = os.path.join(prefix, suffix)
66 if os.path.isdir(ret):
67 return ret
68 # In source tree
69 ret = os.path.join(dirname, "../../../setup")
70 if os.path.isdir(ret):
71 return ret
72 raise Exception("Unable to find setup directory.")
74 # descriptors of the naming contexts
75 # hard coded at this point, but will probably be changed when
76 # we enable different fsmo roles
78 def get_config_descriptor(domain_sid):
79 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
82 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
85 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
86 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
87 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
88 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
89 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
90 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
91 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
92 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
93 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
94 sec = security.descriptor.from_sddl(sddl, domain_sid)
95 return ndr_pack(sec)
97 def get_domain_descriptor(domain_sid):
98 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
99 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
100 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
101 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
102 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
103 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
104 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
105 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
106 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
107 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
108 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
109 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
110 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
111 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
112 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
113 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
114 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
119 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
120 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
121 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
122 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
123 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
125 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
126 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
127 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
128 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
129 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
133 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
134 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
135 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
136 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
137 "(A;;RPRC;;;RU)" \
138 "(A;CI;LC;;;RU)" \
139 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
140 "(A;;RP;;;WD)" \
141 "(A;;RPLCLORC;;;ED)" \
142 "(A;;RPLCLORC;;;AU)" \
143 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
144 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
146 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
147 sec = security.descriptor.from_sddl(sddl, domain_sid)
148 return ndr_pack(sec)
150 DEFAULTSITE = "Default-First-Site-Name"
152 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
172 class ProvisionNames(object):
174 def __init__(self):
175 self.rootdn = None
176 self.domaindn = None
177 self.configdn = None
178 self.schemadn = None
179 self.ldapmanagerdn = None
180 self.dnsdomain = None
181 self.realm = None
182 self.netbiosname = None
183 self.domain = None
184 self.hostname = None
185 self.sitename = None
186 self.smbconf = None
189 class ProvisionResult(object):
191 def __init__(self):
192 self.paths = None
193 self.domaindn = None
194 self.lp = None
195 self.samdb = None
198 def check_install(lp, session_info, credentials):
199 """Check whether the current install seems ok.
201 :param lp: Loadparm context
202 :param session_info: Session information
203 :param credentials: Credentials
205 if lp.get("realm") == "":
206 raise Exception("Realm empty")
207 samdb = Ldb(lp.get("sam database"), session_info=session_info,
208 credentials=credentials, lp=lp)
209 if len(samdb.search("(cn=Administrator)")) != 1:
210 raise ProvisioningError("No administrator account found")
213 def findnss(nssfn, names):
214 """Find a user or group from a list of possibilities.
216 :param nssfn: NSS Function to try (should raise KeyError if not found)
217 :param names: Names to check.
218 :return: Value return by first names list.
220 for name in names:
221 try:
222 return nssfn(name)
223 except KeyError:
224 pass
225 raise KeyError("Unable to find user/group in %r" % names)
228 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
229 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
232 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
233 """Setup a ldb in the private dir.
235 :param ldb: LDB file to import data into
236 :param ldif_path: Path of the LDIF file to load
237 :param subst_vars: Optional variables to subsitute in LDIF.
238 :param nocontrols: Optional list of controls, can be None for no controls
240 assert isinstance(ldif_path, str)
241 data = read_and_sub_file(ldif_path, subst_vars)
242 ldb.add_ldif(data, controls)
245 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
246 """Modify a ldb in the private dir.
248 :param ldb: LDB object.
249 :param ldif_path: LDIF file path.
250 :param subst_vars: Optional dictionary with substitution variables.
252 data = read_and_sub_file(ldif_path, subst_vars)
253 ldb.modify_ldif(data)
256 def setup_ldb(ldb, ldif_path, subst_vars):
257 """Import a LDIF a file into a LDB handle, optionally substituting variables.
259 :note: Either all LDIF data will be added or none (using transactions).
261 :param ldb: LDB file to import into.
262 :param ldif_path: Path to the LDIF file.
263 :param subst_vars: Dictionary with substitution variables.
265 assert ldb is not None
266 ldb.transaction_start()
267 try:
268 setup_add_ldif(ldb, ldif_path, subst_vars)
269 except:
270 ldb.transaction_cancel()
271 raise
272 else:
273 ldb.transaction_commit()
276 def provision_paths_from_lp(lp, dnsdomain):
277 """Set the default paths for provisioning.
279 :param lp: Loadparm context.
280 :param dnsdomain: DNS Domain name
282 paths = ProvisionPaths()
283 paths.private_dir = lp.get("private dir")
285 # This is stored without path prefix for the "privateKeytab" attribute in
286 # "secrets_dns.ldif".
287 paths.dns_keytab = "dns.keytab"
289 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
290 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
291 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
292 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
293 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
294 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
295 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
296 paths.spn_update_list = os.path.join(paths.private_dir, "spn_update_list")
297 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
298 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
299 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
300 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
301 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
302 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
303 paths.phpldapadminconfig = os.path.join(paths.private_dir,
304 "phpldapadmin-config.php")
305 paths.hklm = "hklm.ldb"
306 paths.hkcr = "hkcr.ldb"
307 paths.hkcu = "hkcu.ldb"
308 paths.hku = "hku.ldb"
309 paths.hkpd = "hkpd.ldb"
310 paths.hkpt = "hkpt.ldb"
311 paths.sysvol = lp.get("path", "sysvol")
312 paths.netlogon = lp.get("path", "netlogon")
313 paths.smbconf = lp.configfile
314 return paths
317 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
318 serverrole=None, rootdn=None, domaindn=None, configdn=None,
319 schemadn=None, serverdn=None, sitename=None):
320 """Guess configuration settings to use."""
322 if hostname is None:
323 hostname = socket.gethostname().split(".")[0]
325 netbiosname = lp.get("netbios name")
326 if netbiosname is None:
327 netbiosname = hostname
328 assert netbiosname is not None
329 netbiosname = netbiosname.upper()
330 if not valid_netbios_name(netbiosname):
331 raise InvalidNetbiosName(netbiosname)
333 if dnsdomain is None:
334 dnsdomain = lp.get("realm")
335 if dnsdomain is None or dnsdomain == "":
336 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
338 dnsdomain = dnsdomain.lower()
340 if serverrole is None:
341 serverrole = lp.get("server role")
342 if serverrole is None:
343 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
345 serverrole = serverrole.lower()
347 realm = dnsdomain.upper()
349 if lp.get("realm") == "":
350 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
352 if lp.get("realm").upper() != realm:
353 raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
355 if lp.get("server role").lower() != serverrole:
356 raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'! Please remove the smb.conf file and let provision generate it" % (lp.get("server role").upper(), serverrole, lp.configfile))
358 if serverrole == "domain controller":
359 if domain is None:
360 # This will, for better or worse, default to 'WORKGROUP'
361 domain = lp.get("workgroup")
362 domain = domain.upper()
364 if lp.get("workgroup").upper() != domain:
365 raise ProvisioningError("guess_names: Workgroup '%s' in %s must match chosen domain '%s'! Please remove the %s file and let provision generate it" % (lp.get("workgroup").upper(), domain, lp.configfile))
367 if domaindn is None:
368 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
369 else:
370 domain = netbiosname
371 if domaindn is None:
372 domaindn = "DC=" + netbiosname
374 if not valid_netbios_name(domain):
375 raise InvalidNetbiosName(domain)
377 if hostname.upper() == realm:
378 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
379 if netbiosname == realm:
380 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
381 if domain == realm:
382 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
384 if rootdn is None:
385 rootdn = domaindn
387 if configdn is None:
388 configdn = "CN=Configuration," + rootdn
389 if schemadn is None:
390 schemadn = "CN=Schema," + configdn
392 if sitename is None:
393 sitename=DEFAULTSITE
395 names = ProvisionNames()
396 names.rootdn = rootdn
397 names.domaindn = domaindn
398 names.configdn = configdn
399 names.schemadn = schemadn
400 names.ldapmanagerdn = "CN=Manager," + rootdn
401 names.dnsdomain = dnsdomain
402 names.domain = domain
403 names.realm = realm
404 names.netbiosname = netbiosname
405 names.hostname = hostname
406 names.sitename = sitename
407 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
409 return names
412 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
413 targetdir, sid_generator,eadb):
414 """Create a new smb.conf file based on a couple of basic settings.
416 assert smbconf is not None
417 if hostname is None:
418 hostname = socket.gethostname().split(".")[0]
419 netbiosname = hostname.upper()
421 if serverrole is None:
422 serverrole = "standalone"
424 assert serverrole in ("domain controller", "member server", "standalone")
425 if serverrole == "domain controller":
426 smbconfsuffix = "dc"
427 elif serverrole == "member server":
428 smbconfsuffix = "member"
429 elif serverrole == "standalone":
430 smbconfsuffix = "standalone"
432 if sid_generator is None:
433 sid_generator = "internal"
435 assert domain is not None
436 domain = domain.upper()
438 assert realm is not None
439 realm = realm.upper()
441 default_lp = param.LoadParm()
442 #Load non-existant file
443 if os.path.exists(smbconf):
444 default_lp.load(smbconf)
445 if eadb:
446 if targetdir is not None:
447 privdir = os.path.join(targetdir, "private")
448 else:
449 privdir = default_lp.get("private dir")
450 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
451 else:
452 posixeadb_line = ""
454 if targetdir is not None:
455 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
456 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
458 default_lp.set("lock dir", os.path.abspath(targetdir))
459 else:
460 privatedir_line = ""
461 lockdir_line = ""
463 if sid_generator == "internal":
464 sid_generator_line = ""
465 else:
466 sid_generator_line = "sid generator = " + sid_generator
468 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
469 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
471 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
472 smbconf, {
473 "NETBIOS_NAME": netbiosname,
474 "DOMAIN": domain,
475 "REALM": realm,
476 "SERVERROLE": serverrole,
477 "NETLOGONPATH": netlogon,
478 "SYSVOLPATH": sysvol,
479 "SIDGENERATOR_LINE": sid_generator_line,
480 "PRIVATEDIR_LINE": privatedir_line,
481 "LOCKDIR_LINE": lockdir_line,
482 "POSIXEADB_LINE": posixeadb_line
486 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
487 users_gid, wheel_gid):
488 """setup reasonable name mappings for sam names to unix names.
490 :param samdb: SamDB object.
491 :param idmap: IDmap db object.
492 :param sid: The domain sid.
493 :param domaindn: The domain DN.
494 :param root_uid: uid of the UNIX root user.
495 :param nobody_uid: uid of the UNIX nobody user.
496 :param users_gid: gid of the UNIX users group.
497 :param wheel_gid: gid of the UNIX wheel group."""
498 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
499 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
501 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
502 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
505 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
506 provision_backend, names, schema,
507 serverrole,
508 erase=False):
509 """Setup the partitions for the SAM database.
511 Alternatively, provision() may call this, and then populate the database.
513 :note: This will wipe the Sam Database!
515 :note: This function always removes the local SAM LDB file. The erase
516 parameter controls whether to erase the existing data, which
517 may not be stored locally but in LDAP.
520 assert session_info is not None
522 # We use options=["modules:"] to stop the modules loading - we
523 # just want to wipe and re-initialise the database, not start it up
525 try:
526 os.unlink(samdb_path)
527 except OSError:
528 pass
530 samdb = Ldb(url=samdb_path, session_info=session_info,
531 lp=lp, options=["modules:"])
533 ldap_backend_line = "# No LDAP backend"
534 if provision_backend.type is not "ldb":
535 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
537 samdb.transaction_start()
538 try:
539 message("Setting up sam.ldb partitions and settings")
540 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
541 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
542 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
543 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
544 "LDAP_BACKEND_LINE": ldap_backend_line,
548 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
549 "BACKEND_TYPE": provision_backend.type,
550 "SERVER_ROLE": serverrole
553 message("Setting up sam.ldb rootDSE")
554 setup_samdb_rootdse(samdb, setup_path, names)
555 except:
556 samdb.transaction_cancel()
557 raise
558 else:
559 samdb.transaction_commit()
562 def secretsdb_self_join(secretsdb, domain,
563 netbiosname, machinepass, domainsid=None,
564 realm=None, dnsdomain=None,
565 keytab_path=None,
566 key_version_number=1,
567 secure_channel_type=SEC_CHAN_WKSTA):
568 """Add domain join-specific bits to a secrets database.
570 :param secretsdb: Ldb Handle to the secrets database
571 :param machinepass: Machine password
573 attrs=["whenChanged",
574 "secret",
575 "priorSecret",
576 "priorChanged",
577 "krb5Keytab",
578 "privateKeytab"]
581 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
582 msg["secureChannelType"] = str(secure_channel_type)
583 msg["flatname"] = [domain]
584 msg["objectClass"] = ["top", "primaryDomain"]
585 if realm is not None:
586 if dnsdomain is None:
587 dnsdomain = realm.lower()
588 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
589 msg["realm"] = realm
590 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
591 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
592 msg["privateKeytab"] = ["secrets.keytab"]
595 msg["secret"] = [machinepass]
596 msg["samAccountName"] = ["%s$" % netbiosname]
597 msg["secureChannelType"] = [str(secure_channel_type)]
598 if domainsid is not None:
599 msg["objectSid"] = [ndr_pack(domainsid)]
601 res = secretsdb.search(base="cn=Primary Domains",
602 attrs=attrs,
603 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
604 scope=ldb.SCOPE_ONELEVEL)
606 for del_msg in res:
607 if del_msg.dn is not msg.dn:
608 secretsdb.delete(del_msg.dn)
610 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
612 if len(res) == 1:
613 msg["priorSecret"] = res[0]["secret"]
614 msg["priorWhenChanged"] = res[0]["whenChanged"]
616 if res["privateKeytab"] is not None:
617 msg["privateKeytab"] = res[0]["privateKeytab"]
619 if res["krb5Keytab"] is not None:
620 msg["krb5Keytab"] = res[0]["krb5Keytab"]
622 for el in msg:
623 el.set_flags(ldb.FLAG_MOD_REPLACE)
624 secretsdb.modify(msg)
625 else:
626 secretsdb.add(msg)
629 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
630 realm, dnsdomain,
631 dns_keytab_path, dnspass):
632 """Add DNS specific bits to a secrets database.
634 :param secretsdb: Ldb Handle to the secrets database
635 :param setup_path: Setup path function
636 :param machinepass: Machine password
638 try:
639 os.unlink(os.path.join(private_dir, dns_keytab_path))
640 except OSError:
641 pass
643 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
644 "REALM": realm,
645 "DNSDOMAIN": dnsdomain,
646 "DNS_KEYTAB": dns_keytab_path,
647 "DNSPASS_B64": b64encode(dnspass),
651 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
652 """Setup the secrets database.
654 :param path: Path to the secrets database.
655 :param setup_path: Get the path to a setup file.
656 :param session_info: Session info.
657 :param credentials: Credentials
658 :param lp: Loadparm context
659 :return: LDB handle for the created secrets database
661 if os.path.exists(path):
662 os.unlink(path)
663 secrets_ldb = Ldb(path, session_info=session_info,
664 lp=lp)
665 secrets_ldb.erase()
666 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
667 secrets_ldb = Ldb(path, session_info=session_info,
668 lp=lp)
669 secrets_ldb.transaction_start()
670 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
672 if backend_credentials is not None and backend_credentials.authentication_requested():
673 if backend_credentials.get_bind_dn() is not None:
674 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
675 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
676 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
678 else:
679 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
680 "LDAPADMINUSER": backend_credentials.get_username(),
681 "LDAPADMINREALM": backend_credentials.get_realm(),
682 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
685 return secrets_ldb
687 def setup_privileges(path, setup_path, session_info, lp):
688 """Setup the privileges database.
690 :param path: Path to the privileges database.
691 :param setup_path: Get the path to a setup file.
692 :param session_info: Session info.
693 :param credentials: Credentials
694 :param lp: Loadparm context
695 :return: LDB handle for the created secrets database
697 if os.path.exists(path):
698 os.unlink(path)
699 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
700 privilege_ldb.erase()
701 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
704 def setup_registry(path, setup_path, session_info, lp):
705 """Setup the registry.
707 :param path: Path to the registry database
708 :param setup_path: Function that returns the path to a setup.
709 :param session_info: Session information
710 :param credentials: Credentials
711 :param lp: Loadparm context
713 reg = registry.Registry()
714 hive = registry.open_ldb(path, session_info=session_info,
715 lp_ctx=lp)
716 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
717 provision_reg = setup_path("provision.reg")
718 assert os.path.exists(provision_reg)
719 reg.diff_apply(provision_reg)
722 def setup_idmapdb(path, setup_path, session_info, lp):
723 """Setup the idmap database.
725 :param path: path to the idmap database
726 :param setup_path: Function that returns a path to a setup file
727 :param session_info: Session information
728 :param credentials: Credentials
729 :param lp: Loadparm context
731 if os.path.exists(path):
732 os.unlink(path)
734 idmap_ldb = IDmapDB(path, session_info=session_info,
735 lp=lp)
737 idmap_ldb.erase()
738 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
739 return idmap_ldb
742 def setup_samdb_rootdse(samdb, setup_path, names):
743 """Setup the SamDB rootdse.
745 :param samdb: Sam Database handle
746 :param setup_path: Obtain setup path
748 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
749 "SCHEMADN": names.schemadn,
750 "NETBIOSNAME": names.netbiosname,
751 "DNSDOMAIN": names.dnsdomain,
752 "REALM": names.realm,
753 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
754 "DOMAINDN": names.domaindn,
755 "ROOTDN": names.rootdn,
756 "CONFIGDN": names.configdn,
757 "SERVERDN": names.serverdn,
761 def setup_self_join(samdb, names,
762 machinepass, dnspass,
763 domainsid, invocationid, setup_path,
764 policyguid, policyguid_dc, domainControllerFunctionality,
765 ntdsguid):
766 """Join a host to its own domain."""
767 assert isinstance(invocationid, str)
768 if ntdsguid is not None:
769 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
770 else:
771 ntdsguid_line = ""
772 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
773 "CONFIGDN": names.configdn,
774 "SCHEMADN": names.schemadn,
775 "DOMAINDN": names.domaindn,
776 "SERVERDN": names.serverdn,
777 "INVOCATIONID": invocationid,
778 "NETBIOSNAME": names.netbiosname,
779 "DEFAULTSITE": names.sitename,
780 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
781 "MACHINEPASS_B64": b64encode(machinepass),
782 "REALM": names.realm,
783 "DOMAIN": names.domain,
784 "DOMAINSID": str(domainsid),
785 "DNSDOMAIN": names.dnsdomain,
786 "SAMBA_VERSION_STRING": version,
787 "NTDSGUID": ntdsguid_line,
788 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
790 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
791 "POLICYGUID": policyguid,
792 "POLICYGUID_DC": policyguid_dc,
793 "DNSDOMAIN": names.dnsdomain,
794 "DOMAINSID": str(domainsid),
795 "DOMAINDN": names.domaindn})
797 # add the NTDSGUID based SPNs
798 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
799 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
800 expression="", scope=ldb.SCOPE_BASE)
801 assert isinstance(names.ntdsguid, str)
803 # Setup fSMORoleOwner entries to point at the newly created DC entry
804 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
805 "DOMAIN": names.domain,
806 "DNSDOMAIN": names.dnsdomain,
807 "DOMAINDN": names.domaindn,
808 "CONFIGDN": names.configdn,
809 "SCHEMADN": names.schemadn,
810 "DEFAULTSITE": names.sitename,
811 "SERVERDN": names.serverdn,
812 "NETBIOSNAME": names.netbiosname,
813 "NTDSGUID": names.ntdsguid,
814 "DNSPASS_B64": b64encode(dnspass),
817 def getpolicypath(sysvolpath, dnsdomain, guid):
818 if guid[0] != "{":
819 guid = "{%s}" % guid
820 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
821 return policy_path
823 def create_gpo_struct(policy_path):
824 os.makedirs(policy_path, 0755)
825 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
826 "[General]\r\nVersion=65543")
827 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
828 os.makedirs(os.path.join(policy_path, "USER"), 0755)
831 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
832 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
833 create_gpo_struct(policy_path)
835 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
836 create_gpo_struct(policy_path)
839 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
840 names, message,
841 domainsid, domainguid, policyguid, policyguid_dc,
842 fill, adminpass, krbtgtpass,
843 machinepass, invocationid, dnspass, ntdsguid,
844 serverrole, am_rodc, dom_for_fun_level=None,
845 schema=None):
846 """Setup a complete SAM Database.
848 :note: This will wipe the main SAM database file!
851 # ATTENTION: Do NOT change these default values without discussion with the
852 # team and/or release manager. They have a big impact on the whole program!
853 domainControllerFunctionality = DS_DC_FUNCTION_2008
855 if dom_for_fun_level is None:
856 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
857 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
858 message("You want to run SAMBA 4 on a domain and forest function level"
859 " lower than Windows 2003 (Native). This is not recommended")
861 if dom_for_fun_level > domainControllerFunctionality:
862 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!")
864 domainFunctionality = dom_for_fun_level
865 forestFunctionality = dom_for_fun_level
867 # Also wipes the database
868 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
869 provision_backend=provision_backend, session_info=session_info,
870 names=names, serverrole=serverrole, schema=schema)
872 if schema is None:
873 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
874 am_rodc=am_rodc)
876 # Load the database, but don's load the global schema and don't connect quite yet
877 samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
878 credentials=provision_backend.credentials, lp=lp, global_schema=False,
879 am_rodc=am_rodc)
881 message("Pre-loading the Samba 4 and AD schema")
883 # Load the schema from the one we computed earlier
884 samdb.set_schema_from_ldb(schema.ldb)
886 # And now we can connect to the DB - the schema won't be loaded from the DB
887 samdb.connect(path)
889 if fill == FILL_DRS:
890 return samdb
892 samdb.transaction_start()
893 try:
894 # Set the domain functionality levels onto the database.
895 # Various module (the password_hash module in particular) need
896 # to know what level of AD we are emulating.
898 # These will be fixed into the database via the database
899 # modifictions below, but we need them set from the start.
900 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
901 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
902 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
904 samdb.set_domain_sid(str(domainsid))
905 samdb.set_invocation_id(invocationid)
906 samdb.set_ntds_settings_dn("CN=NTDS Settings,%s" % names.serverdn)
908 message("Adding DomainDN: %s" % names.domaindn)
910 #impersonate domain admin
911 admin_session_info = admin_session(lp, str(domainsid))
912 samdb.set_session_info(admin_session_info)
913 if domainguid is not None:
914 domainguid_line = "objectGUID: %s\n-" % domainguid
915 else:
916 domainguid_line = ""
918 descr = b64encode(get_domain_descriptor(domainsid))
919 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
920 "DOMAINDN": names.domaindn,
921 "DOMAINGUID": domainguid_line,
922 "DESCRIPTOR": descr
926 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
927 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
928 "DOMAINSID": str(domainsid),
929 "SCHEMADN": names.schemadn,
930 "NETBIOSNAME": names.netbiosname,
931 "DEFAULTSITE": names.sitename,
932 "CONFIGDN": names.configdn,
933 "SERVERDN": names.serverdn,
934 "POLICYGUID": policyguid,
935 "DOMAINDN": names.domaindn,
936 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
937 "SAMBA_VERSION_STRING": version
940 message("Adding configuration container")
941 descr = b64encode(get_config_descriptor(domainsid))
942 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
943 "CONFIGDN": names.configdn,
944 "DESCRIPTOR": descr,
947 # The LDIF here was created when the Schema object was constructed
948 message("Setting up sam.ldb schema")
949 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
950 samdb.modify_ldif(schema.schema_dn_modify)
951 samdb.write_prefixes_from_schema()
952 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
953 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
954 {"SCHEMADN": names.schemadn})
956 message("Reopening sam.ldb with new schema")
957 except:
958 samdb.transaction_cancel()
959 raise
960 else:
961 samdb.transaction_commit()
963 samdb = SamDB(session_info=admin_session_info,
964 credentials=provision_backend.credentials, lp=lp,
965 global_schema=False, am_rodc=am_rodc)
966 samdb.connect(path)
967 samdb.transaction_start()
968 try:
969 samdb.invocation_id = invocationid
971 message("Setting up sam.ldb configuration data")
972 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
973 "CONFIGDN": names.configdn,
974 "NETBIOSNAME": names.netbiosname,
975 "DEFAULTSITE": names.sitename,
976 "DNSDOMAIN": names.dnsdomain,
977 "DOMAIN": names.domain,
978 "SCHEMADN": names.schemadn,
979 "DOMAINDN": names.domaindn,
980 "SERVERDN": names.serverdn,
981 "FOREST_FUNCTIONALITY": str(forestFunctionality),
982 "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
985 message("Setting up display specifiers")
986 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
987 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
988 check_all_substituted(display_specifiers_ldif)
989 samdb.add_ldif(display_specifiers_ldif)
991 message("Adding users container")
992 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
993 "DOMAINDN": names.domaindn})
994 message("Modifying users container")
995 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
996 "DOMAINDN": names.domaindn})
997 message("Adding computers container")
998 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
999 "DOMAINDN": names.domaindn})
1000 message("Modifying computers container")
1001 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1002 "DOMAINDN": names.domaindn})
1003 message("Setting up sam.ldb data")
1004 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1005 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1006 "DOMAINDN": names.domaindn,
1007 "NETBIOSNAME": names.netbiosname,
1008 "DEFAULTSITE": names.sitename,
1009 "CONFIGDN": names.configdn,
1010 "SERVERDN": names.serverdn,
1011 "POLICYGUID_DC": policyguid_dc
1014 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1015 "DOMAINDN": names.domaindn})
1017 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1018 "CONFIGDN": names.configdn,
1019 "SCHEMADN": names.schemadn})
1020 if fill == FILL_FULL:
1021 message("Setting up sam.ldb users and groups")
1022 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1023 "DOMAINDN": names.domaindn,
1024 "DOMAINSID": str(domainsid),
1025 "CONFIGDN": names.configdn,
1026 "ADMINPASS_B64": b64encode(adminpass),
1027 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1030 message("Setting up self join")
1031 setup_self_join(samdb, names=names, invocationid=invocationid,
1032 dnspass=dnspass,
1033 machinepass=machinepass,
1034 domainsid=domainsid, policyguid=policyguid,
1035 policyguid_dc=policyguid_dc,
1036 setup_path=setup_path,
1037 domainControllerFunctionality=domainControllerFunctionality,
1038 ntdsguid=ntdsguid)
1040 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1041 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1042 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1043 assert isinstance(names.ntdsguid, str)
1044 except:
1045 samdb.transaction_cancel()
1046 raise
1047 else:
1048 samdb.transaction_commit()
1049 return samdb
1052 FILL_FULL = "FULL"
1053 FILL_NT4SYNC = "NT4SYNC"
1054 FILL_DRS = "DRS"
1055 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1056 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)"
1058 def set_dir_acl(path, acl, lp, domsid):
1059 setntacl(lp, path, acl, domsid)
1060 for root, dirs, files in os.walk(path, topdown=False):
1061 for name in files:
1062 setntacl(lp, os.path.join(root, name), acl, domsid)
1063 for name in dirs:
1064 setntacl(lp, os.path.join(root, name), acl, domsid)
1067 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1068 # Set ACL for GPO
1069 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1070 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1071 lp, str(domainsid))
1072 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1073 attrs=["cn","nTSecurityDescriptor"],
1074 expression="", scope=ldb.SCOPE_ONELEVEL)
1075 for policy in res:
1076 acl = ndr_unpack(security.descriptor,
1077 str(policy["nTSecurityDescriptor"])).as_sddl()
1078 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1079 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1080 str(domainsid))
1082 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1083 lp):
1084 try:
1085 os.chown(sysvol,-1,gid)
1086 except:
1087 canchown = False
1088 else:
1089 canchown = True
1091 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1092 for root, dirs, files in os.walk(sysvol, topdown=False):
1093 for name in files:
1094 if canchown:
1095 os.chown(os.path.join(root, name),-1,gid)
1096 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1097 for name in dirs:
1098 if canchown:
1099 os.chown(os.path.join(root, name),-1,gid)
1100 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1101 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1104 def provision(setup_dir, message, session_info,
1105 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1106 realm=None,
1107 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1108 serverdn=None,
1109 domain=None, hostname=None, hostip=None, hostip6=None,
1110 domainsid=None, adminpass=None, ldapadminpass=None,
1111 krbtgtpass=None, domainguid=None,
1112 policyguid=None, policyguid_dc=None, invocationid=None,
1113 machinepass=None, ntdsguid=None,
1114 dnspass=None, root=None, nobody=None, users=None,
1115 wheel=None, backup=None, aci=None, serverrole=None,
1116 dom_for_fun_level=None,
1117 ldap_backend_extra_port=None, backend_type=None,
1118 sitename=None,
1119 ol_mmr_urls=None, ol_olc=None,
1120 setup_ds_path=None, slapd_path=None, nosync=False,
1121 ldap_dryrun_mode=False,useeadb=False, am_rodc=False):
1122 """Provision samba4
1124 :note: caution, this wipes all existing data!
1127 def setup_path(file):
1128 return os.path.join(setup_dir, file)
1130 if domainsid is None:
1131 domainsid = security.random_sid()
1132 else:
1133 domainsid = security.dom_sid(domainsid)
1135 # create/adapt the group policy GUIDs
1136 if policyguid is None:
1137 policyguid = str(uuid.uuid4())
1138 policyguid = policyguid.upper()
1139 if policyguid_dc is None:
1140 policyguid_dc = str(uuid.uuid4())
1141 policyguid_dc = policyguid_dc.upper()
1143 if adminpass is None:
1144 adminpass = samba.generate_random_password(12, 32)
1145 if krbtgtpass is None:
1146 krbtgtpass = samba.generate_random_password(128, 255)
1147 if machinepass is None:
1148 machinepass = samba.generate_random_password(128, 255)
1149 if dnspass is None:
1150 dnspass = samba.generate_random_password(128, 255)
1151 if ldapadminpass is None:
1152 #Make a new, random password between Samba and it's LDAP server
1153 ldapadminpass=samba.generate_random_password(128, 255)
1155 if backend_type is None:
1156 backend_type = "ldb"
1158 sid_generator = "internal"
1159 if backend_type == "fedora-ds":
1160 sid_generator = "backend"
1162 root_uid = findnss_uid([root or "root"])
1163 nobody_uid = findnss_uid([nobody or "nobody"])
1164 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1165 if wheel is None:
1166 wheel_gid = findnss_gid(["wheel", "adm"])
1167 else:
1168 wheel_gid = findnss_gid([wheel])
1169 try:
1170 bind_gid = findnss_gid(["bind", "named"])
1171 except KeyError:
1172 bind_gid = None
1174 if targetdir is not None:
1175 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1176 elif smbconf is None:
1177 smbconf = param.default_path()
1178 if not os.path.exists(os.path.dirname(smbconf)):
1179 os.makedirs(os.path.dirname(smbconf))
1181 # only install a new smb.conf if there isn't one there already
1182 if os.path.exists(smbconf):
1183 # if Samba Team members can't figure out the weird errors
1184 # loading an empty smb.conf gives, then we need to be smarter.
1185 # Pretend it just didn't exist --abartlet
1186 data = open(smbconf, 'r').read()
1187 data = data.lstrip()
1188 if data is None or data == "":
1189 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1190 serverrole, targetdir, sid_generator, useeadb)
1191 else:
1192 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1193 targetdir, sid_generator, useeadb)
1195 lp = param.LoadParm()
1196 lp.load(smbconf)
1198 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1199 dnsdomain=realm, serverrole=serverrole,
1200 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1201 serverdn=serverdn, sitename=sitename)
1203 paths = provision_paths_from_lp(lp, names.dnsdomain)
1205 paths.bind_gid = bind_gid
1207 if hostip is None:
1208 hostips = samba.interface_ips(lp, False)
1209 if len(hostips) == 0:
1210 message("No external IPv4 address has been found: I use the loopback.")
1211 hostip = '127.0.0.1'
1212 else:
1213 hostip = hostips[0]
1214 if len(hostips) > 1:
1215 message("More than one IPv4 address found: I use " + hostip + ".")
1217 if hostip6 is None:
1218 try:
1219 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1220 if hostip6 is None:
1221 hostip6 = ip[-1][0]
1222 if hostip6 == '::1' and ip[-1][0] != '::1':
1223 hostip6 = ip[-1][0]
1224 except socket.gaierror, (socket.EAI_NODATA, msg):
1225 hostip6 = None
1227 if serverrole is None:
1228 serverrole = lp.get("server role")
1230 assert serverrole in ("domain controller", "member server", "standalone")
1231 if invocationid is None:
1232 invocationid = str(uuid.uuid4())
1234 if not os.path.exists(paths.private_dir):
1235 os.mkdir(paths.private_dir)
1236 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1237 os.mkdir(os.path.join(paths.private_dir,"tls"))
1239 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1241 schema = Schema(setup_path, domainsid, invocationid=invocationid, schemadn=names.schemadn,
1242 serverdn=names.serverdn, am_rodc=am_rodc)
1244 if backend_type == "ldb":
1245 provision_backend = LDBBackend(backend_type,
1246 paths=paths, setup_path=setup_path,
1247 lp=lp, credentials=credentials,
1248 names=names,
1249 message=message)
1250 elif backend_type == "existing":
1251 provision_backend = ExistingBackend(backend_type,
1252 paths=paths, setup_path=setup_path,
1253 lp=lp, credentials=credentials,
1254 names=names,
1255 message=message,
1256 ldapi_url=ldapi_url)
1257 elif backend_type == "fedora-ds":
1258 provision_backend = FDSBackend(backend_type,
1259 paths=paths, setup_path=setup_path,
1260 lp=lp, credentials=credentials,
1261 names=names,
1262 message=message,
1263 domainsid=domainsid,
1264 schema=schema,
1265 hostname=hostname,
1266 ldapadminpass=ldapadminpass,
1267 slapd_path=slapd_path,
1268 ldap_backend_extra_port=ldap_backend_extra_port,
1269 ldap_dryrun_mode=ldap_dryrun_mode,
1270 root=root,
1271 setup_ds_path=setup_ds_path)
1272 elif backend_type == "openldap":
1273 provision_backend = OpenLDAPBackend(backend_type,
1274 paths=paths, setup_path=setup_path,
1275 lp=lp, credentials=credentials,
1276 names=names,
1277 message=message,
1278 domainsid=domainsid,
1279 schema=schema,
1280 hostname=hostname,
1281 ldapadminpass=ldapadminpass,
1282 slapd_path=slapd_path,
1283 ldap_backend_extra_port=ldap_backend_extra_port,
1284 ldap_dryrun_mode=ldap_dryrun_mode,
1285 ol_mmr_urls=ol_mmr_urls,
1286 nosync=nosync)
1287 else:
1288 raise ValueError("Unknown LDAP backend type selected")
1290 provision_backend.init()
1291 provision_backend.start()
1293 # only install a new shares config db if there is none
1294 if not os.path.exists(paths.shareconf):
1295 message("Setting up share.ldb")
1296 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1297 lp=lp)
1298 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1301 message("Setting up secrets.ldb")
1302 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1303 session_info=session_info,
1304 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1306 message("Setting up the registry")
1307 setup_registry(paths.hklm, setup_path, session_info,
1308 lp=lp)
1310 message("Setting up the privileges database")
1311 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1313 message("Setting up idmap db")
1314 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1315 lp=lp)
1317 message("Setting up SAM db")
1318 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1319 provision_backend, lp, names,
1320 message,
1321 domainsid=domainsid,
1322 schema=schema, domainguid=domainguid,
1323 policyguid=policyguid, policyguid_dc=policyguid_dc,
1324 fill=samdb_fill,
1325 adminpass=adminpass, krbtgtpass=krbtgtpass,
1326 invocationid=invocationid,
1327 machinepass=machinepass, dnspass=dnspass,
1328 ntdsguid=ntdsguid, serverrole=serverrole,
1329 dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc)
1331 if serverrole == "domain controller":
1332 if paths.netlogon is None:
1333 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1334 message("Please either remove %s or see the template at %s" %
1335 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1336 assert paths.netlogon is not None
1338 if paths.sysvol is None:
1339 message("Existing smb.conf does not have a [sysvol] share, but you"
1340 " are configuring a DC.")
1341 message("Please either remove %s or see the template at %s" %
1342 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1343 assert paths.sysvol is not None
1345 if not os.path.isdir(paths.netlogon):
1346 os.makedirs(paths.netlogon, 0755)
1348 if samdb_fill == FILL_FULL:
1349 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1350 root_uid=root_uid, nobody_uid=nobody_uid,
1351 users_gid=users_gid, wheel_gid=wheel_gid)
1353 if serverrole == "domain controller":
1354 # Set up group policies (domain policy and domain controller policy)
1355 setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1356 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1357 domainsid, names.dnsdomain, names.domaindn, lp)
1359 message("Setting up sam.ldb rootDSE marking as synchronized")
1360 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1362 secretsdb_self_join(secrets_ldb, domain=names.domain,
1363 realm=names.realm,
1364 dnsdomain=names.dnsdomain,
1365 netbiosname=names.netbiosname,
1366 domainsid=domainsid,
1367 machinepass=machinepass,
1368 secure_channel_type=SEC_CHAN_BDC)
1370 if serverrole == "domain controller":
1371 secretsdb_setup_dns(secrets_ldb, setup_path,
1372 paths.private_dir,
1373 realm=names.realm, dnsdomain=names.dnsdomain,
1374 dns_keytab_path=paths.dns_keytab,
1375 dnspass=dnspass)
1377 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1378 assert isinstance(domainguid, str)
1380 # Only make a zone file on the first DC, it should be replicated
1381 # with DNS replication
1382 create_zone_file(lp, message, paths, targetdir, setup_path,
1383 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1384 hostname=names.hostname, realm=names.realm,
1385 domainguid=domainguid, ntdsguid=names.ntdsguid)
1387 create_named_conf(paths, setup_path, realm=names.realm,
1388 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1390 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1391 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1392 keytab_name=paths.dns_keytab)
1393 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1394 message("and %s for further documentation required for secure DNS "
1395 "updates" % paths.namedtxt)
1397 create_krb5_conf(paths.krb5conf, setup_path,
1398 dnsdomain=names.dnsdomain, hostname=names.hostname,
1399 realm=names.realm)
1400 message("A Kerberos configuration suitable for Samba 4 has been "
1401 "generated at %s" % paths.krb5conf)
1403 if serverrole == "domain controller":
1404 create_dns_update_list(lp, message, paths, setup_path)
1406 provision_backend.post_setup()
1407 provision_backend.shutdown()
1409 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1410 ldapi_url)
1412 #Now commit the secrets.ldb to disk
1413 secrets_ldb.transaction_commit()
1415 # the commit creates the dns.keytab, now chown it
1416 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1417 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1418 try:
1419 os.chmod(dns_keytab_path, 0640)
1420 os.chown(dns_keytab_path, -1, paths.bind_gid)
1421 except OSError:
1422 message("Failed to chown %s to bind gid %u" % (dns_keytab_path,
1423 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: " + provision_backend.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
1459 def provision_become_dc(setup_dir=None,
1460 smbconf=None, targetdir=None, realm=None,
1461 rootdn=None, domaindn=None, schemadn=None,
1462 configdn=None, serverdn=None,
1463 domain=None, hostname=None, domainsid=None,
1464 adminpass=None, krbtgtpass=None, domainguid=None,
1465 policyguid=None, policyguid_dc=None, invocationid=None,
1466 machinepass=None,
1467 dnspass=None, root=None, nobody=None, users=None,
1468 wheel=None, backup=None, serverrole=None,
1469 ldap_backend=None, ldap_backend_type=None,
1470 sitename=None, debuglevel=1):
1472 def message(text):
1473 """print a message if quiet is not set."""
1474 print text
1476 samba.set_debug_level(debuglevel)
1478 return provision(setup_dir, message, system_session(), None,
1479 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1480 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1481 configdn=configdn, serverdn=serverdn, domain=domain,
1482 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1483 machinepass=machinepass, serverrole="domain controller",
1484 sitename=sitename)
1487 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1488 """Create a PHP LDAP admin configuration file.
1490 :param path: Path to write the configuration to.
1491 :param setup_path: Function to generate setup paths.
1493 setup_file(setup_path("phpldapadmin-config.php"), path,
1494 {"S4_LDAPI_URI": ldapi_uri})
1497 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1498 hostip, hostip6, hostname, realm, domainguid,
1499 ntdsguid):
1500 """Write out a DNS zone file, from the info in the current database.
1502 :param paths: paths object
1503 :param setup_path: Setup path function.
1504 :param dnsdomain: DNS Domain name
1505 :param domaindn: DN of the Domain
1506 :param hostip: Local IPv4 IP
1507 :param hostip6: Local IPv6 IP
1508 :param hostname: Local hostname
1509 :param realm: Realm name
1510 :param domainguid: GUID of the domain.
1511 :param ntdsguid: GUID of the hosts nTDSDSA record.
1513 assert isinstance(domainguid, str)
1515 if hostip6 is not None:
1516 hostip6_base_line = " IN AAAA " + hostip6
1517 hostip6_host_line = hostname + " IN AAAA " + hostip6
1518 else:
1519 hostip6_base_line = ""
1520 hostip6_host_line = ""
1522 if hostip is not None:
1523 hostip_base_line = " IN A " + hostip
1524 hostip_host_line = hostname + " IN A " + hostip
1525 else:
1526 hostip_base_line = ""
1527 hostip_host_line = ""
1529 dns_dir = os.path.dirname(paths.dns)
1531 try:
1532 shutil.rmtree(dns_dir, True)
1533 except OSError:
1534 pass
1536 os.mkdir(dns_dir, 0775)
1538 # we need to freeze the zone while we update the contents
1539 if targetdir is None:
1540 rndc = ' '.join(lp.get("rndc command"))
1541 os.system(rndc + " freeze " + lp.get("realm"))
1543 setup_file(setup_path("provision.zone"), paths.dns, {
1544 "HOSTNAME": hostname,
1545 "DNSDOMAIN": dnsdomain,
1546 "REALM": realm,
1547 "HOSTIP_BASE_LINE": hostip_base_line,
1548 "HOSTIP_HOST_LINE": hostip_host_line,
1549 "DOMAINGUID": domainguid,
1550 "DATESTRING": time.strftime("%Y%m%d%H"),
1551 "DEFAULTSITE": DEFAULTSITE,
1552 "NTDSGUID": ntdsguid,
1553 "HOSTIP6_BASE_LINE": hostip6_base_line,
1554 "HOSTIP6_HOST_LINE": hostip6_host_line,
1557 # note that we use no variable substitution on this file
1558 # the substitution is done at runtime by samba_dnsupdate
1559 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1561 # and the SPN update list
1562 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1564 if paths.bind_gid is not None:
1565 try:
1566 os.chown(dns_dir, -1, paths.bind_gid)
1567 os.chown(paths.dns, -1, paths.bind_gid)
1568 # chmod needed to cope with umask
1569 os.chmod(dns_dir, 0775)
1570 os.chmod(paths.dns, 0664)
1571 except OSError:
1572 message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1574 if targetdir is None:
1575 os.system(rndc + " unfreeze " + lp.get("realm"))
1578 def create_dns_update_list(lp, message, paths, setup_path):
1579 """Write out a dns_update_list file"""
1580 # note that we use no variable substitution on this file
1581 # the substitution is done at runtime by samba_dnsupdate
1582 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1583 setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
1585 def create_named_conf(paths, setup_path, realm, dnsdomain,
1586 private_dir):
1587 """Write out a file containing zone statements suitable for inclusion in a
1588 named.conf file (including GSS-TSIG configuration).
1590 :param paths: all paths
1591 :param setup_path: Setup path function.
1592 :param realm: Realm name
1593 :param dnsdomain: DNS Domain name
1594 :param private_dir: Path to private directory
1595 :param keytab_name: File name of DNS keytab file
1598 setup_file(setup_path("named.conf"), paths.namedconf, {
1599 "DNSDOMAIN": dnsdomain,
1600 "REALM": realm,
1601 "ZONE_FILE": paths.dns,
1602 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1603 "NAMED_CONF": paths.namedconf,
1604 "NAMED_CONF_UPDATE": paths.namedconf_update
1607 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1609 def create_named_txt(path, setup_path, realm, dnsdomain,
1610 private_dir, keytab_name):
1611 """Write out a file containing zone statements suitable for inclusion in a
1612 named.conf file (including GSS-TSIG configuration).
1614 :param path: Path of the new named.conf file.
1615 :param setup_path: Setup path function.
1616 :param realm: Realm name
1617 :param dnsdomain: DNS Domain name
1618 :param private_dir: Path to private directory
1619 :param keytab_name: File name of DNS keytab file
1622 setup_file(setup_path("named.txt"), path, {
1623 "DNSDOMAIN": dnsdomain,
1624 "REALM": realm,
1625 "DNS_KEYTAB": keytab_name,
1626 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1627 "PRIVATE_DIR": private_dir
1630 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1631 """Write out a file containing zone statements suitable for inclusion in a
1632 named.conf file (including GSS-TSIG configuration).
1634 :param path: Path of the new named.conf file.
1635 :param setup_path: Setup path function.
1636 :param dnsdomain: DNS Domain name
1637 :param hostname: Local hostname
1638 :param realm: Realm name
1640 setup_file(setup_path("krb5.conf"), path, {
1641 "DNSDOMAIN": dnsdomain,
1642 "HOSTNAME": hostname,
1643 "REALM": realm,
1647 class ProvisioningError(Exception):
1648 """A generic provision error."""
1650 def __init__(self, value):
1651 self.value = value
1653 def __str__(self):
1654 return "ProvisioningError: " + self.value
1657 class InvalidNetbiosName(Exception):
1658 """A specified name was not a valid NetBIOS name."""
1659 def __init__(self, name):
1660 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)