s4:samba_dnsupdate Add a 'file based' mode to samba_dnsupdate
[Samba/kamenim.git] / source4 / scripting / python / samba / provision.py
blobbac234cfac6fbf743c0892e4717707bad80645ad
2 # Unix SMB/CIFS implementation.
3 # backend code for provisioning a Samba4 server
5 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
6 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
7 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
9 # Based on the original in EJS:
10 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 3 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 """Functions for setting up a Samba configuration."""
28 from base64 import b64encode
29 import os
30 import pwd
31 import grp
32 import time
33 import uuid
34 import socket
35 import param
36 import registry
37 import urllib
38 import shutil
40 import ldb
42 from samba.auth import system_session, admin_session
43 from samba import glue, version, Ldb, substitute_var, valid_netbios_name
44 from samba import check_all_substituted, read_and_sub_file, setup_file
45 from samba import DS_DOMAIN_FUNCTION_2003, DS_DC_FUNCTION_2008
46 from samba.dcerpc import security
47 from samba.dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
48 from samba.idmap import IDmapDB
49 from samba.ntacls import setntacl, dsacl2fsacl
50 from samba.ndr import ndr_pack,ndr_unpack
51 from samba.schema import Schema
52 from ms_display_specifiers import read_ms_ldif
53 from samba.provisionbackend import LDBBackend, ExistingBackend, FDSBackend, OpenLDAPBackend
54 from provisionexceptions import ProvisioningError, InvalidNetbiosName
56 __docformat__ = "restructuredText"
58 def find_setup_dir():
59 """Find the setup directory used by provision."""
60 dirname = os.path.dirname(__file__)
61 if "/site-packages/" in dirname:
62 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
63 for suffix in ["share/setup", "share/samba/setup", "setup"]:
64 ret = os.path.join(prefix, suffix)
65 if os.path.isdir(ret):
66 return ret
67 # In source tree
68 ret = os.path.join(dirname, "../../../setup")
69 if os.path.isdir(ret):
70 return ret
71 raise Exception("Unable to find setup directory.")
73 # descriptors of the naming contexts
74 # hard coded at this point, but will probably be changed when
75 # we enable different fsmo roles
77 def get_config_descriptor(domain_sid):
78 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
79 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
81 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
83 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
84 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
85 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
86 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
87 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
88 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
89 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
90 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
91 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
92 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
93 sec = security.descriptor.from_sddl(sddl, domain_sid)
94 return ndr_pack(sec)
96 def get_domain_descriptor(domain_sid):
97 sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
98 "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
99 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
100 "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
101 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
102 "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
103 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
104 "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
105 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
106 "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
107 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
108 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
109 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
110 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
111 "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
112 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
113 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
114 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
115 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
116 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
117 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
118 "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
119 "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
120 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
121 "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
122 "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
123 "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
124 "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
125 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
126 "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
127 "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
128 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
129 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
130 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
131 "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
132 "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
133 "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
134 "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
135 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
136 "(A;;RPRC;;;RU)" \
137 "(A;CI;LC;;;RU)" \
138 "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
139 "(A;;RP;;;WD)" \
140 "(A;;RPLCLORC;;;ED)" \
141 "(A;;RPLCLORC;;;AU)" \
142 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
143 "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
144 "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
145 "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
146 sec = security.descriptor.from_sddl(sddl, domain_sid)
147 return ndr_pack(sec)
149 DEFAULTSITE = "Default-First-Site-Name"
151 # Exception classes
153 class ProvisionPaths(object):
154 def __init__(self):
155 self.shareconf = None
156 self.hklm = None
157 self.hkcu = None
158 self.hkcr = None
159 self.hku = None
160 self.hkpd = None
161 self.hkpt = None
162 self.samdb = None
163 self.idmapdb = None
164 self.secrets = None
165 self.keytab = None
166 self.dns_keytab = None
167 self.dns = None
168 self.winsdb = None
169 self.private_dir = None
172 class ProvisionNames(object):
173 def __init__(self):
174 self.rootdn = None
175 self.domaindn = None
176 self.configdn = None
177 self.schemadn = None
178 self.ldapmanagerdn = None
179 self.dnsdomain = None
180 self.realm = None
181 self.netbiosname = None
182 self.domain = None
183 self.hostname = None
184 self.sitename = None
185 self.smbconf = None
188 class ProvisionResult(object):
189 def __init__(self):
190 self.paths = None
191 self.domaindn = None
192 self.lp = None
193 self.samdb = None
195 def check_install(lp, session_info, credentials):
196 """Check whether the current install seems ok.
198 :param lp: Loadparm context
199 :param session_info: Session information
200 :param credentials: Credentials
202 if lp.get("realm") == "":
203 raise Exception("Realm empty")
204 ldb = Ldb(lp.get("sam database"), session_info=session_info,
205 credentials=credentials, lp=lp)
206 if len(ldb.search("(cn=Administrator)")) != 1:
207 raise ProvisioningError("No administrator account found")
210 def findnss(nssfn, names):
211 """Find a user or group from a list of possibilities.
213 :param nssfn: NSS Function to try (should raise KeyError if not found)
214 :param names: Names to check.
215 :return: Value return by first names list.
217 for name in names:
218 try:
219 return nssfn(name)
220 except KeyError:
221 pass
222 raise KeyError("Unable to find user/group %r" % names)
225 findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
226 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
229 def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
230 """Setup a ldb in the private dir.
232 :param ldb: LDB file to import data into
233 :param ldif_path: Path of the LDIF file to load
234 :param subst_vars: Optional variables to subsitute in LDIF.
235 :param nocontrols: Optional list of controls, can be None for no controls
237 assert isinstance(ldif_path, str)
238 data = read_and_sub_file(ldif_path, subst_vars)
239 ldb.add_ldif(data,controls)
242 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
243 """Modify a ldb in the private dir.
245 :param ldb: LDB object.
246 :param ldif_path: LDIF file path.
247 :param subst_vars: Optional dictionary with substitution variables.
249 data = read_and_sub_file(ldif_path, subst_vars)
251 ldb.modify_ldif(data)
254 def setup_ldb(ldb, ldif_path, subst_vars):
255 """Import a LDIF a file into a LDB handle, optionally substituting variables.
257 :note: Either all LDIF data will be added or none (using transactions).
259 :param ldb: LDB file to import into.
260 :param ldif_path: Path to the LDIF file.
261 :param subst_vars: Dictionary with substitution variables.
263 assert ldb is not None
264 ldb.transaction_start()
265 try:
266 setup_add_ldif(ldb, ldif_path, subst_vars)
267 except:
268 ldb.transaction_cancel()
269 raise
270 ldb.transaction_commit()
273 def provision_paths_from_lp(lp, dnsdomain):
274 """Set the default paths for provisioning.
276 :param lp: Loadparm context.
277 :param dnsdomain: DNS Domain name
279 paths = ProvisionPaths()
280 paths.private_dir = lp.get("private dir")
281 paths.dns_keytab = "dns.keytab"
283 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
284 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
285 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
286 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
287 paths.privilege = os.path.join(paths.private_dir, "privilege.ldb")
288 paths.dns = os.path.join(paths.private_dir, "dns", dnsdomain + ".zone")
289 paths.dns_update_list = os.path.join(paths.private_dir, "dns_update_list")
290 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
291 paths.namedconf_update = os.path.join(paths.private_dir, "named.conf.update")
292 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
293 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
294 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
295 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
296 paths.phpldapadminconfig = os.path.join(paths.private_dir,
297 "phpldapadmin-config.php")
298 paths.hklm = "hklm.ldb"
299 paths.hkcr = "hkcr.ldb"
300 paths.hkcu = "hkcu.ldb"
301 paths.hku = "hku.ldb"
302 paths.hkpd = "hkpd.ldb"
303 paths.hkpt = "hkpt.ldb"
305 paths.sysvol = lp.get("path", "sysvol")
307 paths.netlogon = lp.get("path", "netlogon")
309 paths.smbconf = lp.configfile
311 return paths
314 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
315 serverrole=None, rootdn=None, domaindn=None, configdn=None,
316 schemadn=None, serverdn=None, sitename=None):
317 """Guess configuration settings to use."""
319 if hostname is None:
320 hostname = socket.gethostname().split(".")[0]
322 netbiosname = lp.get("netbios name")
323 if netbiosname is None:
324 netbiosname = hostname
325 assert netbiosname is not None
326 netbiosname = netbiosname.upper()
327 if not valid_netbios_name(netbiosname):
328 raise InvalidNetbiosName(netbiosname)
330 if dnsdomain is None:
331 dnsdomain = lp.get("realm")
332 if dnsdomain is None or dnsdomain == "":
333 raise ProvisioningError("guess_names: 'realm' not specified in supplied smb.conf!")
335 dnsdomain = dnsdomain.lower()
337 if serverrole is None:
338 serverrole = lp.get("server role")
339 if serverrole is None:
340 raise ProvisioningError("guess_names: 'server role' not specified in supplied smb.conf!")
342 serverrole = serverrole.lower()
344 realm = dnsdomain.upper()
346 if lp.get("realm").upper() != realm:
347 raise ProvisioningError("guess_names: Realm '%s' in smb.conf must match chosen realm '%s'!", lp.get("realm").upper(), realm)
349 if lp.get("server role").lower() != serverrole:
350 raise ProvisioningError("guess_names: server role '%s' in smb.conf must match chosen server role '%s'!", lp.get("server role").upper(), serverrole)
352 if serverrole == "domain controller":
353 if domain is None:
354 domain = lp.get("workgroup")
355 if domain is None:
356 raise ProvisioningError("guess_names: 'workgroup' not specified in supplied smb.conf!")
357 domain = domain.upper()
359 if lp.get("workgroup").upper() != domain:
360 raise ProvisioningError("guess_names: Workgroup '%s' in smb.conf must match chosen domain '%s'!", lp.get("workgroup").upper(), domain)
362 if domaindn is None:
363 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
364 else:
365 domain = netbiosname
366 if domaindn is None:
367 domaindn = "DC=" + netbiosname
369 if not valid_netbios_name(domain):
370 raise InvalidNetbiosName(domain)
372 if hostname.upper() == realm:
373 raise ProvisioningError("guess_names: Realm '%s' must not be equal to hostname '%s'!", realm, hostname)
374 if netbiosname == realm:
375 raise ProvisioningError("guess_names: Realm '%s' must not be equal to netbios hostname '%s'!", realm, netbiosname)
376 if domain == realm:
377 raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!", realm, domain)
379 if rootdn is None:
380 rootdn = domaindn
382 if configdn is None:
383 configdn = "CN=Configuration," + rootdn
384 if schemadn is None:
385 schemadn = "CN=Schema," + configdn
387 if sitename is None:
388 sitename=DEFAULTSITE
390 names = ProvisionNames()
391 names.rootdn = rootdn
392 names.domaindn = domaindn
393 names.configdn = configdn
394 names.schemadn = schemadn
395 names.ldapmanagerdn = "CN=Manager," + rootdn
396 names.dnsdomain = dnsdomain
397 names.domain = domain
398 names.realm = realm
399 names.netbiosname = netbiosname
400 names.hostname = hostname
401 names.sitename = sitename
402 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
404 return names
407 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
408 targetdir, sid_generator,eadb):
409 """Create a new smb.conf file based on a couple of basic settings.
411 assert smbconf is not None
412 if hostname is None:
413 hostname = socket.gethostname().split(".")[0]
414 netbiosname = hostname.upper()
416 if serverrole is None:
417 serverrole = "standalone"
419 assert serverrole in ("domain controller", "member server", "standalone")
420 if serverrole == "domain controller":
421 smbconfsuffix = "dc"
422 elif serverrole == "member server":
423 smbconfsuffix = "member"
424 elif serverrole == "standalone":
425 smbconfsuffix = "standalone"
427 if sid_generator is None:
428 sid_generator = "internal"
430 assert domain is not None
431 domain = domain.upper()
433 assert realm is not None
434 realm = realm.upper()
436 default_lp = param.LoadParm()
437 #Load non-existant file
438 if os.path.exists(smbconf):
439 default_lp.load(smbconf)
440 if eadb:
441 if targetdir is not None:
442 privdir = os.path.join(targetdir, "private")
443 else:
444 privdir = default_lp.get("private dir")
445 posixeadb_line = "posix:eadb = " + os.path.abspath(os.path.join(privdir,"eadb.tdb"))
446 else:
447 posixeadb_line = ""
449 if targetdir is not None:
450 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
451 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
453 default_lp.set("lock dir", os.path.abspath(targetdir))
454 else:
455 privatedir_line = ""
456 lockdir_line = ""
458 if sid_generator == "internal":
459 sid_generator_line = ""
460 else:
461 sid_generator_line = "sid generator = " + sid_generator
463 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
464 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
466 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
467 smbconf, {
468 "NETBIOS_NAME": netbiosname,
469 "DOMAIN": domain,
470 "REALM": realm,
471 "SERVERROLE": serverrole,
472 "NETLOGONPATH": netlogon,
473 "SYSVOLPATH": sysvol,
474 "SIDGENERATOR_LINE": sid_generator_line,
475 "PRIVATEDIR_LINE": privatedir_line,
476 "LOCKDIR_LINE": lockdir_line,
477 "POSIXEADB_LINE": posixeadb_line
481 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
482 users_gid, wheel_gid):
483 """setup reasonable name mappings for sam names to unix names.
485 :param samdb: SamDB object.
486 :param idmap: IDmap db object.
487 :param sid: The domain sid.
488 :param domaindn: The domain DN.
489 :param root_uid: uid of the UNIX root user.
490 :param nobody_uid: uid of the UNIX nobody user.
491 :param users_gid: gid of the UNIX users group.
492 :param wheel_gid: gid of the UNIX wheel group."""
494 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
495 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
497 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
498 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
500 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
501 provision_backend, names, schema,
502 serverrole,
503 erase=False):
504 """Setup the partitions for the SAM database.
506 Alternatively, provision() may call this, and then populate the database.
508 :note: This will wipe the Sam Database!
510 :note: This function always removes the local SAM LDB file. The erase
511 parameter controls whether to erase the existing data, which
512 may not be stored locally but in LDAP.
515 assert session_info is not None
517 # We use options=["modules:"] to stop the modules loading - we
518 # just want to wipe and re-initialise the database, not start it up
520 try:
521 os.unlink(samdb_path)
522 except OSError:
523 pass
525 samdb = Ldb(url=samdb_path, session_info=session_info,
526 lp=lp, options=["modules:"])
528 ldap_backend_line = "# No LDAP backend"
529 if provision_backend.type is not "ldb":
530 ldap_backend_line = "ldapBackend: %s" % provision_backend.ldapi_uri
532 samdb.transaction_start()
533 try:
534 message("Setting up sam.ldb partitions and settings")
535 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
536 "SCHEMADN": ldb.Dn(schema.ldb, names.schemadn).get_casefold(),
537 "CONFIGDN": ldb.Dn(schema.ldb, names.configdn).get_casefold(),
538 "DOMAINDN": ldb.Dn(schema.ldb, names.domaindn).get_casefold(),
539 "LDAP_BACKEND_LINE": ldap_backend_line,
543 setup_add_ldif(samdb, setup_path("provision_init.ldif"), {
544 "BACKEND_TYPE": provision_backend.type,
545 "SERVER_ROLE": serverrole
548 message("Setting up sam.ldb rootDSE")
549 setup_samdb_rootdse(samdb, setup_path, names)
551 except:
552 samdb.transaction_cancel()
553 raise
555 samdb.transaction_commit()
558 def secretsdb_self_join(secretsdb, domain,
559 netbiosname, machinepass, domainsid=None,
560 realm=None, dnsdomain=None,
561 keytab_path=None,
562 key_version_number=1,
563 secure_channel_type=SEC_CHAN_WKSTA):
564 """Add domain join-specific bits to a secrets database.
566 :param secretsdb: Ldb Handle to the secrets database
567 :param machinepass: Machine password
569 attrs=["whenChanged",
570 "secret",
571 "priorSecret",
572 "priorChanged",
573 "krb5Keytab",
574 "privateKeytab"]
577 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain))
578 msg["secureChannelType"] = str(secure_channel_type)
579 msg["flatname"] = [domain]
580 msg["objectClass"] = ["top", "primaryDomain"]
581 if realm is not None:
582 if dnsdomain is None:
583 dnsdomain = realm.lower()
584 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
585 msg["realm"] = realm
586 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
587 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
588 msg["privateKeytab"] = ["secrets.keytab"]
591 msg["secret"] = [machinepass]
592 msg["samAccountName"] = ["%s$" % netbiosname]
593 msg["secureChannelType"] = [str(secure_channel_type)]
594 if domainsid is not None:
595 msg["objectSid"] = [ndr_pack(domainsid)]
597 res = secretsdb.search(base="cn=Primary Domains",
598 attrs=attrs,
599 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
600 scope=ldb.SCOPE_ONELEVEL)
602 for del_msg in res:
603 if del_msg.dn is not msg.dn:
604 secretsdb.delete(del_msg.dn)
606 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=ldb.SCOPE_BASE)
608 if len(res) == 1:
609 msg["priorSecret"] = res[0]["secret"]
610 msg["priorWhenChanged"] = res[0]["whenChanged"]
612 if res["privateKeytab"] is not None:
613 msg["privateKeytab"] = res[0]["privateKeytab"]
615 if res["krb5Keytab"] is not None:
616 msg["krb5Keytab"] = res[0]["krb5Keytab"]
618 for el in msg:
619 el.set_flags(ldb.FLAG_MOD_REPLACE)
620 secretsdb.modify(msg)
621 else:
622 secretsdb.add(msg)
625 def secretsdb_setup_dns(secretsdb, setup_path, private_dir,
626 realm, dnsdomain,
627 dns_keytab_path, dnspass):
628 """Add DNS specific bits to a secrets database.
630 :param secretsdb: Ldb Handle to the secrets database
631 :param setup_path: Setup path function
632 :param machinepass: Machine password
634 try:
635 os.unlink(os.path.join(private_dir, dns_keytab_path))
636 except OSError:
637 pass
639 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
640 "REALM": realm,
641 "DNSDOMAIN": dnsdomain,
642 "DNS_KEYTAB": dns_keytab_path,
643 "DNSPASS_B64": b64encode(dnspass),
647 def setup_secretsdb(path, setup_path, session_info, backend_credentials, lp):
648 """Setup the secrets database.
650 :param path: Path to the secrets database.
651 :param setup_path: Get the path to a setup file.
652 :param session_info: Session info.
653 :param credentials: Credentials
654 :param lp: Loadparm context
655 :return: LDB handle for the created secrets database
657 if os.path.exists(path):
658 os.unlink(path)
659 secrets_ldb = Ldb(path, session_info=session_info,
660 lp=lp)
661 secrets_ldb.erase()
662 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
663 secrets_ldb = Ldb(path, session_info=session_info,
664 lp=lp)
665 secrets_ldb.transaction_start()
666 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
668 if backend_credentials is not None and backend_credentials.authentication_requested():
669 if backend_credentials.get_bind_dn() is not None:
670 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
671 "LDAPMANAGERDN": backend_credentials.get_bind_dn(),
672 "LDAPMANAGERPASS_B64": b64encode(backend_credentials.get_password())
674 else:
675 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
676 "LDAPADMINUSER": backend_credentials.get_username(),
677 "LDAPADMINREALM": backend_credentials.get_realm(),
678 "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
681 return secrets_ldb
683 def setup_privileges(path, setup_path, session_info, lp):
684 """Setup the privileges database.
686 :param path: Path to the privileges database.
687 :param setup_path: Get the path to a setup file.
688 :param session_info: Session info.
689 :param credentials: Credentials
690 :param lp: Loadparm context
691 :return: LDB handle for the created secrets database
693 if os.path.exists(path):
694 os.unlink(path)
695 privilege_ldb = Ldb(path, session_info=session_info, lp=lp)
696 privilege_ldb.erase()
697 privilege_ldb.load_ldif_file_add(setup_path("provision_privilege.ldif"))
700 def setup_registry(path, setup_path, session_info, lp):
701 """Setup the registry.
703 :param path: Path to the registry database
704 :param setup_path: Function that returns the path to a setup.
705 :param session_info: Session information
706 :param credentials: Credentials
707 :param lp: Loadparm context
709 reg = registry.Registry()
710 hive = registry.open_ldb(path, session_info=session_info,
711 lp_ctx=lp)
712 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
713 provision_reg = setup_path("provision.reg")
714 assert os.path.exists(provision_reg)
715 reg.diff_apply(provision_reg)
718 def setup_idmapdb(path, setup_path, session_info, lp):
719 """Setup the idmap database.
721 :param path: path to the idmap database
722 :param setup_path: Function that returns a path to a setup file
723 :param session_info: Session information
724 :param credentials: Credentials
725 :param lp: Loadparm context
727 if os.path.exists(path):
728 os.unlink(path)
730 idmap_ldb = IDmapDB(path, session_info=session_info,
731 lp=lp)
733 idmap_ldb.erase()
734 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
735 return idmap_ldb
738 def setup_samdb_rootdse(samdb, setup_path, names):
739 """Setup the SamDB rootdse.
741 :param samdb: Sam Database handle
742 :param setup_path: Obtain setup path
744 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
745 "SCHEMADN": names.schemadn,
746 "NETBIOSNAME": names.netbiosname,
747 "DNSDOMAIN": names.dnsdomain,
748 "REALM": names.realm,
749 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
750 "DOMAINDN": names.domaindn,
751 "ROOTDN": names.rootdn,
752 "CONFIGDN": names.configdn,
753 "SERVERDN": names.serverdn,
757 def setup_self_join(samdb, names,
758 machinepass, dnspass,
759 domainsid, invocationid, setup_path,
760 policyguid, policyguid_dc, domainControllerFunctionality,
761 ntdsguid):
762 """Join a host to its own domain."""
763 assert isinstance(invocationid, str)
764 if ntdsguid is not None:
765 ntdsguid_line = "objectGUID: %s\n"%ntdsguid
766 else:
767 ntdsguid_line = ""
768 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
769 "CONFIGDN": names.configdn,
770 "SCHEMADN": names.schemadn,
771 "DOMAINDN": names.domaindn,
772 "SERVERDN": names.serverdn,
773 "INVOCATIONID": invocationid,
774 "NETBIOSNAME": names.netbiosname,
775 "DEFAULTSITE": names.sitename,
776 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
777 "MACHINEPASS_B64": b64encode(machinepass),
778 "REALM": names.realm,
779 "DOMAIN": names.domain,
780 "DOMAINSID": str(domainsid),
781 "DNSDOMAIN": names.dnsdomain,
782 "SAMBA_VERSION_STRING": version,
783 "NTDSGUID": ntdsguid_line,
784 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
786 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
787 "POLICYGUID": policyguid,
788 "POLICYGUID_DC": policyguid_dc,
789 "DNSDOMAIN": names.dnsdomain,
790 "DOMAINSID": str(domainsid),
791 "DOMAINDN": names.domaindn})
793 # add the NTDSGUID based SPNs
794 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
795 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
796 expression="", scope=ldb.SCOPE_BASE)
797 assert isinstance(names.ntdsguid, str)
799 # Setup fSMORoleOwner entries to point at the newly created DC entry
800 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
801 "DOMAIN": names.domain,
802 "DNSDOMAIN": names.dnsdomain,
803 "DOMAINDN": names.domaindn,
804 "CONFIGDN": names.configdn,
805 "SCHEMADN": names.schemadn,
806 "DEFAULTSITE": names.sitename,
807 "SERVERDN": names.serverdn,
808 "NETBIOSNAME": names.netbiosname,
809 "NTDSGUID": names.ntdsguid,
810 "DNSPASS_B64": b64encode(dnspass),
814 def setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid):
815 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
816 "{" + policyguid + "}")
817 os.makedirs(policy_path, 0755)
818 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
819 "[General]\r\nVersion=65543")
820 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
821 os.makedirs(os.path.join(policy_path, "USER"), 0755)
823 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
824 "{" + policyguid_dc + "}")
825 os.makedirs(policy_path_dc, 0755)
826 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
827 "[General]\r\nVersion=2")
828 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
829 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
832 def setup_samdb(path, setup_path, session_info, provision_backend, lp,
833 names, message,
834 domainsid, domainguid, policyguid, policyguid_dc,
835 fill, adminpass, krbtgtpass,
836 machinepass, invocationid, dnspass, ntdsguid,
837 serverrole, dom_for_fun_level=None,
838 schema=None):
839 """Setup a complete SAM Database.
841 :note: This will wipe the main SAM database file!
844 # ATTENTION: Do NOT change these default values without discussion with the
845 # team and/or release manager. They have a big impact on the whole program!
846 domainControllerFunctionality = DS_DC_FUNCTION_2008
848 if dom_for_fun_level is None:
849 dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
850 if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
851 message("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This is not recommended")
853 if dom_for_fun_level > domainControllerFunctionality:
854 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!")
856 domainFunctionality = dom_for_fun_level
857 forestFunctionality = dom_for_fun_level
859 # Also wipes the database
860 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
861 provision_backend=provision_backend, session_info=session_info,
862 names=names,
863 serverrole=serverrole, schema=schema)
865 if (schema == None):
866 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
868 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
869 samdb = Ldb(session_info=session_info,
870 credentials=provision_backend.credentials, lp=lp)
872 message("Pre-loading the Samba 4 and AD schema")
874 # Load the schema from the one we computed earlier
875 samdb.set_schema_from_ldb(schema.ldb)
877 # And now we can connect to the DB - the schema won't be loaded from the DB
878 samdb.connect(path)
880 if fill == FILL_DRS:
881 return samdb
883 samdb.transaction_start()
884 try:
885 # Set the domain functionality levels onto the database.
886 # Various module (the password_hash module in particular) need
887 # to know what level of AD we are emulating.
889 # These will be fixed into the database via the database
890 # modifictions below, but we need them set from the start.
891 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
892 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
893 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
895 samdb.set_domain_sid(str(domainsid))
896 samdb.set_invocation_id(invocationid)
898 message("Adding DomainDN: %s" % names.domaindn)
900 #impersonate domain admin
901 admin_session_info = admin_session(lp, str(domainsid))
902 samdb.set_session_info(admin_session_info)
903 if domainguid is not None:
904 domainguid_line = "objectGUID: %s\n-" % domainguid
905 else:
906 domainguid_line = ""
908 descr = b64encode(get_domain_descriptor(domainsid))
909 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
910 "DOMAINDN": names.domaindn,
911 "DOMAINGUID": domainguid_line,
912 "DESCRIPTOR": descr
916 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
917 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
918 "DOMAINSID": str(domainsid),
919 "SCHEMADN": names.schemadn,
920 "NETBIOSNAME": names.netbiosname,
921 "DEFAULTSITE": names.sitename,
922 "CONFIGDN": names.configdn,
923 "SERVERDN": names.serverdn,
924 "POLICYGUID": policyguid,
925 "DOMAINDN": names.domaindn,
926 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
927 "SAMBA_VERSION_STRING": version
930 message("Adding configuration container")
931 descr = b64encode(get_config_descriptor(domainsid))
932 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
933 "CONFIGDN": names.configdn,
934 "DESCRIPTOR": descr,
937 # The LDIF here was created when the Schema object was constructed
938 message("Setting up sam.ldb schema")
939 samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
940 samdb.modify_ldif(schema.schema_dn_modify)
941 samdb.write_prefixes_from_schema()
942 samdb.add_ldif(schema.schema_data, controls=["relax:0"])
943 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
944 {"SCHEMADN": names.schemadn})
946 message("Reopening sam.ldb with new schema")
947 samdb.transaction_commit()
948 samdb = Ldb(session_info=admin_session_info,
949 credentials=provision_backend.credentials, lp=lp)
950 samdb.connect(path)
951 samdb.transaction_start()
952 samdb.set_invocation_id(invocationid)
954 message("Setting up sam.ldb configuration data")
955 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
956 "CONFIGDN": names.configdn,
957 "NETBIOSNAME": names.netbiosname,
958 "DEFAULTSITE": names.sitename,
959 "DNSDOMAIN": names.dnsdomain,
960 "DOMAIN": names.domain,
961 "SCHEMADN": names.schemadn,
962 "DOMAINDN": names.domaindn,
963 "SERVERDN": names.serverdn,
964 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
967 message("Setting up display specifiers")
968 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
969 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
970 check_all_substituted(display_specifiers_ldif)
971 samdb.add_ldif(display_specifiers_ldif)
973 message("Adding users container")
974 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
975 "DOMAINDN": names.domaindn})
976 message("Modifying users container")
977 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
978 "DOMAINDN": names.domaindn})
979 message("Adding computers container")
980 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
981 "DOMAINDN": names.domaindn})
982 message("Modifying computers container")
983 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
984 "DOMAINDN": names.domaindn})
985 message("Setting up sam.ldb data")
986 setup_add_ldif(samdb, setup_path("provision.ldif"), {
987 "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
988 "DOMAINDN": names.domaindn,
989 "NETBIOSNAME": names.netbiosname,
990 "DEFAULTSITE": names.sitename,
991 "CONFIGDN": names.configdn,
992 "SERVERDN": names.serverdn,
993 "POLICYGUID_DC": policyguid_dc
996 setup_modify_ldif(samdb, setup_path("provision_basedn_references.ldif"), {
997 "DOMAINDN": names.domaindn})
999 setup_modify_ldif(samdb, setup_path("provision_configuration_references.ldif"), {
1000 "CONFIGDN": names.configdn,
1001 "SCHEMADN": names.schemadn})
1002 if fill == FILL_FULL:
1003 message("Setting up sam.ldb users and groups")
1004 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1005 "DOMAINDN": names.domaindn,
1006 "DOMAINSID": str(domainsid),
1007 "CONFIGDN": names.configdn,
1008 "ADMINPASS_B64": b64encode(adminpass),
1009 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1012 message("Setting up self join")
1013 setup_self_join(samdb, names=names, invocationid=invocationid,
1014 dnspass=dnspass,
1015 machinepass=machinepass,
1016 domainsid=domainsid, policyguid=policyguid,
1017 policyguid_dc=policyguid_dc,
1018 setup_path=setup_path,
1019 domainControllerFunctionality=domainControllerFunctionality,
1020 ntdsguid=ntdsguid)
1022 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1023 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1024 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
1025 assert isinstance(names.ntdsguid, str)
1027 except:
1028 samdb.transaction_cancel()
1029 raise
1031 samdb.transaction_commit()
1032 return samdb
1035 FILL_FULL = "FULL"
1036 FILL_NT4SYNC = "NT4SYNC"
1037 FILL_DRS = "DRS"
1038 SYSVOL_ACL = "O:LAG:BAD:P(A;OICI;0x001f01ff;;;BA)(A;OICI;0x001200a9;;;SO)(A;OICI;0x001f01ff;;;SY)(A;OICI;0x001200a9;;;AU)"
1039 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)"
1041 def set_gpo_acl(path,acl,lp,domsid):
1042 setntacl(lp,path,acl,domsid)
1043 for root, dirs, files in os.walk(path, topdown=False):
1044 for name in files:
1045 setntacl(lp,os.path.join(root, name),acl,domsid)
1046 for name in dirs:
1047 setntacl(lp,os.path.join(root, name),acl,domsid)
1049 def setsysvolacl(samdb,names,netlogon,sysvol,gid,domainsid,lp):
1050 canchown = 1
1051 try:
1052 os.chown(sysvol,-1,gid)
1053 except:
1054 canchown = 0
1056 setntacl(lp,sysvol,SYSVOL_ACL,str(domainsid))
1057 for root, dirs, files in os.walk(sysvol, topdown=False):
1058 for name in files:
1059 if canchown:
1060 os.chown(os.path.join(root, name),-1,gid)
1061 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1062 for name in dirs:
1063 if canchown:
1064 os.chown(os.path.join(root, name),-1,gid)
1065 setntacl(lp,os.path.join(root, name),SYSVOL_ACL,str(domainsid))
1067 # Set ACL for GPO
1068 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies")
1069 set_gpo_acl(policy_path,dsacl2fsacl(POLICIES_ACL,str(domainsid)),lp,str(domainsid))
1070 res = samdb.search(base="CN=Policies,CN=System,%s"%(names.domaindn),
1071 attrs=["cn","nTSecurityDescriptor"],
1072 expression="", scope=ldb.SCOPE_ONELEVEL)
1073 for policy in res:
1074 acl = ndr_unpack(security.descriptor,str(policy["nTSecurityDescriptor"])).as_sddl()
1075 policy_path = os.path.join(sysvol, names.dnsdomain, "Policies",
1076 str(policy["cn"]))
1077 set_gpo_acl(policy_path,dsacl2fsacl(acl,str(domainsid)),lp,str(domainsid))
1081 def provision(setup_dir, message, session_info,
1082 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1083 realm=None,
1084 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1085 serverdn=None,
1086 domain=None, hostname=None, hostip=None, hostip6=None,
1087 domainsid=None, adminpass=None, ldapadminpass=None,
1088 krbtgtpass=None, domainguid=None,
1089 policyguid=None, policyguid_dc=None, invocationid=None,
1090 machinepass=None, ntdsguid=None,
1091 dnspass=None, root=None, nobody=None, users=None,
1092 wheel=None, backup=None, aci=None, serverrole=None,
1093 dom_for_fun_level=None,
1094 ldap_backend_extra_port=None, backend_type=None,
1095 sitename=None,
1096 ol_mmr_urls=None, ol_olc=None,
1097 setup_ds_path=None, slapd_path=None, nosync=False,
1098 ldap_dryrun_mode=False,useeadb=False):
1099 """Provision samba4
1101 :note: caution, this wipes all existing data!
1104 def setup_path(file):
1105 return os.path.join(setup_dir, file)
1107 if domainsid is None:
1108 domainsid = security.random_sid()
1109 else:
1110 domainsid = security.dom_sid(domainsid)
1112 # create/adapt the group policy GUIDs
1113 if policyguid is None:
1114 policyguid = str(uuid.uuid4())
1115 policyguid = policyguid.upper()
1116 if policyguid_dc is None:
1117 policyguid_dc = str(uuid.uuid4())
1118 policyguid_dc = policyguid_dc.upper()
1120 if adminpass is None:
1121 adminpass = glue.generate_random_password(12, 32)
1122 if krbtgtpass is None:
1123 krbtgtpass = glue.generate_random_password(128, 255)
1124 if machinepass is None:
1125 machinepass = glue.generate_random_password(128, 255)
1126 if dnspass is None:
1127 dnspass = glue.generate_random_password(128, 255)
1128 if ldapadminpass is None:
1129 #Make a new, random password between Samba and it's LDAP server
1130 ldapadminpass=glue.generate_random_password(128, 255)
1132 if backend_type is None:
1133 backend_type = "ldb"
1135 sid_generator = "internal"
1136 if backend_type == "fedora-ds":
1137 sid_generator = "backend"
1139 root_uid = findnss_uid([root or "root"])
1140 nobody_uid = findnss_uid([nobody or "nobody"])
1141 users_gid = findnss_gid([users or "users"])
1142 if wheel is None:
1143 wheel_gid = findnss_gid(["wheel", "adm"])
1144 else:
1145 wheel_gid = findnss_gid([wheel])
1146 try:
1147 bind_gid = findnss_gid(["bind", "named"])
1148 except KeyError:
1149 bind_gid = None
1151 if targetdir is not None:
1152 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1153 os.makedirs(os.path.join(targetdir, "etc"))
1154 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1155 elif smbconf is None:
1156 smbconf = param.default_path()
1158 # only install a new smb.conf if there isn't one there already
1159 if os.path.exists(smbconf):
1160 # if Samba Team members can't figure out the weird errors
1161 # loading an empty smb.conf gives, then we need to be smarter.
1162 # Pretend it just didn't exist --abartlet
1163 data = open(smbconf, 'r').read()
1164 data = data.lstrip()
1165 if data is None or data == "":
1166 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1167 targetdir, sid_generator, useeadb)
1168 else:
1169 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1170 targetdir, sid_generator, useeadb)
1172 lp = param.LoadParm()
1173 lp.load(smbconf)
1175 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1176 dnsdomain=realm, serverrole=serverrole,
1177 domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1178 serverdn=serverdn, sitename=sitename)
1180 paths = provision_paths_from_lp(lp, names.dnsdomain)
1182 paths.bind_gid = bind_gid
1184 if hostip is None:
1185 hostips = glue.interface_ips(lp, False)
1186 if len(hostips) == 0:
1187 message("No external IPv4 address has been found: I use the loopback.")
1188 hostip = '127.0.0.1'
1189 else:
1190 hostip = hostips[0]
1191 if len(hostips) > 1:
1192 message("More than one IPv4 address found: I use " + hostip + ".")
1194 if hostip6 is None:
1195 try:
1196 for ip in socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP):
1197 if hostip6 is None:
1198 hostip6 = ip[-1][0]
1199 if hostip6 == '::1' and ip[-1][0] != '::1':
1200 hostip6 = ip[-1][0]
1201 except socket.gaierror, (socket.EAI_NODATA, msg):
1202 hostip6 = None
1204 if serverrole is None:
1205 serverrole = lp.get("server role")
1207 assert serverrole in ("domain controller", "member server", "standalone")
1208 if invocationid is None:
1209 invocationid = str(uuid.uuid4())
1211 if not os.path.exists(paths.private_dir):
1212 os.mkdir(paths.private_dir)
1213 if not os.path.exists(os.path.join(paths.private_dir,"tls")):
1214 os.mkdir(os.path.join(paths.private_dir,"tls"))
1216 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1218 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn)
1220 if backend_type == "ldb":
1221 provision_backend = LDBBackend(backend_type,
1222 paths=paths, setup_path=setup_path,
1223 lp=lp, credentials=credentials,
1224 names=names,
1225 message=message)
1226 elif backend_type == "existing":
1227 provision_backend = ExistingBackend(backend_type,
1228 paths=paths, setup_path=setup_path,
1229 lp=lp, credentials=credentials,
1230 names=names,
1231 message=message,
1232 ldapi_url=ldapi_url)
1233 elif backend_type == "fedora-ds":
1234 provision_backend = FDSBackend(backend_type,
1235 paths=paths, setup_path=setup_path,
1236 lp=lp, credentials=credentials,
1237 names=names,
1238 message=message,
1239 domainsid=domainsid,
1240 schema=schema,
1241 hostname=hostname,
1242 ldapadminpass=ldapadminpass,
1243 slapd_path=slapd_path,
1244 ldap_backend_extra_port=ldap_backend_extra_port,
1245 ldap_dryrun_mode=ldap_dryrun_mode,
1246 root=root,
1247 setup_ds_path=setup_ds_path)
1248 elif backend_type == "openldap":
1249 provision_backend = OpenLDAPBackend(backend_type,
1250 paths=paths, setup_path=setup_path,
1251 lp=lp, credentials=credentials,
1252 names=names,
1253 message=message,
1254 domainsid=domainsid,
1255 schema=schema,
1256 hostname=hostname,
1257 ldapadminpass=ldapadminpass,
1258 slapd_path=slapd_path,
1259 ldap_backend_extra_port=ldap_backend_extra_port,
1260 ldap_dryrun_mode=ldap_dryrun_mode,
1261 ol_mmr_urls=ol_mmr_urls,
1262 nosync=nosync)
1263 else:
1264 raise ProvisioningError("Unknown LDAP backend type selected")
1266 provision_backend.init()
1267 provision_backend.start()
1269 # only install a new shares config db if there is none
1270 if not os.path.exists(paths.shareconf):
1271 message("Setting up share.ldb")
1272 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1273 lp=lp)
1274 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1277 message("Setting up secrets.ldb")
1278 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1279 session_info=session_info,
1280 backend_credentials=provision_backend.secrets_credentials, lp=lp)
1282 message("Setting up the registry")
1283 setup_registry(paths.hklm, setup_path, session_info,
1284 lp=lp)
1286 message("Setting up the privileges database")
1287 setup_privileges(paths.privilege, setup_path, session_info, lp=lp)
1289 message("Setting up idmap db")
1290 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1291 lp=lp)
1293 message("Setting up SAM db")
1294 samdb = setup_samdb(paths.samdb, setup_path, session_info,
1295 provision_backend, lp, names,
1296 message,
1297 domainsid=domainsid,
1298 schema=schema, domainguid=domainguid,
1299 policyguid=policyguid, policyguid_dc=policyguid_dc,
1300 fill=samdb_fill,
1301 adminpass=adminpass, krbtgtpass=krbtgtpass,
1302 invocationid=invocationid,
1303 machinepass=machinepass, dnspass=dnspass,
1304 ntdsguid=ntdsguid, serverrole=serverrole,
1305 dom_for_fun_level=dom_for_fun_level)
1307 if serverrole == "domain controller":
1308 if paths.netlogon is None:
1309 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1310 message("Please either remove %s or see the template at %s" %
1311 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1312 assert(paths.netlogon is not None)
1314 if paths.sysvol is None:
1315 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1316 message("Please either remove %s or see the template at %s" %
1317 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1318 assert(paths.sysvol is not None)
1321 if not os.path.isdir(paths.netlogon):
1322 os.makedirs(paths.netlogon, 0755)
1324 if samdb_fill == FILL_FULL:
1325 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1326 root_uid=root_uid, nobody_uid=nobody_uid,
1327 users_gid=users_gid, wheel_gid=wheel_gid)
1329 if serverrole == "domain controller":
1330 # Set up group policies (domain policy and domain controller policy)
1331 setup_gpo(paths,names,samdb,policyguid,policyguid_dc,domainsid)
1332 setsysvolacl(samdb,names,paths.netlogon,paths.sysvol,wheel_gid,domainsid,lp)
1334 message("Setting up sam.ldb rootDSE marking as synchronized")
1335 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1337 secretsdb_self_join(secrets_ldb, domain=names.domain,
1338 realm=names.realm,
1339 dnsdomain=names.dnsdomain,
1340 netbiosname=names.netbiosname,
1341 domainsid=domainsid,
1342 machinepass=machinepass,
1343 secure_channel_type=SEC_CHAN_BDC)
1345 if serverrole == "domain controller":
1346 secretsdb_setup_dns(secrets_ldb, setup_path,
1347 paths.private_dir,
1348 realm=names.realm, dnsdomain=names.dnsdomain,
1349 dns_keytab_path=paths.dns_keytab,
1350 dnspass=dnspass)
1352 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1353 assert isinstance(domainguid, str)
1355 # Only make a zone file on the first DC, it should be replicated
1356 # with DNS replication
1357 create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain=names.dnsdomain,
1358 hostip=hostip,
1359 hostip6=hostip6, hostname=names.hostname,
1360 realm=names.realm,
1361 domainguid=domainguid, ntdsguid=names.ntdsguid)
1363 create_named_conf(paths, setup_path, realm=names.realm,
1364 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1366 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1367 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1368 keytab_name=paths.dns_keytab)
1369 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1370 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1372 create_krb5_conf(paths.krb5conf, setup_path,
1373 dnsdomain=names.dnsdomain, hostname=names.hostname,
1374 realm=names.realm)
1375 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1377 if serverrole == "domain controller":
1378 create_dns_update_list(lp, message, paths, setup_path)
1380 provision_backend.post_setup()
1381 provision_backend.shutdown()
1383 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1384 ldapi_url)
1386 #Now commit the secrets.ldb to disk
1387 secrets_ldb.transaction_commit()
1389 # the commit creates the dns.keytab, now chown it
1390 dns_keytab_path = os.path.join(paths.private_dir, paths.dns_keytab)
1391 if (os.path.isfile(dns_keytab_path) and paths.bind_gid is not None):
1392 try:
1393 os.chmod(dns_keytab_path, 0640)
1394 os.chown(dns_keytab_path, -1, paths.bind_gid)
1395 except OSError:
1396 message("Failed to chown %s to bind gid %u" % (dns_keytab_path, paths.bind_gid))
1399 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1401 message("Once the above files are installed, your Samba4 server will be ready to use")
1402 message("Server Role: %s" % serverrole)
1403 message("Hostname: %s" % names.hostname)
1404 message("NetBIOS Domain: %s" % names.domain)
1405 message("DNS Domain: %s" % names.dnsdomain)
1406 message("DOMAIN SID: %s" % str(domainsid))
1407 if samdb_fill == FILL_FULL:
1408 message("Admin password: %s" % adminpass)
1409 if provision_backend.type is not "ldb":
1410 if provision_backend.credentials.get_bind_dn() is not None:
1411 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1412 else:
1413 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1415 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1417 if provision_backend.slapd_command_escaped is not None:
1418 # now display slapd_command_file.txt to show how slapd must be started next time
1419 message("Use later the following commandline to start slapd, then Samba:")
1420 message(provision_backend.slapd_command_escaped)
1421 message("This slapd-Commandline is also stored under: " + provision_backend.ldapdir + "/ldap_backend_startup.sh")
1424 result = ProvisionResult()
1425 result.domaindn = domaindn
1426 result.paths = paths
1427 result.lp = lp
1428 result.samdb = samdb
1429 return result
1433 def provision_become_dc(setup_dir=None,
1434 smbconf=None, targetdir=None, realm=None,
1435 rootdn=None, domaindn=None, schemadn=None,
1436 configdn=None, serverdn=None,
1437 domain=None, hostname=None, domainsid=None,
1438 adminpass=None, krbtgtpass=None, domainguid=None,
1439 policyguid=None, policyguid_dc=None, invocationid=None,
1440 machinepass=None,
1441 dnspass=None, root=None, nobody=None, users=None,
1442 wheel=None, backup=None, serverrole=None,
1443 ldap_backend=None, ldap_backend_type=None,
1444 sitename=None, debuglevel=1):
1446 def message(text):
1447 """print a message if quiet is not set."""
1448 print text
1450 glue.set_debug_level(debuglevel)
1452 return provision(setup_dir, message, system_session(), None,
1453 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1454 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1455 configdn=configdn, serverdn=serverdn, domain=domain,
1456 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1457 machinepass=machinepass, serverrole="domain controller",
1458 sitename=sitename)
1461 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1462 """Create a PHP LDAP admin configuration file.
1464 :param path: Path to write the configuration to.
1465 :param setup_path: Function to generate setup paths.
1467 setup_file(setup_path("phpldapadmin-config.php"), path,
1468 {"S4_LDAPI_URI": ldapi_uri})
1471 def create_zone_file(lp, message, paths, targetdir, setup_path, dnsdomain,
1472 hostip, hostip6, hostname, realm, domainguid,
1473 ntdsguid):
1474 """Write out a DNS zone file, from the info in the current database.
1476 :param paths: paths object
1477 :param setup_path: Setup path function.
1478 :param dnsdomain: DNS Domain name
1479 :param domaindn: DN of the Domain
1480 :param hostip: Local IPv4 IP
1481 :param hostip6: Local IPv6 IP
1482 :param hostname: Local hostname
1483 :param realm: Realm name
1484 :param domainguid: GUID of the domain.
1485 :param ntdsguid: GUID of the hosts nTDSDSA record.
1487 assert isinstance(domainguid, str)
1489 if hostip6 is not None:
1490 hostip6_base_line = " IN AAAA " + hostip6
1491 hostip6_host_line = hostname + " IN AAAA " + hostip6
1492 else:
1493 hostip6_base_line = ""
1494 hostip6_host_line = ""
1496 if hostip is not None:
1497 hostip_base_line = " IN A " + hostip
1498 hostip_host_line = hostname + " IN A " + hostip
1499 else:
1500 hostip_base_line = ""
1501 hostip_host_line = ""
1503 dns_dir = os.path.dirname(paths.dns)
1505 try:
1506 shutil.rmtree(dns_dir, True)
1507 except OSError:
1508 pass
1510 os.mkdir(dns_dir, 0775)
1512 # we need to freeze the zone while we update the contents
1513 if targetdir is None:
1514 rndc = ' '.join(lp.get("rndc command"))
1515 os.system(rndc + " freeze " + lp.get("realm"))
1517 setup_file(setup_path("provision.zone"), paths.dns, {
1518 "HOSTNAME": hostname,
1519 "DNSDOMAIN": dnsdomain,
1520 "REALM": realm,
1521 "HOSTIP_BASE_LINE": hostip_base_line,
1522 "HOSTIP_HOST_LINE": hostip_host_line,
1523 "DOMAINGUID": domainguid,
1524 "DATESTRING": time.strftime("%Y%m%d%H"),
1525 "DEFAULTSITE": DEFAULTSITE,
1526 "NTDSGUID": ntdsguid,
1527 "HOSTIP6_BASE_LINE": hostip6_base_line,
1528 "HOSTIP6_HOST_LINE": hostip6_host_line,
1531 # note that we use no variable substitution on this file
1532 # the substitution is done at runtime by samba_dnsupdate
1533 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1535 if paths.bind_gid is not None:
1536 try:
1537 os.chown(dns_dir, -1, paths.bind_gid)
1538 os.chown(paths.dns, -1, paths.bind_gid)
1539 # chmod needed to cope with umask
1540 os.chmod(dns_dir, 0775)
1541 os.chmod(paths.dns, 0664)
1542 except OSError:
1543 message("Failed to chown %s to bind gid %u" % (dns_dir, paths.bind_gid))
1545 if targetdir is None:
1546 os.system(rndc + " unfreeze " + lp.get("realm"))
1549 def create_dns_update_list(lp, message, paths, setup_path):
1550 """Write out a dns_update_list file"""
1551 # note that we use no variable substitution on this file
1552 # the substitution is done at runtime by samba_dnsupdate
1553 setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
1556 def create_named_conf(paths, setup_path, realm, dnsdomain,
1557 private_dir):
1558 """Write out a file containing zone statements suitable for inclusion in a
1559 named.conf file (including GSS-TSIG configuration).
1561 :param paths: all paths
1562 :param setup_path: Setup path function.
1563 :param realm: Realm name
1564 :param dnsdomain: DNS Domain name
1565 :param private_dir: Path to private directory
1566 :param keytab_name: File name of DNS keytab file
1569 setup_file(setup_path("named.conf"), paths.namedconf, {
1570 "DNSDOMAIN": dnsdomain,
1571 "REALM": realm,
1572 "ZONE_FILE": paths.dns,
1573 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
1574 "NAMED_CONF": paths.namedconf,
1575 "NAMED_CONF_UPDATE": paths.namedconf_update
1578 setup_file(setup_path("named.conf.update"), paths.namedconf_update)
1580 def create_named_txt(path, setup_path, realm, dnsdomain,
1581 private_dir, keytab_name):
1582 """Write out a file containing zone statements suitable for inclusion in a
1583 named.conf file (including GSS-TSIG configuration).
1585 :param path: Path of the new named.conf file.
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.txt"), path, {
1594 "DNSDOMAIN": dnsdomain,
1595 "REALM": realm,
1596 "DNS_KEYTAB": keytab_name,
1597 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
1598 "PRIVATE_DIR": private_dir
1601 def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
1602 """Write out a file containing zone statements suitable for inclusion in a
1603 named.conf file (including GSS-TSIG configuration).
1605 :param path: Path of the new named.conf file.
1606 :param setup_path: Setup path function.
1607 :param dnsdomain: DNS Domain name
1608 :param hostname: Local hostname
1609 :param realm: Realm name
1612 setup_file(setup_path("krb5.conf"), path, {
1613 "DNSDOMAIN": dnsdomain,
1614 "HOSTNAME": hostname,
1615 "REALM": realm,