Fixed a problem with provision missing the default_dir/etc directory.
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / provision.py
blobf2004878c1cf2d6040974111a6bb50d298c1652a
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.namedconf = os.path.join(paths.private_dir, "named.conf")
297 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
298 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
299 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
300 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
301 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
302 paths.phpldapadminconfig = os.path.join(paths.private_dir,
303 "phpldapadmin-config.php")
304 paths.hklm = "hklm.ldb"
305 paths.hkcr = "hkcr.ldb"
306 paths.hkcu = "hkcu.ldb"
307 paths.hku = "hku.ldb"
308 paths.hkpd = "hkpd.ldb"
309 paths.hkpt = "hkpt.ldb"
310 paths.sysvol = lp.get("path", "sysvol")
311 paths.netlogon = lp.get("path", "netlogon")
312 paths.smbconf = lp.configfile
313 return paths
316 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
317 serverrole=None, rootdn=None, domaindn=None, configdn=None,
318 schemadn=None, serverdn=None, sitename=None):
319 """Guess configuration settings to use."""
321 if hostname is None:
322 hostname = socket.gethostname().split(".")[0]
324 netbiosname = lp.get("netbios name")
325 if netbiosname is None:
326 netbiosname = hostname
327 assert netbiosname is not None
328 netbiosname = netbiosname.upper()
329 if not valid_netbios_name(netbiosname):
330 raise InvalidNetbiosName(netbiosname)
332 if dnsdomain is None:
333 dnsdomain = lp.get("realm")
334 if dnsdomain is None or dnsdomain == "":
335 raise ProvisioningError("guess_names: 'realm' not specified in supplied %s!", lp.configfile)
337 dnsdomain = dnsdomain.lower()
339 if serverrole is None:
340 serverrole = lp.get("server role")
341 if serverrole is None:
342 raise ProvisioningError("guess_names: 'server role' not specified in supplied %s!" % lp.configfile)
344 serverrole = serverrole.lower()
346 realm = dnsdomain.upper()
348 if lp.get("realm") == "":
349 raise ProvisioningError("guess_names: 'realm =' was not specified in supplied %s. Please remove the smb.conf file and let provision generate it" % lp.configfile)
351 if lp.get("realm").upper() != realm:
352 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))
354 if lp.get("server role").lower() != serverrole:
355 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))
357 if serverrole == "domain controller":
358 if domain is None:
359 # This will, for better or worse, default to 'WORKGROUP'
360 domain = lp.get("workgroup")
361 domain = domain.upper()
363 if lp.get("workgroup").upper() != domain:
364 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))
366 if domaindn is None:
367 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
368 else:
369 domain = netbiosname
370 if domaindn is None:
371 domaindn = "DC=" + netbiosname
373 if not valid_netbios_name(domain):
374 raise InvalidNetbiosName(domain)
376 if hostname.upper() == realm:
377 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!" % (realm, hostname))
378 if netbiosname == realm:
379 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!" % (realm, netbiosname))
380 if domain == realm:
381 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
383 if rootdn is None:
384 rootdn = domaindn
386 if configdn is None:
387 configdn = "CN=Configuration," + rootdn
388 if schemadn is None:
389 schemadn = "CN=Schema," + configdn
391 if sitename is None:
392 sitename=DEFAULTSITE
394 names = ProvisionNames()
395 names.rootdn = rootdn
396 names.domaindn = domaindn
397 names.configdn = configdn
398 names.schemadn = schemadn
399 names.ldapmanagerdn = "CN=Manager," + rootdn
400 names.dnsdomain = dnsdomain
401 names.domain = domain
402 names.realm = realm
403 names.netbiosname = netbiosname
404 names.hostname = hostname
405 names.sitename = sitename
406 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
408 return names
411 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
412 targetdir, sid_generator,eadb):
413 """Create a new smb.conf file based on a couple of basic settings.
415 assert smbconf is not None
416 if hostname is None:
417 hostname = socket.gethostname().split(".")[0]
418 netbiosname = hostname.upper()
420 if serverrole is None:
421 serverrole = "standalone"
423 assert serverrole in ("domain controller", "member server", "standalone")
424 if serverrole == "domain controller":
425 smbconfsuffix = "dc"
426 elif serverrole == "member server":
427 smbconfsuffix = "member"
428 elif serverrole == "standalone":
429 smbconfsuffix = "standalone"
431 if sid_generator is None:
432 sid_generator = "internal"
434 assert domain is not None
435 domain = domain.upper()
437 assert realm is not None
438 realm = realm.upper()
440 default_lp = param.LoadParm()
441 #Load non-existant file
442 if os.path.exists(smbconf):
443 default_lp.load(smbconf)
444 if eadb:
445 if targetdir is not None:
446 privdir = os.path.join(targetdir, "private")
447 else:
448 privdir = default_lp.get("private dir")
449 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
450 else:
451 posixeadb_line = ""
453 if targetdir is not None:
454 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
455 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
457 default_lp.set("lock dir", os.path.abspath(targetdir))
458 else:
459 privatedir_line = ""
460 lockdir_line = ""
462 if sid_generator == "internal":
463 sid_generator_line = ""
464 else:
465 sid_generator_line = "sid generator = " + sid_generator
467 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
468 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
470 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
471 smbconf, {
472 "NETBIOS_NAME": netbiosname,
473 "DOMAIN": domain,
474 "REALM": realm,
475 "SERVERROLE": serverrole,
476 "NETLOGONPATH": netlogon,
477 "SYSVOLPATH": sysvol,
478 "SIDGENERATOR_LINE": sid_generator_line,
479 "PRIVATEDIR_LINE": privatedir_line,
480 "LOCKDIR_LINE": lockdir_line,
481 "POSIXEADB_LINE": posixeadb_line
485 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
486 users_gid, wheel_gid):
487 """setup reasonable name mappings for sam names to unix names.
489 :param samdb: SamDB object.
490 :param idmap: IDmap db object.
491 :param sid: The domain sid.
492 :param domaindn: The domain DN.
493 :param root_uid: uid of the UNIX root user.
494 :param nobody_uid: uid of the UNIX nobody user.
495 :param users_gid: gid of the UNIX users group.
496 :param wheel_gid: gid of the UNIX wheel group."""
497 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
498 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
500 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
501 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
504 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
505 provision_backend, names, schema,
506 serverrole,
507 erase=False):
508 """Setup the partitions for the SAM database.
510 Alternatively, provision() may call this, and then populate the database.
512 :note: This will wipe the Sam Database!
514 :note: This function always removes the local SAM LDB file. The erase
515 parameter controls whether to erase the existing data, which
516 may not be stored locally but in LDAP.
519 assert session_info is not None
521 # We use options=["modules:"] to stop the modules loading - we
522 # just want to wipe and re-initialise the database, not start it up
524 try:
525 os.unlink(samdb_path)
526 except OSError:
527 pass
529 samdb = Ldb(url=samdb_path, session_info=session_info,
530 lp=lp, options=["modules:"])
532 ldap_backend_line = "# No LDAP backend"
533 if provision_backend.type is not "ldb":
534 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
536 samdb.transaction_start()
537 try:
538 message("Setting up sam.ldb partitions and settings")
539 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
540 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
541 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
542 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
543 "LDAP_BACKEND_LINE": ldap_backend_line,
547 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
548 "BACKEND_TYPE": provision_backend.type,
549 "SERVER_ROLE": serverrole
552 message("Setting up sam.ldb rootDSE")
553 setup_samdb_rootdse(samdb, setup_path, names)
554 except:
555 samdb.transaction_cancel()
556 raise
557 else:
558 samdb.transaction_commit()
561 def secretsdb_self_join(secretsdb, domain,
562 netbiosname, machinepass, domainsid=None,
563 realm=None, dnsdomain=None,
564 keytab_path=None,
565 key_version_number=1,
566 secure_channel_type=SEC_CHAN_WKSTA):
567 """Add domain join-specific bits to a secrets database.
569 :param secretsdb: Ldb Handle to the secrets database
570 :param machinepass: Machine password
572 attrs=["whenChanged",
573 "secret",
574 "priorSecret",
575 "priorChanged",
576 "krb5Keytab",
577 "privateKeytab"]
580 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
581 msg["secureChannelType"] = str(secure_channel_type)
582 msg["flatname"] = [domain]
583 msg["objectClass"] = ["top", "primaryDomain"]
584 if realm is not None:
585 if dnsdomain is None:
586 dnsdomain = realm.lower()
587 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
588 msg["realm"] = realm
589 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
590 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
591 msg["privateKeytab"] = ["secrets.keytab"]
594 msg["secret"] = [machinepass]
595 msg["samAccountName"] = ["%s$" % netbiosname]
596 msg["secureChannelType"] = [str(secure_channel_type)]
597 if domainsid is not None:
598 msg["objectSid"] = [ndr_pack(domainsid)]
600 res = secretsdb.search(base="cn=Primary Domains",
601 attrs=attrs,
602 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
603 scope=ldb.SCOPE_ONELEVEL)
605 for del_msg in res:
606 if del_msg.dn is not msg.dn:
607 secretsdb.delete(del_msg.dn)
609 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
611 if len(res) == 1:
612 msg["priorSecret"] = res[0]["secret"]
613 msg["priorWhenChanged"] = res[0]["whenChanged"]
615 if res["privateKeytab"] is not None:
616 msg["privateKeytab"] = res[0]["privateKeytab"]
618 if res["krb5Keytab"] is not None:
619 msg["krb5Keytab"] = res[0]["krb5Keytab"]
621 for el in msg:
622 el.set_flags(ldb.FLAG_MOD_REPLACE)
623 secretsdb.modify(msg)
624 else:
625 secretsdb.add(msg)
628 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
629 realm, dnsdomain,
630 dns_keytab_path, dnspass):
631 """Add DNS specific bits to a secrets database.
633 :param secretsdb: Ldb Handle to the secrets database
634 :param setup_path: Setup path function
635 :param machinepass: Machine password
637 try:
638 os.unlink(os.path.join(private_dir, dns_keytab_path))
639 except OSError:
640 pass
642 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
643 "REALM": realm,
644 "DNSDOMAIN": dnsdomain,
645 "DNS_KEYTAB": dns_keytab_path,
646 "DNSPASS_B64": b64encode(dnspass),
650 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
651 """Setup the secrets database.
653 :param path: Path to the secrets database.
654 :param setup_path: Get the path to a setup file.
655 :param session_info: Session info.
656 :param credentials: Credentials
657 :param lp: Loadparm context
658 :return: LDB handle for the created secrets database
660 if os.path.exists(path):
661 os.unlink(path)
662 secrets_ldb = Ldb(path, session_info=session_info,
663 lp=lp)
664 secrets_ldb.erase()
665 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
666 secrets_ldb = Ldb(path, session_info=session_info,
667 lp=lp)
668 secrets_ldb.transaction_start()
669 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
671 if backend_credentials is not None and backend_credentials.authentication_requested():
672 if backend_credentials.get_bind_dn() is not None:
673 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
674 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
675 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
677 else:
678 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
679 "LDAPADMINUSER": backend_credentials.get_username(),
680 "LDAPADMINREALM": backend_credentials.get_realm(),
681 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
684 return secrets_ldb
686 def setup_privileges(path, setup_path, session_info, lp):
687 """Setup the privileges database.
689 :param path: Path to the privileges database.
690 :param setup_path: Get the path to a setup file.
691 :param session_info: Session info.
692 :param credentials: Credentials
693 :param lp: Loadparm context
694 :return: LDB handle for the created secrets database
696 if os.path.exists(path):
697 os.unlink(path)
698 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
699 privilege_ldb.erase()
700 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
703 def setup_registry(path, setup_path, session_info, lp):
704 """Setup the registry.
706 :param path: Path to the registry database
707 :param setup_path: Function that returns the path to a setup.
708 :param session_info: Session information
709 :param credentials: Credentials
710 :param lp: Loadparm context
712 reg = registry.Registry()
713 hive = registry.open_ldb(path, session_info=session_info,
714 lp_ctx=lp)
715 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
716 provision_reg = setup_path("provision.reg")
717 assert os.path.exists(provision_reg)
718 reg.diff_apply(provision_reg)
721 def setup_idmapdb(path, setup_path, session_info, lp):
722 """Setup the idmap database.
724 :param path: path to the idmap database
725 :param setup_path: Function that returns a path to a setup file
726 :param session_info: Session information
727 :param credentials: Credentials
728 :param lp: Loadparm context
730 if os.path.exists(path):
731 os.unlink(path)
733 idmap_ldb = IDmapDB(path, session_info=session_info,
734 lp=lp)
736 idmap_ldb.erase()
737 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
738 return idmap_ldb
741 def setup_samdb_rootdse(samdb, setup_path, names):
742 """Setup the SamDB rootdse.
744 :param samdb: Sam Database handle
745 :param setup_path: Obtain setup path
747 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
748 "SCHEMADN": names.schemadn,
749 "NETBIOSNAME": names.netbiosname,
750 "DNSDOMAIN": names.dnsdomain,
751 "REALM": names.realm,
752 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
753 "DOMAINDN": names.domaindn,
754 "ROOTDN": names.rootdn,
755 "CONFIGDN": names.configdn,
756 "SERVERDN": names.serverdn,
760 def setup_self_join(samdb, names,
761 machinepass, dnspass,
762 domainsid, invocationid, setup_path,
763 policyguid, policyguid_dc, domainControllerFunctionality,
764 ntdsguid):
765 """Join a host to its own domain."""
766 assert isinstance(invocationid, str)
767 if ntdsguid is not None:
768 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
769 else:
770 ntdsguid_line = ""
771 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
772 "CONFIGDN": names.configdn,
773 "SCHEMADN": names.schemadn,
774 "DOMAINDN": names.domaindn,
775 "SERVERDN": names.serverdn,
776 "INVOCATIONID": invocationid,
777 "NETBIOSNAME": names.netbiosname,
778 "DEFAULTSITE": names.sitename,
779 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
780 "MACHINEPASS_B64": b64encode(machinepass),
781 "REALM": names.realm,
782 "DOMAIN": names.domain,
783 "DOMAINSID": str(domainsid),
784 "DNSDOMAIN": names.dnsdomain,
785 "SAMBA_VERSION_STRING": version,
786 "NTDSGUID": ntdsguid_line,
787 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
789 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
790 "POLICYGUID": policyguid,
791 "POLICYGUID_DC": policyguid_dc,
792 "DNSDOMAIN": names.dnsdomain,
793 "DOMAINSID": str(domainsid),
794 "DOMAINDN": names.domaindn})
796 # add the NTDSGUID based SPNs
797 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
798 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
799 expression="", scope=ldb.SCOPE_BASE)
800 assert isinstance(names.ntdsguid, str)
802 # Setup fSMORoleOwner entries to point at the newly created DC entry
803 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
804 "DOMAIN": names.domain,
805 "DNSDOMAIN": names.dnsdomain,
806 "DOMAINDN": names.domaindn,
807 "CONFIGDN": names.configdn,
808 "SCHEMADN": names.schemadn,
809 "DEFAULTSITE": names.sitename,
810 "SERVERDN": names.serverdn,
811 "NETBIOSNAME": names.netbiosname,
812 "NTDSGUID": names.ntdsguid,
813 "DNSPASS_B64": b64encode(dnspass),
816 def getpolicypath(sysvolpath, dnsdomain, guid):
817 if guid[0] != "{":
818 guid = "{%s}" % guid
819 policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
820 return policy_path
822 def create_gpo_struct(policy_path):
823 os.makedirs(policy_path, 0755)
824 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
825 "[General]\r\nVersion=65543")
826 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
827 os.makedirs(os.path.join(policy_path, "USER"), 0755)
830 def setup_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
831 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid)
832 create_gpo_struct(policy_path)
834 policy_path = getpolicypath(sysvolpath,dnsdomain,policyguid_dc)
835 create_gpo_struct(policy_path)
838 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
839 names, message,
840 domainsid, domainguid, policyguid, policyguid_dc,
841 fill, adminpass, krbtgtpass,
842 machinepass, invocationid, dnspass, ntdsguid,
843 serverrole, dom_for_fun_level=None,
844 schema=None):
845 """Setup a complete SAM Database.
847 :note: This will wipe the main SAM database file!
850 # ATTENTION: Do NOT change these default values without discussion with the
851 # team and/or release manager. They have a big impact on the whole program!
852 domainControllerFunctionality = DS_DC_FUNCTION_2008
854 if dom_for_fun_level is None:
855 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
856 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
857 message("You want to run SAMBA 4 on a domain and forest function level"
858 " lower than Windows 2003 (Native). This is not recommended")
860 if dom_for_fun_level > domainControllerFunctionality:
861 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!")
863 domainFunctionality = dom_for_fun_level
864 forestFunctionality = dom_for_fun_level
866 # Also wipes the database
867 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
868 provision_backend=provision_backend, session_info=session_info,
869 names=names, serverrole=serverrole, schema=schema)
871 if schema is None:
872 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
874 # Load the database, but importantly, use Ldb not SamDB as we don't want to
875 # load the global schema
876 samdb = Ldb(session_info=session_info,
877 credentials=provision_backend.credentials, lp=lp)
879 message("Pre-loading the Samba 4 and AD schema")
881 # Load the schema from the one we computed earlier
882 samdb.set_schema_from_ldb(schema.ldb)
884 # And now we can connect to the DB - the schema won't be loaded from the DB
885 samdb.connect(path)
887 if fill == FILL_DRS:
888 return samdb
890 samdb.transaction_start()
891 try:
892 # Set the domain functionality levels onto the database.
893 # Various module (the password_hash module in particular) need
894 # to know what level of AD we are emulating.
896 # These will be fixed into the database via the database
897 # modifictions below, but we need them set from the start.
898 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
899 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
900 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
902 samdb.set_domain_sid(str(domainsid))
903 samdb.set_invocation_id(invocationid)
905 message("Adding DomainDN: %s" % names.domaindn)
907 #impersonate domain admin
908 admin_session_info = admin_session(lp, str(domainsid))
909 samdb.set_session_info(admin_session_info)
910 if domainguid is not None:
911 domainguid_line = "objectGUID: %s\n-" % domainguid
912 else:
913 domainguid_line = ""
915 descr = b64encode(get_domain_descriptor(domainsid))
916 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
917 "DOMAINDN": names.domaindn,
918 "DOMAINGUID": domainguid_line,
919 "DESCRIPTOR": descr
923 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
924 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
925 "DOMAINSID": str(domainsid),
926 "SCHEMADN": names.schemadn,
927 "NETBIOSNAME": names.netbiosname,
928 "DEFAULTSITE": names.sitename,
929 "CONFIGDN": names.configdn,
930 "SERVERDN": names.serverdn,
931 "POLICYGUID": policyguid,
932 "DOMAINDN": names.domaindn,
933 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
934 "SAMBA_VERSION_STRING": version
937 message("Adding configuration container")
938 descr = b64encode(get_config_descriptor(domainsid))
939 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
940 "CONFIGDN": names.configdn,
941 "DESCRIPTOR": descr,
944 # The LDIF here was created when the Schema object was constructed
945 message("Setting up sam.ldb schema")
946 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
947 samdb.modify_ldif(schema.schema_dn_modify)
948 # set schemaInfo to defalt value for a new Forest
949 samdb.set_schema_info()
950 samdb.write_prefixes_from_schema()
951 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
952 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
953 {"SCHEMADN": names.schemadn})
955 message("Reopening sam.ldb with new schema")
956 except:
957 samdb.transaction_cancel()
958 raise
959 else:
960 samdb.transaction_commit()
962 samdb = SamDB(session_info=admin_session_info,
963 credentials=provision_backend.credentials, lp=lp,
964 global_schema=False)
965 samdb.connect(path)
966 samdb.transaction_start()
967 try:
968 samdb.invocation_id = invocationid
970 message("Setting up sam.ldb configuration data")
971 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
972 "CONFIGDN": names.configdn,
973 "NETBIOSNAME": names.netbiosname,
974 "DEFAULTSITE": names.sitename,
975 "DNSDOMAIN": names.dnsdomain,
976 "DOMAIN": names.domain,
977 "SCHEMADN": names.schemadn,
978 "DOMAINDN": names.domaindn,
979 "SERVERDN": names.serverdn,
980 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
983 message("Setting up display specifiers")
984 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
985 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
986 check_all_substituted(display_specifiers_ldif)
987 samdb.add_ldif(display_specifiers_ldif)
989 message("Adding users container")
990 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
991 "DOMAINDN": names.domaindn})
992 message("Modifying users container")
993 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
994 "DOMAINDN": names.domaindn})
995 message("Adding computers container")
996 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
997 "DOMAINDN": names.domaindn})
998 message("Modifying computers container")
999 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1000 "DOMAINDN": names.domaindn})
1001 message("Setting up sam.ldb data")
1002 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1003 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
1004 "DOMAINDN": names.domaindn,
1005 "NETBIOSNAME": names.netbiosname,
1006 "DEFAULTSITE": names.sitename,
1007 "CONFIGDN": names.configdn,
1008 "SERVERDN": names.serverdn,
1009 "POLICYGUID_DC": policyguid_dc
1012 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
1013 "DOMAINDN": names.domaindn})
1015 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1016 "CONFIGDN": names.configdn,
1017 "SCHEMADN": names.schemadn})
1018 if fill == FILL_FULL:
1019 message("Setting up sam.ldb users and groups")
1020 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1021 "DOMAINDN": names.domaindn,
1022 "DOMAINSID": str(domainsid),
1023 "CONFIGDN": names.configdn,
1024 "ADMINPASS_B64": b64encode(adminpass),
1025 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1028 message("Setting up self join")
1029 setup_self_join(samdb, names=names, invocationid=invocationid,
1030 dnspass=dnspass,
1031 machinepass=machinepass,
1032 domainsid=domainsid, policyguid=policyguid,
1033 policyguid_dc=policyguid_dc,
1034 setup_path=setup_path,
1035 domainControllerFunctionality=domainControllerFunctionality,
1036 ntdsguid=ntdsguid)
1038 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1039 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1040 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1041 assert isinstance(names.ntdsguid, str)
1042 except:
1043 samdb.transaction_cancel()
1044 raise
1045 else:
1046 samdb.transaction_commit()
1047 return samdb
1050 FILL_FULL = "FULL"
1051 FILL_NT4SYNC = "NT4SYNC"
1052 FILL_DRS = "DRS"
1053 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1054 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)"
1056 def set_dir_acl(path, acl, lp, domsid):
1057 setntacl(lp, path, acl, domsid)
1058 for root, dirs, files in os.walk(path, topdown=False):
1059 for name in files:
1060 setntacl(lp, os.path.join(root, name), acl, domsid)
1061 for name in dirs:
1062 setntacl(lp, os.path.join(root, name), acl, domsid)
1065 def set_gpo_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp):
1066 # Set ACL for GPO
1067 policy_path = os.path.join(sysvol, dnsdomain, "Policies")
1068 set_dir_acl(policy_path,dsacl2fsacl(POLICIES_ACL, str(domainsid)),
1069 lp, str(domainsid))
1070 res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn),
1071 attrs=["cn","nTSecurityDescriptor"],
1072 expression="", scope=ldb.SCOPE_ONELEVEL)
1073 for policy in res:
1074 acl = ndr_unpack(security.descriptor,
1075 str(policy["nTSecurityDescriptor"])).as_sddl()
1076 policy_path = getpolicypath(sysvol,dnsdomain,str(policy["cn"]))
1077 set_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp,
1078 str(domainsid))
1080 def setsysvolacl(samdb, netlogon, sysvol, gid, domainsid, dnsdomain, domaindn,
1081 lp):
1082 try:
1083 os.chown(sysvol,-1,gid)
1084 except:
1085 canchown = False
1086 else:
1087 canchown = True
1089 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1090 for root, dirs, files in os.walk(sysvol, topdown=False):
1091 for name in files:
1092 if canchown:
1093 os.chown(os.path.join(root, name),-1,gid)
1094 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1095 for name in dirs:
1096 if canchown:
1097 os.chown(os.path.join(root, name),-1,gid)
1098 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1099 set_gpo_acl(sysvol,dnsdomain,domainsid,domaindn,samdb,lp)
1102 def provision(setup_dir, message, session_info,
1103 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1104 realm=None,
1105 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1106 serverdn=None,
1107 domain=None, hostname=None, hostip=None, hostip6=None,
1108 domainsid=None, adminpass=None, ldapadminpass=None,
1109 krbtgtpass=None, domainguid=None,
1110 policyguid=None, policyguid_dc=None, invocationid=None,
1111 machinepass=None, ntdsguid=None,
1112 dnspass=None, root=None, nobody=None, users=None,
1113 wheel=None, backup=None, aci=None, serverrole=None,
1114 dom_for_fun_level=None,
1115 ldap_backend_extra_port=None, backend_type=None,
1116 sitename=None,
1117 ol_mmr_urls=None, ol_olc=None,
1118 setup_ds_path=None, slapd_path=None, nosync=False,
1119 ldap_dryrun_mode=False,useeadb=False):
1120 """Provision samba4
1122 :note: caution, this wipes all existing data!
1125 def setup_path(file):
1126 return os.path.join(setup_dir, file)
1128 if domainsid is None:
1129 domainsid = security.random_sid()
1130 else:
1131 domainsid = security.dom_sid(domainsid)
1133 # create/adapt the group policy GUIDs
1134 if policyguid is None:
1135 policyguid = str(uuid.uuid4())
1136 policyguid = policyguid.upper()
1137 if policyguid_dc is None:
1138 policyguid_dc = str(uuid.uuid4())
1139 policyguid_dc = policyguid_dc.upper()
1141 if adminpass is None:
1142 adminpass = samba.generate_random_password(12, 32)
1143 if krbtgtpass is None:
1144 krbtgtpass = samba.generate_random_password(128, 255)
1145 if machinepass is None:
1146 machinepass = samba.generate_random_password(128, 255)
1147 if dnspass is None:
1148 dnspass = samba.generate_random_password(128, 255)
1149 if ldapadminpass is None:
1150 #Make a new, random password between Samba and it's LDAP server
1151 ldapadminpass=samba.generate_random_password(128, 255)
1153 if backend_type is None:
1154 backend_type = "ldb"
1156 sid_generator = "internal"
1157 if backend_type == "fedora-ds":
1158 sid_generator = "backend"
1160 root_uid = findnss_uid([root or "root"])
1161 nobody_uid = findnss_uid([nobody or "nobody"])
1162 users_gid = findnss_gid([users or "users", 'users', 'other', 'staff'])
1163 if wheel is None:
1164 wheel_gid = findnss_gid(["wheel", "adm"])
1165 else:
1166 wheel_gid = findnss_gid([wheel])
1167 try:
1168 bind_gid = findnss_gid(["bind", "named"])
1169 except KeyError:
1170 bind_gid = None
1172 if targetdir is not None:
1173 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1174 elif smbconf is None:
1175 smbconf = param.default_path()
1176 if not os.path.exists(os.path.dirname(smbconf)):
1177 os.makedirs(os.path.dirname(smbconf))
1179 # only install a new smb.conf if there isn't one there already
1180 if os.path.exists(smbconf):
1181 # if Samba Team members can't figure out the weird errors
1182 # loading an empty smb.conf gives, then we need to be smarter.
1183 # Pretend it just didn't exist --abartlet
1184 data = open(smbconf, 'r').read()
1185 data = data.lstrip()
1186 if data is None or data == "":
1187 make_smbconf(smbconf, setup_path, hostname, domain, realm,
1188 serverrole, targetdir, sid_generator, useeadb)
1189 else:
1190 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1191 targetdir, sid_generator, useeadb)
1193 lp = param.LoadParm()
1194 lp.load(smbconf)
1196 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1197 dnsdomain=realm, serverrole=serverrole,
1198 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1199 serverdn=serverdn, sitename=sitename)
1201 paths = provision_paths_from_lp(lp, names.dnsdomain)
1203 paths.bind_gid = bind_gid
1205 if hostip is None:
1206 hostips = samba.interface_ips(lp, False)
1207 if len(hostips) == 0:
1208 message("No external IPv4 address has been found: I use the loopback.")
1209 hostip = '127.0.0.1'
1210 else:
1211 hostip = hostips[0]
1212 if len(hostips) > 1:
1213 message("More than one IPv4 address found: I use " + hostip + ".")
1215 if hostip6 is None:
1216 try:
1217 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1218 if hostip6 is None:
1219 hostip6 = ip[-1][0]
1220 if hostip6 == '::1' and ip[-1][0] != '::1':
1221 hostip6 = ip[-1][0]
1222 except socket.gaierror, (socket.EAI_NODATA, msg):
1223 hostip6 = None
1225 if serverrole is None:
1226 serverrole = lp.get("server role")
1228 assert serverrole in ("domain controller", "member server", "standalone")
1229 if invocationid is None:
1230 invocationid = str(uuid.uuid4())
1232 if not os.path.exists(paths.private_dir):
1233 os.mkdir(paths.private_dir)
1234 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1235 os.mkdir(os.path.join(paths.private_dir,"tls"))
1237 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1239 schema = Schema(setup_path, domainsid, schemadn=names.schemadn,
1240 serverdn=names.serverdn)
1242 if backend_type == "ldb":
1243 provision_backend = LDBBackend(backend_type,
1244 paths=paths, setup_path=setup_path,
1245 lp=lp, credentials=credentials,
1246 names=names,
1247 message=message)
1248 elif backend_type == "existing":
1249 provision_backend = ExistingBackend(backend_type,
1250 paths=paths, setup_path=setup_path,
1251 lp=lp, credentials=credentials,
1252 names=names,
1253 message=message,
1254 ldapi_url=ldapi_url)
1255 elif backend_type == "fedora-ds":
1256 provision_backend = FDSBackend(backend_type,
1257 paths=paths, setup_path=setup_path,
1258 lp=lp, credentials=credentials,
1259 names=names,
1260 message=message,
1261 domainsid=domainsid,
1262 schema=schema,
1263 hostname=hostname,
1264 ldapadminpass=ldapadminpass,
1265 slapd_path=slapd_path,
1266 ldap_backend_extra_port=ldap_backend_extra_port,
1267 ldap_dryrun_mode=ldap_dryrun_mode,
1268 root=root,
1269 setup_ds_path=setup_ds_path)
1270 elif backend_type == "openldap":
1271 provision_backend = OpenLDAPBackend(backend_type,
1272 paths=paths, setup_path=setup_path,
1273 lp=lp, credentials=credentials,
1274 names=names,
1275 message=message,
1276 domainsid=domainsid,
1277 schema=schema,
1278 hostname=hostname,
1279 ldapadminpass=ldapadminpass,
1280 slapd_path=slapd_path,
1281 ldap_backend_extra_port=ldap_backend_extra_port,
1282 ldap_dryrun_mode=ldap_dryrun_mode,
1283 ol_mmr_urls=ol_mmr_urls,
1284 nosync=nosync)
1285 else:
1286 raise ValueError("Unknown LDAP backend type selected")
1288 provision_backend.init()
1289 provision_backend.start()
1291 # only install a new shares config db if there is none
1292 if not os.path.exists(paths.shareconf):
1293 message("Setting up share.ldb")
1294 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1295 lp=lp)
1296 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1299 message("Setting up secrets.ldb")
1300 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1301 session_info=session_info,
1302 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1304 message("Setting up the registry")
1305 setup_registry(paths.hklm, setup_path, session_info,
1306 lp=lp)
1308 message("Setting up the privileges database")
1309 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1311 message("Setting up idmap db")
1312 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1313 lp=lp)
1315 message("Setting up SAM db")
1316 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1317 provision_backend, lp, names,
1318 message,
1319 domainsid=domainsid,
1320 schema=schema, domainguid=domainguid,
1321 policyguid=policyguid, policyguid_dc=policyguid_dc,
1322 fill=samdb_fill,
1323 adminpass=adminpass, krbtgtpass=krbtgtpass,
1324 invocationid=invocationid,
1325 machinepass=machinepass, dnspass=dnspass,
1326 ntdsguid=ntdsguid, serverrole=serverrole,
1327 dom_for_fun_level=dom_for_fun_level)
1329 if serverrole == "domain controller":
1330 if paths.netlogon is None:
1331 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1332 message("Please either remove %s or see the template at %s" %
1333 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1334 assert paths.netlogon is not None
1336 if paths.sysvol is None:
1337 message("Existing smb.conf does not have a [sysvol] share, but you"
1338 " are configuring a DC.")
1339 message("Please either remove %s or see the template at %s" %
1340 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1341 assert paths.sysvol is not None
1343 if not os.path.isdir(paths.netlogon):
1344 os.makedirs(paths.netlogon, 0755)
1346 if samdb_fill == FILL_FULL:
1347 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1348 root_uid=root_uid, nobody_uid=nobody_uid,
1349 users_gid=users_gid, wheel_gid=wheel_gid)
1351 if serverrole == "domain controller":
1352 # Set up group policies (domain policy and domain controller policy)
1353 setup_gpo(paths.sysvol, names.dnsdomain, policyguid, policyguid_dc)
1354 setsysvolacl(samdb, paths.netlogon, paths.sysvol, wheel_gid,
1355 domainsid, names.dnsdomain, names.domaindn, lp)
1357 message("Setting up sam.ldb rootDSE marking as synchronized")
1358 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1360 secretsdb_self_join(secrets_ldb, domain=names.domain,
1361 realm=names.realm,
1362 dnsdomain=names.dnsdomain,
1363 netbiosname=names.netbiosname,
1364 domainsid=domainsid,
1365 machinepass=machinepass,
1366 secure_channel_type=SEC_CHAN_BDC)
1368 if serverrole == "domain controller":
1369 secretsdb_setup_dns(secrets_ldb, setup_path,
1370 paths.private_dir,
1371 realm=names.realm, dnsdomain=names.dnsdomain,
1372 dns_keytab_path=paths.dns_keytab,
1373 dnspass=dnspass)
1375 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1376 assert isinstance(domainguid, str)
1378 # Only make a zone file on the first DC, it should be replicated
1379 # with DNS replication
1380 create_zone_file(lp, message, paths, targetdir, setup_path,
1381 dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
1382 hostname=names.hostname, realm=names.realm,
1383 domainguid=domainguid, ntdsguid=names.ntdsguid)
1385 create_named_conf(paths, setup_path, realm=names.realm,
1386 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1388 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1389 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1390 keytab_name=paths.dns_keytab)
1391 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1392 message("and %s for further documentation required for secure DNS "
1393 "updates" % paths.namedtxt)
1395 create_krb5_conf(paths.krb5conf, setup_path,
1396 dnsdomain=names.dnsdomain, hostname=names.hostname,
1397 realm=names.realm)
1398 message("A Kerberos configuration suitable for Samba 4 has been "
1399 "generated at %s" % paths.krb5conf)
1401 if serverrole == "domain controller":
1402 create_dns_update_list(lp, message, paths, setup_path)
1404 provision_backend.post_setup()
1405 provision_backend.shutdown()
1407 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1408 ldapi_url)
1410 #Now commit the secrets.ldb to disk
1411 secrets_ldb.transaction_commit()
1413 # the commit creates the dns.keytab, now chown it
1414 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1415 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1416 try:
1417 os.chmod(dns_keytab_path, 0640)
1418 os.chown(dns_keytab_path, -1, paths.bind_gid)
1419 except OSError:
1420 message("Failed to chown %s to bind gid %u" % (dns_keytab_path,
1421 paths.bind_gid))
1424 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1426 message("Once the above files are installed, your Samba4 server will be ready to use")
1427 message("Server Role: %s" % serverrole)
1428 message("Hostname: %s" % names.hostname)
1429 message("NetBIOS Domain: %s" % names.domain)
1430 message("DNS Domain: %s" % names.dnsdomain)
1431 message("DOMAIN SID: %s" % str(domainsid))
1432 if samdb_fill == FILL_FULL:
1433 message("Admin password: %s" % adminpass)
1434 if provision_backend.type is not "ldb":
1435 if provision_backend.credentials.get_bind_dn() is not None:
1436 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1437 else:
1438 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1440 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1442 if provision_backend.slapd_command_escaped is not None:
1443 # now display slapd_command_file.txt to show how slapd must be started next time
1444 message("Use later the following commandline to start slapd, then Samba:")
1445 message(provision_backend.slapd_command_escaped)
1446 message("This slapd-Commandline is also stored under: " + provision_backend.ldapdir + "/ldap_backend_startup.sh")
1449 result = ProvisionResult()
1450 result.domaindn = domaindn
1451 result.paths = paths
1452 result.lp = lp
1453 result.samdb = samdb
1454 return result
1457 def provision_become_dc(setup_dir=None,
1458 smbconf=None, targetdir=None, realm=None,
1459 rootdn=None, domaindn=None, schemadn=None,
1460 configdn=None, serverdn=None,
1461 domain=None, hostname=None, domainsid=None,
1462 adminpass=None, krbtgtpass=None, domainguid=None,
1463 policyguid=None, policyguid_dc=None, invocationid=None,
1464 machinepass=None,
1465 dnspass=None, root=None, nobody=None, users=None,
1466 wheel=None, backup=None, serverrole=None,
1467 ldap_backend=None, ldap_backend_type=None,
1468 sitename=None, debuglevel=1):
1470 def message(text):
1471 """print a message if quiet is not set."""
1472 print text
1474 samba.set_debug_level(debuglevel)
1476 return provision(setup_dir, message, system_session(), None,
1477 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1478 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1479 configdn=configdn, serverdn=serverdn, domain=domain,
1480 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1481 machinepass=machinepass, serverrole="domain controller",
1482 sitename=sitename)
1485 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1486 """Create a PHP LDAP admin configuration file.
1488 :param path: Path to write the configuration to.
1489 :param setup_path: Function to generate setup paths.
1491 setup_file(setup_path("phpldapadmin-config.php"), path,
1492 {"S4_LDAPI_URI": ldapi_uri})
1495 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1496 hostip, hostip6, hostname, realm, domainguid,
1497 ntdsguid):
1498 """Write out a DNS zone file, from the info in the current database.
1500 :param paths: paths object
1501 :param setup_path: Setup path function.
1502 :param dnsdomain: DNS Domain name
1503 :param domaindn: DN of the Domain
1504 :param hostip: Local IPv4 IP
1505 :param hostip6: Local IPv6 IP
1506 :param hostname: Local hostname
1507 :param realm: Realm name
1508 :param domainguid: GUID of the domain.
1509 :param ntdsguid: GUID of the hosts nTDSDSA record.
1511 assert isinstance(domainguid, str)
1513 if hostip6 is not None:
1514 hostip6_base_line = " IN AAAA " + hostip6
1515 hostip6_host_line = hostname + " IN AAAA " + hostip6
1516 else:
1517 hostip6_base_line = ""
1518 hostip6_host_line = ""
1520 if hostip is not None:
1521 hostip_base_line = " IN A " + hostip
1522 hostip_host_line = hostname + " IN A " + hostip
1523 else:
1524 hostip_base_line = ""
1525 hostip_host_line = ""
1527 dns_dir = os.path.dirname(paths.dns)
1529 try:
1530 shutil.rmtree(dns_dir, True)
1531 except OSError:
1532 pass
1534 os.mkdir(dns_dir, 0775)
1536 # we need to freeze the zone while we update the contents
1537 if targetdir is None:
1538 rndc = ' '.join(lp.get("rndc command"))
1539 os.system(rndc + " freeze " + lp.get("realm"))
1541 setup_file(setup_path("provision.zone"), paths.dns, {
1542 "HOSTNAME": hostname,
1543 "DNSDOMAIN": dnsdomain,
1544 "REALM": realm,
1545 "HOSTIP_BASE_LINE": hostip_base_line,
1546 "HOSTIP_HOST_LINE": hostip_host_line,
1547 "DOMAINGUID": domainguid,
1548 "DATESTRING": time.strftime("%Y%m%d%H"),
1549 "DEFAULTSITE": DEFAULTSITE,
1550 "NTDSGUID": ntdsguid,
1551 "HOSTIP6_BASE_LINE": hostip6_base_line,
1552 "HOSTIP6_HOST_LINE": hostip6_host_line,
1555 # note that we use no variable substitution on this file
1556 # the substitution is done at runtime by samba_dnsupdate
1557 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1559 if paths.bind_gid is not None:
1560 try:
1561 os.chown(dns_dir, -1, paths.bind_gid)
1562 os.chown(paths.dns, -1, paths.bind_gid)
1563 # chmod needed to cope with umask
1564 os.chmod(dns_dir, 0775)
1565 os.chmod(paths.dns, 0664)
1566 except OSError:
1567 message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1569 if targetdir is None:
1570 os.system(rndc + " unfreeze " + lp.get("realm"))
1573 def create_dns_update_list(lp, message, paths, setup_path):
1574 """Write out a dns_update_list file"""
1575 # note that we use no variable substitution on this file
1576 # the substitution is done at runtime by samba_dnsupdate
1577 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1580 def create_named_conf(paths, setup_path, realm, dnsdomain,
1581 private_dir):
1582 """Write out a file containing zone statements suitable for inclusion in a
1583 named.conf file (including GSS-TSIG configuration).
1585 :param paths: all paths
1586 :param setup_path: Setup path function.
1587 :param realm: Realm name
1588 :param dnsdomain: DNS Domain name
1589 :param private_dir: Path to private directory
1590 :param keytab_name: File name of DNS keytab file
1593 setup_file(setup_path("named.conf"), paths.namedconf, {
1594 "DNSDOMAIN": dnsdomain,
1595 "REALM": realm,
1596 "ZONE_FILE": paths.dns,
1597 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1598 "NAMED_CONF": paths.namedconf,
1599 "NAMED_CONF_UPDATE": paths.namedconf_update
1602 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1604 def create_named_txt(path, setup_path, realm, dnsdomain,
1605 private_dir, keytab_name):
1606 """Write out a file containing zone statements suitable for inclusion in a
1607 named.conf file (including GSS-TSIG configuration).
1609 :param path: Path of the new named.conf file.
1610 :param setup_path: Setup path function.
1611 :param realm: Realm name
1612 :param dnsdomain: DNS Domain name
1613 :param private_dir: Path to private directory
1614 :param keytab_name: File name of DNS keytab file
1617 setup_file(setup_path("named.txt"), path, {
1618 "DNSDOMAIN": dnsdomain,
1619 "REALM": realm,
1620 "DNS_KEYTAB": keytab_name,
1621 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1622 "PRIVATE_DIR": private_dir
1625 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1626 """Write out a file containing zone statements suitable for inclusion in a
1627 named.conf file (including GSS-TSIG configuration).
1629 :param path: Path of the new named.conf file.
1630 :param setup_path: Setup path function.
1631 :param dnsdomain: DNS Domain name
1632 :param hostname: Local hostname
1633 :param realm: Realm name
1635 setup_file(setup_path("krb5.conf"), path, {
1636 "DNSDOMAIN": dnsdomain,
1637 "HOSTNAME": hostname,
1638 "REALM": realm,
1642 class ProvisioningError(Exception):
1643 """A generic provision error."""
1645 def __init__(self, value):
1646 self.value = value
1648 def __str__(self):
1649 return "ProvisioningError: " + self.value
1652 class InvalidNetbiosName(Exception):
1653 """A specified name was not a valid NetBIOS name."""
1654 def __init__(self, name):
1655 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)