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