source: vendor/current/source4/scripting/python/samba/provision.py@ 414

Last change on this file since 414 was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

File size: 83.8 KB
Line 
1#
2# Unix SMB/CIFS implementation.
3# backend code for provisioning a Samba4 server
4
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
8#
9# Based on the original in EJS:
10# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
11#
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.
16#
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.
21#
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/>.
24#
25
26"""Functions for setting up a Samba configuration."""
27
28from base64 import b64encode
29import os
30import sys
31import pwd
32import grp
33import time
34import uuid, glue
35import socket
36import param
37import registry
38import samba
39import subprocess
40import ldb
41
42import shutil
43from credentials import Credentials, DONT_USE_KERBEROS
44from auth import system_session, admin_session
45from samba import version, Ldb, substitute_var, valid_netbios_name
46from samba import check_all_substituted
47from samba import DS_DOMAIN_FUNCTION_2000, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008, DS_DC_FUNCTION_2008_R2
48from samba.samdb import SamDB
49from samba.idmap import IDmapDB
50from samba.dcerpc import security
51from samba.ndr import ndr_pack
52import urllib
53from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
54from ms_schema import read_ms_schema
55from ms_display_specifiers import read_ms_ldif
56from signal import SIGTERM
57from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
58
59__docformat__ = "restructuredText"
60
61def find_setup_dir():
62 """Find the setup directory used by provision."""
63 dirname = os.path.dirname(__file__)
64 if "/site-packages/" in dirname:
65 prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
66 for suffix in ["share/setup", "share/samba/setup", "setup"]:
67 ret = os.path.join(prefix, suffix)
68 if os.path.isdir(ret):
69 return ret
70 # In source tree
71 ret = os.path.join(dirname, "../../../setup")
72 if os.path.isdir(ret):
73 return ret
74 raise Exception("Unable to find setup directory.")
75
76def get_schema_descriptor(domain_sid):
77 sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \
78 "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
79 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
80 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
81 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
82 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
83 "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \
84 "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \
85 "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \
86 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
87 sec = security.descriptor.from_sddl(sddl, domain_sid)
88 return b64encode(ndr_pack(sec))
89
90def get_config_descriptor(domain_sid):
91 sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
92 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
93 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
94 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
95 "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
96 "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
97 "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
98 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
99 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
100 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
101 "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
102 "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
103 "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
104 "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
105 "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
106 sec = security.descriptor.from_sddl(sddl, domain_sid)
107 return b64encode(ndr_pack(sec))
108
109
110DEFAULTSITE = "Default-First-Site-Name"
111
112# Exception classes
113
114class ProvisioningError(Exception):
115 """A generic provision error."""
116
117class InvalidNetbiosName(Exception):
118 """A specified name was not a valid NetBIOS name."""
119 def __init__(self, name):
120 super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
121
122
123class ProvisionPaths(object):
124 def __init__(self):
125 self.shareconf = None
126 self.hklm = None
127 self.hkcu = None
128 self.hkcr = None
129 self.hku = None
130 self.hkpd = None
131 self.hkpt = None
132 self.samdb = None
133 self.idmapdb = None
134 self.secrets = None
135 self.keytab = None
136 self.dns_keytab = None
137 self.dns = None
138 self.winsdb = None
139 self.private_dir = None
140 self.ldapdir = None
141 self.slapdconf = None
142 self.modulesconf = None
143 self.memberofconf = None
144 self.fedoradsinf = None
145 self.fedoradspartitions = None
146 self.fedoradssasl = None
147 self.olmmron = None
148 self.olmmrserveridsconf = None
149 self.olmmrsyncreplconf = None
150 self.olcdir = None
151 self.olslapd = None
152 self.olcseedldif = None
153
154
155class ProvisionNames(object):
156 def __init__(self):
157 self.rootdn = None
158 self.domaindn = None
159 self.configdn = None
160 self.schemadn = None
161 self.sambadn = None
162 self.ldapmanagerdn = None
163 self.dnsdomain = None
164 self.realm = None
165 self.netbiosname = None
166 self.domain = None
167 self.hostname = None
168 self.sitename = None
169 self.smbconf = None
170
171
172class ProvisionResult(object):
173 def __init__(self):
174 self.paths = None
175 self.domaindn = None
176 self.lp = None
177 self.samdb = None
178
179class Schema(object):
180 def __init__(self, setup_path, domain_sid, schemadn=None,
181 serverdn=None, sambadn=None, ldap_backend_type=None):
182 """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
183
184 :param samdb: Load a schema into a SamDB.
185 :param setup_path: Setup path function.
186 :param schemadn: DN of the schema
187 :param serverdn: DN of the server
188
189 Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
190 """
191
192 self.ldb = Ldb()
193 self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
194 setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
195 self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
196 self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
197 check_all_substituted(self.schema_data)
198
199 self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
200 {"SCHEMADN": schemadn,
201 "SERVERDN": serverdn,
202 })
203
204 descr = get_schema_descriptor(domain_sid)
205 self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
206 {"SCHEMADN": schemadn,
207 "DESCRIPTOR": descr
208 })
209
210 prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
211 prefixmap = b64encode(prefixmap)
212
213 # We don't actually add this ldif, just parse it
214 prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
215 self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
216
217
218# Return a hash with the forward attribute as a key and the back as the value
219def get_linked_attributes(schemadn,schemaldb):
220 attrs = ["linkID", "lDAPDisplayName"]
221 res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
222 attributes = {}
223 for i in range (0, len(res)):
224 expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
225 target = schemaldb.searchone(basedn=schemadn,
226 expression=expression,
227 attribute="lDAPDisplayName",
228 scope=SCOPE_SUBTREE)
229 if target is not None:
230 attributes[str(res[i]["lDAPDisplayName"])]=str(target)
231
232 return attributes
233
234def get_dnsyntax_attributes(schemadn,schemaldb):
235 attrs = ["linkID", "lDAPDisplayName"]
236 res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
237 attributes = []
238 for i in range (0, len(res)):
239 attributes.append(str(res[i]["lDAPDisplayName"]))
240
241 return attributes
242
243
244def check_install(lp, session_info, credentials):
245 """Check whether the current install seems ok.
246
247 :param lp: Loadparm context
248 :param session_info: Session information
249 :param credentials: Credentials
250 """
251 if lp.get("realm") == "":
252 raise Exception("Realm empty")
253 ldb = Ldb(lp.get("sam database"), session_info=session_info,
254 credentials=credentials, lp=lp)
255 if len(ldb.search("(cn=Administrator)")) != 1:
256 raise ProvisioningError("No administrator account found")
257
258
259def findnss(nssfn, names):
260 """Find a user or group from a list of possibilities.
261
262 :param nssfn: NSS Function to try (should raise KeyError if not found)
263 :param names: Names to check.
264 :return: Value return by first names list.
265 """
266 for name in names:
267 try:
268 return nssfn(name)
269 except KeyError:
270 pass
271 raise KeyError("Unable to find user/group %r" % names)
272
273
274findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
275findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
276
277
278def read_and_sub_file(file, subst_vars):
279 """Read a file and sub in variables found in it
280
281 :param file: File to be read (typically from setup directory)
282 param subst_vars: Optional variables to subsitute in the file.
283 """
284 data = open(file, 'r').read()
285 if subst_vars is not None:
286 data = substitute_var(data, subst_vars)
287 check_all_substituted(data)
288 return data
289
290
291def setup_add_ldif(ldb, ldif_path, subst_vars=None):
292 """Setup a ldb in the private dir.
293
294 :param ldb: LDB file to import data into
295 :param ldif_path: Path of the LDIF file to load
296 :param subst_vars: Optional variables to subsitute in LDIF.
297 """
298 assert isinstance(ldif_path, str)
299
300 data = read_and_sub_file(ldif_path, subst_vars)
301 ldb.add_ldif(data)
302
303
304def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
305 """Modify a ldb in the private dir.
306
307 :param ldb: LDB object.
308 :param ldif_path: LDIF file path.
309 :param subst_vars: Optional dictionary with substitution variables.
310 """
311 data = read_and_sub_file(ldif_path, subst_vars)
312
313 ldb.modify_ldif(data)
314
315
316def setup_ldb(ldb, ldif_path, subst_vars):
317 """Import a LDIF a file into a LDB handle, optionally substituting variables.
318
319 :note: Either all LDIF data will be added or none (using transactions).
320
321 :param ldb: LDB file to import into.
322 :param ldif_path: Path to the LDIF file.
323 :param subst_vars: Dictionary with substitution variables.
324 """
325 assert ldb is not None
326 ldb.transaction_start()
327 try:
328 setup_add_ldif(ldb, ldif_path, subst_vars)
329 except:
330 ldb.transaction_cancel()
331 raise
332 ldb.transaction_commit()
333
334
335def setup_file(template, fname, subst_vars):
336 """Setup a file in the private dir.
337
338 :param template: Path of the template file.
339 :param fname: Path of the file to create.
340 :param subst_vars: Substitution variables.
341 """
342 f = fname
343
344 if os.path.exists(f):
345 os.unlink(f)
346
347 data = read_and_sub_file(template, subst_vars)
348 open(f, 'w').write(data)
349
350
351def provision_paths_from_lp(lp, dnsdomain):
352 """Set the default paths for provisioning.
353
354 :param lp: Loadparm context.
355 :param dnsdomain: DNS Domain name
356 """
357 paths = ProvisionPaths()
358 paths.private_dir = lp.get("private dir")
359 paths.dns_keytab = "dns.keytab"
360
361 paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
362 paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
363 paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
364 paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
365 paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
366 paths.namedconf = os.path.join(paths.private_dir, "named.conf")
367 paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
368 paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
369 paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
370 paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
371 paths.phpldapadminconfig = os.path.join(paths.private_dir,
372 "phpldapadmin-config.php")
373 paths.ldapdir = os.path.join(paths.private_dir,
374 "ldap")
375 paths.slapdconf = os.path.join(paths.ldapdir,
376 "slapd.conf")
377 paths.slapdpid = os.path.join(paths.ldapdir,
378 "slapd.pid")
379 paths.modulesconf = os.path.join(paths.ldapdir,
380 "modules.conf")
381 paths.memberofconf = os.path.join(paths.ldapdir,
382 "memberof.conf")
383 paths.fedoradsinf = os.path.join(paths.ldapdir,
384 "fedorads.inf")
385 paths.fedoradspartitions = os.path.join(paths.ldapdir,
386 "fedorads-partitions.ldif")
387 paths.fedoradssasl = os.path.join(paths.ldapdir,
388 "fedorads-sasl.ldif")
389 paths.fedoradssamba = os.path.join(paths.ldapdir,
390 "fedorads-samba.ldif")
391 paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
392 "mmr_serverids.conf")
393 paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
394 "mmr_syncrepl.conf")
395 paths.olcdir = os.path.join(paths.ldapdir,
396 "slapd.d")
397 paths.olcseedldif = os.path.join(paths.ldapdir,
398 "olc_seed.ldif")
399 paths.hklm = "hklm.ldb"
400 paths.hkcr = "hkcr.ldb"
401 paths.hkcu = "hkcu.ldb"
402 paths.hku = "hku.ldb"
403 paths.hkpd = "hkpd.ldb"
404 paths.hkpt = "hkpt.ldb"
405
406 paths.sysvol = lp.get("path", "sysvol")
407
408 paths.netlogon = lp.get("path", "netlogon")
409
410 paths.smbconf = lp.configfile
411
412 return paths
413
414
415def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
416 serverrole=None, rootdn=None, domaindn=None, configdn=None,
417 schemadn=None, serverdn=None, sitename=None, sambadn=None):
418 """Guess configuration settings to use."""
419
420 if hostname is None:
421 hostname = socket.gethostname().split(".")[0].lower()
422
423 netbiosname = hostname.upper()
424 if not valid_netbios_name(netbiosname):
425 raise InvalidNetbiosName(netbiosname)
426
427 hostname = hostname.lower()
428
429 if dnsdomain is None:
430 dnsdomain = lp.get("realm")
431
432 if serverrole is None:
433 serverrole = lp.get("server role")
434
435 assert dnsdomain is not None
436 realm = dnsdomain.upper()
437
438 if lp.get("realm").upper() != realm:
439 raise Exception("realm '%s' in %s must match chosen realm '%s'" %
440 (lp.get("realm"), lp.configfile, realm))
441
442 dnsdomain = dnsdomain.lower()
443
444 if serverrole == "domain controller":
445 if domain is None:
446 domain = lp.get("workgroup")
447 if domaindn is None:
448 domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
449 if lp.get("workgroup").upper() != domain.upper():
450 raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
451 lp.get("workgroup"), domain)
452 else:
453 domain = netbiosname
454 if domaindn is None:
455 domaindn = "CN=" + netbiosname
456
457 assert domain is not None
458 domain = domain.upper()
459 if not valid_netbios_name(domain):
460 raise InvalidNetbiosName(domain)
461
462 if netbiosname.upper() == realm.upper():
463 raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
464
465 if hostname.upper() == realm.upper():
466 raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
467
468 if domain.upper() == realm.upper():
469 raise Exception("realm %s must not be equal to domain name %s", realm, domain)
470
471 if rootdn is None:
472 rootdn = domaindn
473
474 if configdn is None:
475 configdn = "CN=Configuration," + rootdn
476 if schemadn is None:
477 schemadn = "CN=Schema," + configdn
478 if sambadn is None:
479 sambadn = "CN=Samba"
480
481 if sitename is None:
482 sitename=DEFAULTSITE
483
484 names = ProvisionNames()
485 names.rootdn = rootdn
486 names.domaindn = domaindn
487 names.configdn = configdn
488 names.schemadn = schemadn
489 names.sambadn = sambadn
490 names.ldapmanagerdn = "CN=Manager," + rootdn
491 names.dnsdomain = dnsdomain
492 names.domain = domain
493 names.realm = realm
494 names.netbiosname = netbiosname
495 names.hostname = hostname
496 names.sitename = sitename
497 names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
498
499 return names
500
501
502def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
503 targetdir):
504 """Create a new smb.conf file based on a couple of basic settings.
505 """
506 assert smbconf is not None
507 if hostname is None:
508 hostname = socket.gethostname().split(".")[0].lower()
509
510 if serverrole is None:
511 serverrole = "standalone"
512
513 assert serverrole in ("domain controller", "member server", "standalone")
514 if serverrole == "domain controller":
515 smbconfsuffix = "dc"
516 elif serverrole == "member server":
517 smbconfsuffix = "member"
518 elif serverrole == "standalone":
519 smbconfsuffix = "standalone"
520
521 assert domain is not None
522 assert realm is not None
523
524 default_lp = param.LoadParm()
525 #Load non-existant file
526 if os.path.exists(smbconf):
527 default_lp.load(smbconf)
528
529 if targetdir is not None:
530 privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
531 lockdir_line = "lock dir = " + os.path.abspath(targetdir)
532
533 default_lp.set("lock dir", os.path.abspath(targetdir))
534 else:
535 privatedir_line = ""
536 lockdir_line = ""
537
538 sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
539 netlogon = os.path.join(sysvol, realm.lower(), "scripts")
540
541 setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
542 smbconf, {
543 "HOSTNAME": hostname,
544 "DOMAIN": domain,
545 "REALM": realm,
546 "SERVERROLE": serverrole,
547 "NETLOGONPATH": netlogon,
548 "SYSVOLPATH": sysvol,
549 "PRIVATEDIR_LINE": privatedir_line,
550 "LOCKDIR_LINE": lockdir_line
551 })
552
553
554def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
555 users_gid, wheel_gid):
556 """setup reasonable name mappings for sam names to unix names.
557
558 :param samdb: SamDB object.
559 :param idmap: IDmap db object.
560 :param sid: The domain sid.
561 :param domaindn: The domain DN.
562 :param root_uid: uid of the UNIX root user.
563 :param nobody_uid: uid of the UNIX nobody user.
564 :param users_gid: gid of the UNIX users group.
565 :param wheel_gid: gid of the UNIX wheel group."""
566
567 idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
568 idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
569
570 idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
571 idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
572
573def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
574 credentials, names,
575 serverrole, ldap_backend=None,
576 erase=False):
577 """Setup the partitions for the SAM database.
578
579 Alternatively, provision() may call this, and then populate the database.
580
581 :note: This will wipe the Sam Database!
582
583 :note: This function always removes the local SAM LDB file. The erase
584 parameter controls whether to erase the existing data, which
585 may not be stored locally but in LDAP.
586 """
587 assert session_info is not None
588
589 # We use options=["modules:"] to stop the modules loading - we
590 # just want to wipe and re-initialise the database, not start it up
591
592 try:
593 samdb = Ldb(url=samdb_path, session_info=session_info,
594 credentials=credentials, lp=lp, options=["modules:"])
595 # Wipes the database
596 samdb.erase_except_schema_controlled()
597 except LdbError:
598 os.unlink(samdb_path)
599 samdb = Ldb(url=samdb_path, session_info=session_info,
600 credentials=credentials, lp=lp, options=["modules:"])
601 # Wipes the database
602 samdb.erase_except_schema_controlled()
603
604
605 #Add modules to the list to activate them by default
606 #beware often order is important
607 #
608 # Some Known ordering constraints:
609 # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
610 # - objectclass must be before password_hash, because password_hash checks
611 # that the objectclass is of type person (filled in by objectclass
612 # module when expanding the objectclass list)
613 # - partition must be last
614 # - each partition has its own module list then
615 modules_list = ["resolve_oids",
616 "rootdse",
617 "acl",
618 "paged_results",
619 "ranged_results",
620 "anr",
621 "server_sort",
622 "asq",
623 "extended_dn_store",
624 "extended_dn_in",
625 "rdn_name",
626 "objectclass",
627 "descriptor",
628 "samldb",
629 "password_hash",
630 "operational",
631 "kludge_acl"]
632 tdb_modules_list = [
633 "subtree_rename",
634 "subtree_delete",
635 "linked_attributes",
636 "extended_dn_out_ldb"]
637 modules_list2 = ["show_deleted",
638 "partition"]
639
640 domaindn_ldb = "users.ldb"
641 configdn_ldb = "configuration.ldb"
642 schemadn_ldb = "schema.ldb"
643 if ldap_backend is not None:
644 domaindn_ldb = ldap_backend.ldapi_uri
645 configdn_ldb = ldap_backend.ldapi_uri
646 schemadn_ldb = ldap_backend.ldapi_uri
647
648 if ldap_backend.ldap_backend_type == "fedora-ds":
649 backend_modules = ["nsuniqueid", "paged_searches"]
650 # We can handle linked attributes here, as we don't have directory-side subtree operations
651 tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
652 elif ldap_backend.ldap_backend_type == "openldap":
653 backend_modules = ["entryuuid", "paged_searches"]
654 # OpenLDAP handles subtree renames, so we don't want to do any of these things
655 tdb_modules_list = ["extended_dn_out_dereference"]
656
657 elif serverrole == "domain controller":
658 tdb_modules_list.insert(0, "repl_meta_data")
659 backend_modules = []
660 else:
661 backend_modules = ["objectguid"]
662
663 if tdb_modules_list is None:
664 tdb_modules_list_as_string = ""
665 else:
666 tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
667
668 samdb.transaction_start()
669 try:
670 message("Setting up sam.ldb partitions and settings")
671 setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
672 "SCHEMADN": names.schemadn,
673 "SCHEMADN_LDB": schemadn_ldb,
674 "SCHEMADN_MOD2": ",objectguid",
675 "CONFIGDN": names.configdn,
676 "CONFIGDN_LDB": configdn_ldb,
677 "DOMAINDN": names.domaindn,
678 "DOMAINDN_LDB": domaindn_ldb,
679 "SCHEMADN_MOD": "schema_fsmo,instancetype",
680 "CONFIGDN_MOD": "naming_fsmo,instancetype",
681 "DOMAINDN_MOD": "pdc_fsmo,instancetype",
682 "MODULES_LIST": ",".join(modules_list),
683 "TDB_MODULES_LIST": tdb_modules_list_as_string,
684 "MODULES_LIST2": ",".join(modules_list2),
685 "BACKEND_MOD": ",".join(backend_modules),
686 })
687
688 samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
689
690 message("Setting up sam.ldb rootDSE")
691 setup_samdb_rootdse(samdb, setup_path, names)
692
693 except:
694 samdb.transaction_cancel()
695 raise
696
697 samdb.transaction_commit()
698
699def secretsdb_self_join(secretsdb, domain,
700 netbiosname, domainsid, machinepass,
701 realm=None, dnsdomain=None,
702 keytab_path=None,
703 key_version_number=1,
704 secure_channel_type=SEC_CHAN_WKSTA):
705 """Add domain join-specific bits to a secrets database.
706
707 :param secretsdb: Ldb Handle to the secrets database
708 :param machinepass: Machine password
709 """
710 attrs=["whenChanged",
711 "secret",
712 "priorSecret",
713 "priorChanged",
714 "krb5Keytab",
715 "privateKeytab"]
716
717
718 msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
719 msg["secureChannelType"] = str(secure_channel_type)
720 msg["flatname"] = [domain]
721 msg["objectClass"] = ["top", "primaryDomain"]
722 if realm is not None:
723 if dnsdomain is None:
724 dnsdomain = realm.lower()
725 msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
726 msg["realm"] = realm
727 msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
728 msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
729 msg["privateKeytab"] = ["secrets.keytab"];
730
731
732 msg["secret"] = [machinepass]
733 msg["samAccountName"] = ["%s$" % netbiosname]
734 msg["secureChannelType"] = [str(secure_channel_type)]
735 msg["objectSid"] = [ndr_pack(domainsid)]
736
737 res = secretsdb.search(base="cn=Primary Domains",
738 attrs=attrs,
739 expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))),
740 scope=SCOPE_ONELEVEL)
741
742 for del_msg in res:
743 if del_msg.dn is not msg.dn:
744 secretsdb.delete(del_msg.dn)
745
746 res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
747
748 if len(res) == 1:
749 msg["priorSecret"] = res[0]["secret"]
750 msg["priorWhenChanged"] = res[0]["whenChanged"]
751
752 if res["privateKeytab"] is not None:
753 msg["privateKeytab"] = res[0]["privateKeytab"]
754
755 if res["krb5Keytab"] is not None:
756 msg["krb5Keytab"] = res[0]["krb5Keytab"]
757
758 for el in msg:
759 el.set_flags(ldb.FLAG_MOD_REPLACE)
760 secretsdb.modify(msg)
761 else:
762 secretsdb.add(msg)
763
764
765def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain,
766 dns_keytab_path, dnspass):
767 """Add DNS specific bits to a secrets database.
768
769 :param secretsdb: Ldb Handle to the secrets database
770 :param setup_path: Setup path function
771 :param machinepass: Machine password
772 """
773 setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
774 "REALM": realm,
775 "DNSDOMAIN": dnsdomain,
776 "DNS_KEYTAB": dns_keytab_path,
777 "DNSPASS_B64": b64encode(dnspass),
778 })
779
780
781def setup_secretsdb(path, setup_path, session_info, credentials, lp):
782 """Setup the secrets database.
783
784 :param path: Path to the secrets database.
785 :param setup_path: Get the path to a setup file.
786 :param session_info: Session info.
787 :param credentials: Credentials
788 :param lp: Loadparm context
789 :return: LDB handle for the created secrets database
790 """
791 if os.path.exists(path):
792 os.unlink(path)
793 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
794 lp=lp)
795 secrets_ldb.erase()
796 secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
797 secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
798 lp=lp)
799 secrets_ldb.transaction_start()
800 secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
801
802 if credentials is not None and credentials.authentication_requested():
803 if credentials.get_bind_dn() is not None:
804 setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
805 "LDAPMANAGERDN": credentials.get_bind_dn(),
806 "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
807 })
808 else:
809 setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
810 "LDAPADMINUSER": credentials.get_username(),
811 "LDAPADMINREALM": credentials.get_realm(),
812 "LDAPADMINPASS_B64": b64encode(credentials.get_password())
813 })
814
815 return secrets_ldb
816
817def setup_registry(path, setup_path, session_info, lp):
818 """Setup the registry.
819
820 :param path: Path to the registry database
821 :param setup_path: Function that returns the path to a setup.
822 :param session_info: Session information
823 :param credentials: Credentials
824 :param lp: Loadparm context
825 """
826 reg = registry.Registry()
827 hive = registry.open_ldb(path, session_info=session_info,
828 lp_ctx=lp)
829 reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
830 provision_reg = setup_path("provision.reg")
831 assert os.path.exists(provision_reg)
832 reg.diff_apply(provision_reg)
833
834
835def setup_idmapdb(path, setup_path, session_info, lp):
836 """Setup the idmap database.
837
838 :param path: path to the idmap database
839 :param setup_path: Function that returns a path to a setup file
840 :param session_info: Session information
841 :param credentials: Credentials
842 :param lp: Loadparm context
843 """
844 if os.path.exists(path):
845 os.unlink(path)
846
847 idmap_ldb = IDmapDB(path, session_info=session_info,
848 lp=lp)
849
850 idmap_ldb.erase()
851 idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
852 return idmap_ldb
853
854
855def setup_samdb_rootdse(samdb, setup_path, names):
856 """Setup the SamDB rootdse.
857
858 :param samdb: Sam Database handle
859 :param setup_path: Obtain setup path
860 """
861 setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
862 "SCHEMADN": names.schemadn,
863 "NETBIOSNAME": names.netbiosname,
864 "DNSDOMAIN": names.dnsdomain,
865 "REALM": names.realm,
866 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
867 "DOMAINDN": names.domaindn,
868 "ROOTDN": names.rootdn,
869 "CONFIGDN": names.configdn,
870 "SERVERDN": names.serverdn,
871 })
872
873
874def setup_self_join(samdb, names,
875 machinepass, dnspass,
876 domainsid, invocationid, setup_path,
877 policyguid, policyguid_dc, domainControllerFunctionality):
878 """Join a host to its own domain."""
879 assert isinstance(invocationid, str)
880 setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
881 "CONFIGDN": names.configdn,
882 "SCHEMADN": names.schemadn,
883 "DOMAINDN": names.domaindn,
884 "SERVERDN": names.serverdn,
885 "INVOCATIONID": invocationid,
886 "NETBIOSNAME": names.netbiosname,
887 "DEFAULTSITE": names.sitename,
888 "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
889 "MACHINEPASS_B64": b64encode(machinepass),
890 "DNSPASS_B64": b64encode(dnspass),
891 "REALM": names.realm,
892 "DOMAIN": names.domain,
893 "DNSDOMAIN": names.dnsdomain,
894 "SAMBA_VERSION_STRING": version,
895 "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
896
897 setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
898 "POLICYGUID": policyguid,
899 "POLICYGUID_DC": policyguid_dc,
900 "DNSDOMAIN": names.dnsdomain,
901 "DOMAINSID": str(domainsid),
902 "DOMAINDN": names.domaindn})
903
904 # add the NTDSGUID based SPNs
905 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
906 names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
907 expression="", scope=SCOPE_BASE)
908 assert isinstance(names.ntdsguid, str)
909
910 # Setup fSMORoleOwner entries to point at the newly created DC entry
911 setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
912 "DOMAIN": names.domain,
913 "DNSDOMAIN": names.dnsdomain,
914 "DOMAINDN": names.domaindn,
915 "CONFIGDN": names.configdn,
916 "SCHEMADN": names.schemadn,
917 "DEFAULTSITE": names.sitename,
918 "SERVERDN": names.serverdn,
919 "NETBIOSNAME": names.netbiosname,
920 "NTDSGUID": names.ntdsguid
921 })
922
923
924def setup_samdb(path, setup_path, session_info, credentials, lp,
925 names, message,
926 domainsid, domainguid, policyguid, policyguid_dc,
927 fill, adminpass, krbtgtpass,
928 machinepass, invocationid, dnspass,
929 serverrole, schema=None, ldap_backend=None):
930 """Setup a complete SAM Database.
931
932 :note: This will wipe the main SAM database file!
933 """
934
935 # Do NOT change these default values without discussion with the team and reslease manager.
936 domainFunctionality = DS_DOMAIN_FUNCTION_2008
937 forestFunctionality = DS_DOMAIN_FUNCTION_2008
938 domainControllerFunctionality = DS_DC_FUNCTION_2008
939
940 # Also wipes the database
941 setup_samdb_partitions(path, setup_path, message=message, lp=lp,
942 credentials=credentials, session_info=session_info,
943 names=names,
944 ldap_backend=ldap_backend, serverrole=serverrole)
945
946 if (schema == None):
947 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
948 sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
949
950 # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
951 samdb = Ldb(session_info=session_info,
952 credentials=credentials, lp=lp)
953
954 message("Pre-loading the Samba 4 and AD schema")
955
956 # Load the schema from the one we computed earlier
957 samdb.set_schema_from_ldb(schema.ldb)
958
959 # And now we can connect to the DB - the schema won't be loaded from the DB
960 samdb.connect(path)
961
962 # Load @OPTIONS
963 samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
964
965 if fill == FILL_DRS:
966 return samdb
967
968 samdb.transaction_start()
969 try:
970 message("Erasing data from partitions")
971 # Load the schema (again). This time it will force a reindex,
972 # and will therefore make the erase_partitions() below
973 # computationally sane
974 samdb.set_schema_from_ldb(schema.ldb)
975 samdb.erase_partitions()
976
977 # Set the domain functionality levels onto the database.
978 # Various module (the password_hash module in particular) need
979 # to know what level of AD we are emulating.
980
981 # These will be fixed into the database via the database
982 # modifictions below, but we need them set from the start.
983 samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
984 samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
985 samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
986
987 samdb.set_domain_sid(str(domainsid))
988 if serverrole == "domain controller":
989 samdb.set_invocation_id(invocationid)
990
991 message("Adding DomainDN: %s" % names.domaindn)
992 if serverrole == "domain controller":
993 domain_oc = "domainDNS"
994 else:
995 domain_oc = "samba4LocalDomain"
996
997#impersonate domain admin
998 admin_session_info = admin_session(lp, str(domainsid))
999 samdb.set_session_info(admin_session_info)
1000
1001 setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
1002 "DOMAINDN": names.domaindn,
1003 "DOMAIN_OC": domain_oc
1004 })
1005
1006 message("Modifying DomainDN: " + names.domaindn + "")
1007 if domainguid is not None:
1008 domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
1009 else:
1010 domainguid_mod = ""
1011
1012 setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
1013 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
1014 "DOMAINSID": str(domainsid),
1015 "SCHEMADN": names.schemadn,
1016 "NETBIOSNAME": names.netbiosname,
1017 "DEFAULTSITE": names.sitename,
1018 "CONFIGDN": names.configdn,
1019 "SERVERDN": names.serverdn,
1020 "POLICYGUID": policyguid,
1021 "DOMAINDN": names.domaindn,
1022 "DOMAINGUID_MOD": domainguid_mod,
1023 "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
1024 "SAMBA_VERSION_STRING": version
1025 })
1026
1027 message("Adding configuration container")
1028 descr = get_config_descriptor(domainsid);
1029 setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
1030 "CONFIGDN": names.configdn,
1031 "DESCRIPTOR": descr,
1032 })
1033 message("Modifying configuration container")
1034 setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
1035 "CONFIGDN": names.configdn,
1036 "SCHEMADN": names.schemadn,
1037 })
1038
1039 # The LDIF here was created when the Schema object was constructed
1040 message("Setting up sam.ldb schema")
1041 samdb.add_ldif(schema.schema_dn_add)
1042 samdb.modify_ldif(schema.schema_dn_modify)
1043 samdb.write_prefixes_from_schema()
1044 samdb.add_ldif(schema.schema_data)
1045 setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
1046 {"SCHEMADN": names.schemadn})
1047
1048 message("Setting up sam.ldb configuration data")
1049 setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
1050 "CONFIGDN": names.configdn,
1051 "NETBIOSNAME": names.netbiosname,
1052 "DEFAULTSITE": names.sitename,
1053 "DNSDOMAIN": names.dnsdomain,
1054 "DOMAIN": names.domain,
1055 "SCHEMADN": names.schemadn,
1056 "DOMAINDN": names.domaindn,
1057 "SERVERDN": names.serverdn,
1058 "FOREST_FUNCTIONALALITY": str(forestFunctionality)
1059 })
1060
1061 message("Setting up display specifiers")
1062 display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
1063 display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
1064 check_all_substituted(display_specifiers_ldif)
1065 samdb.add_ldif(display_specifiers_ldif)
1066
1067 message("Adding users container")
1068 setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
1069 "DOMAINDN": names.domaindn})
1070 message("Modifying users container")
1071 setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
1072 "DOMAINDN": names.domaindn})
1073 message("Adding computers container")
1074 setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
1075 "DOMAINDN": names.domaindn})
1076 message("Modifying computers container")
1077 setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
1078 "DOMAINDN": names.domaindn})
1079 message("Setting up sam.ldb data")
1080 setup_add_ldif(samdb, setup_path("provision.ldif"), {
1081 "CREATTIME": str(int(time.time()) * 1e7), # seconds -> ticks
1082 "DOMAINDN": names.domaindn,
1083 "NETBIOSNAME": names.netbiosname,
1084 "DEFAULTSITE": names.sitename,
1085 "CONFIGDN": names.configdn,
1086 "SERVERDN": names.serverdn,
1087 "POLICYGUID_DC": policyguid_dc
1088 })
1089
1090 if fill == FILL_FULL:
1091 message("Setting up sam.ldb users and groups")
1092 setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
1093 "DOMAINDN": names.domaindn,
1094 "DOMAINSID": str(domainsid),
1095 "CONFIGDN": names.configdn,
1096 "ADMINPASS_B64": b64encode(adminpass),
1097 "KRBTGTPASS_B64": b64encode(krbtgtpass),
1098 })
1099
1100 if serverrole == "domain controller":
1101 message("Setting up self join")
1102 setup_self_join(samdb, names=names, invocationid=invocationid,
1103 dnspass=dnspass,
1104 machinepass=machinepass,
1105 domainsid=domainsid, policyguid=policyguid,
1106 policyguid_dc=policyguid_dc,
1107 setup_path=setup_path,
1108 domainControllerFunctionality=domainControllerFunctionality)
1109
1110 ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
1111 names.ntdsguid = samdb.searchone(basedn=ntds_dn,
1112 attribute="objectGUID", expression="", scope=SCOPE_BASE)
1113 assert isinstance(names.ntdsguid, str)
1114
1115 except:
1116 samdb.transaction_cancel()
1117 raise
1118
1119 samdb.transaction_commit()
1120 return samdb
1121
1122
1123FILL_FULL = "FULL"
1124FILL_NT4SYNC = "NT4SYNC"
1125FILL_DRS = "DRS"
1126
1127
1128def provision(setup_dir, message, session_info,
1129 credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
1130 realm=None,
1131 rootdn=None, domaindn=None, schemadn=None, configdn=None,
1132 serverdn=None,
1133 domain=None, hostname=None, hostip=None, hostip6=None,
1134 domainsid=None, adminpass=None, ldapadminpass=None,
1135 krbtgtpass=None, domainguid=None,
1136 policyguid=None, policyguid_dc=None, invocationid=None,
1137 machinepass=None,
1138 dnspass=None, root=None, nobody=None, users=None,
1139 wheel=None, backup=None, aci=None, serverrole=None,
1140 ldap_backend_extra_port=None, ldap_backend_type=None,
1141 sitename=None,
1142 ol_mmr_urls=None, ol_olc=None,
1143 setup_ds_path=None, slapd_path=None, nosync=False,
1144 ldap_dryrun_mode=False):
1145 """Provision samba4
1146
1147 :note: caution, this wipes all existing data!
1148 """
1149
1150 def setup_path(file):
1151 return os.path.join(setup_dir, file)
1152
1153 if domainsid is None:
1154 domainsid = security.random_sid()
1155 else:
1156 domainsid = security.dom_sid(domainsid)
1157
1158
1159 # create/adapt the group policy GUIDs
1160 if policyguid is None:
1161 policyguid = str(uuid.uuid4())
1162 policyguid = policyguid.upper()
1163 if policyguid_dc is None:
1164 policyguid_dc = str(uuid.uuid4())
1165 policyguid_dc = policyguid_dc.upper()
1166
1167 if adminpass is None:
1168 adminpass = glue.generate_random_str(12)
1169 if krbtgtpass is None:
1170 krbtgtpass = glue.generate_random_str(12)
1171 if machinepass is None:
1172 machinepass = glue.generate_random_str(12)
1173 if dnspass is None:
1174 dnspass = glue.generate_random_str(12)
1175 if ldapadminpass is None:
1176 #Make a new, random password between Samba and it's LDAP server
1177 ldapadminpass=glue.generate_random_str(12)
1178
1179
1180 root_uid = findnss_uid([root or "root"])
1181 nobody_uid = findnss_uid([nobody or "nobody"])
1182 users_gid = findnss_gid([users or "users"])
1183 if wheel is None:
1184 wheel_gid = findnss_gid(["wheel", "adm"])
1185 else:
1186 wheel_gid = findnss_gid([wheel])
1187
1188 if targetdir is not None:
1189 if (not os.path.exists(os.path.join(targetdir, "etc"))):
1190 os.makedirs(os.path.join(targetdir, "etc"))
1191 smbconf = os.path.join(targetdir, "etc", "smb.conf")
1192 elif smbconf is None:
1193 smbconf = param.default_path()
1194
1195 # only install a new smb.conf if there isn't one there already
1196 if not os.path.exists(smbconf):
1197 make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
1198 targetdir)
1199
1200 lp = param.LoadParm()
1201 lp.load(smbconf)
1202
1203 names = guess_names(lp=lp, hostname=hostname, domain=domain,
1204 dnsdomain=realm, serverrole=serverrole, sitename=sitename,
1205 rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
1206 serverdn=serverdn)
1207
1208 paths = provision_paths_from_lp(lp, names.dnsdomain)
1209
1210 if hostip is None:
1211 try:
1212 hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1213 except socket.gaierror, (socket.EAI_NODATA, msg):
1214 hostip = None
1215
1216 if hostip6 is None:
1217 try:
1218 hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
1219 except socket.gaierror, (socket.EAI_NODATA, msg):
1220 hostip6 = None
1221
1222 if serverrole is None:
1223 serverrole = lp.get("server role")
1224
1225 assert serverrole in ("domain controller", "member server", "standalone")
1226 if invocationid is None and serverrole == "domain controller":
1227 invocationid = str(uuid.uuid4())
1228
1229 if not os.path.exists(paths.private_dir):
1230 os.mkdir(paths.private_dir)
1231
1232 ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
1233
1234 schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
1235 sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
1236
1237 secrets_credentials = credentials
1238 provision_backend = None
1239 if ldap_backend_type:
1240 # We only support an LDAP backend over ldapi://
1241
1242 provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
1243 lp=lp, credentials=credentials,
1244 names=names,
1245 message=message, hostname=hostname,
1246 root=root, schema=schema,
1247 ldap_backend_type=ldap_backend_type,
1248 ldapadminpass=ldapadminpass,
1249 ldap_backend_extra_port=ldap_backend_extra_port,
1250 ol_mmr_urls=ol_mmr_urls,
1251 slapd_path=slapd_path,
1252 setup_ds_path=setup_ds_path,
1253 ldap_dryrun_mode=ldap_dryrun_mode)
1254
1255 # Now use the backend credentials to access the databases
1256 credentials = provision_backend.credentials
1257 secrets_credentials = provision_backend.adminCredentials
1258 ldapi_url = provision_backend.ldapi_uri
1259
1260 # only install a new shares config db if there is none
1261 if not os.path.exists(paths.shareconf):
1262 message("Setting up share.ldb")
1263 share_ldb = Ldb(paths.shareconf, session_info=session_info,
1264 credentials=credentials, lp=lp)
1265 share_ldb.load_ldif_file_add(setup_path("share.ldif"))
1266
1267
1268 message("Setting up secrets.ldb")
1269 secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
1270 session_info=session_info,
1271 credentials=secrets_credentials, lp=lp)
1272
1273 message("Setting up the registry")
1274 setup_registry(paths.hklm, setup_path, session_info,
1275 lp=lp)
1276
1277 message("Setting up idmap db")
1278 idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
1279 lp=lp)
1280
1281 message("Setting up SAM db")
1282 samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
1283 credentials=credentials, lp=lp, names=names,
1284 message=message,
1285 domainsid=domainsid,
1286 schema=schema, domainguid=domainguid,
1287 policyguid=policyguid, policyguid_dc=policyguid_dc,
1288 fill=samdb_fill,
1289 adminpass=adminpass, krbtgtpass=krbtgtpass,
1290 invocationid=invocationid,
1291 machinepass=machinepass, dnspass=dnspass,
1292 serverrole=serverrole, ldap_backend=provision_backend)
1293
1294 if serverrole == "domain controller":
1295 if paths.netlogon is None:
1296 message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
1297 message("Please either remove %s or see the template at %s" %
1298 ( paths.smbconf, setup_path("provision.smb.conf.dc")))
1299 assert(paths.netlogon is not None)
1300
1301 if paths.sysvol is None:
1302 message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
1303 message("Please either remove %s or see the template at %s" %
1304 (paths.smbconf, setup_path("provision.smb.conf.dc")))
1305 assert(paths.sysvol is not None)
1306
1307 # Set up group policies (domain policy and domain controller policy)
1308
1309 policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1310 "{" + policyguid + "}")
1311 os.makedirs(policy_path, 0755)
1312 open(os.path.join(policy_path, "GPT.INI"), 'w').write(
1313 "[General]\r\nVersion=65543")
1314 os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
1315 os.makedirs(os.path.join(policy_path, "USER"), 0755)
1316
1317 policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
1318 "{" + policyguid_dc + "}")
1319 os.makedirs(policy_path_dc, 0755)
1320 open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
1321 "[General]\r\nVersion=2")
1322 os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
1323 os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
1324
1325 if not os.path.isdir(paths.netlogon):
1326 os.makedirs(paths.netlogon, 0755)
1327
1328 if samdb_fill == FILL_FULL:
1329 setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
1330 root_uid=root_uid, nobody_uid=nobody_uid,
1331 users_gid=users_gid, wheel_gid=wheel_gid)
1332
1333 message("Setting up sam.ldb rootDSE marking as synchronized")
1334 setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
1335
1336 # Only make a zone file on the first DC, it should be replicated with DNS replication
1337 if serverrole == "domain controller":
1338 secretsdb_self_join(secrets_ldb, domain=domain,
1339 realm=names.realm,
1340 dnsdomain=names.dnsdomain,
1341 netbiosname=names.netbiosname,
1342 domainsid=domainsid,
1343 machinepass=machinepass,
1344 secure_channel_type=SEC_CHAN_BDC)
1345
1346 secretsdb_setup_dns(secrets_ldb, setup_path,
1347 realm=names.realm, dnsdomain=names.dnsdomain,
1348 dns_keytab_path=paths.dns_keytab,
1349 dnspass=dnspass)
1350
1351 domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
1352 assert isinstance(domainguid, str)
1353
1354 create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
1355 domaindn=names.domaindn, hostip=hostip,
1356 hostip6=hostip6, hostname=names.hostname,
1357 dnspass=dnspass, realm=names.realm,
1358 domainguid=domainguid, ntdsguid=names.ntdsguid)
1359
1360 create_named_conf(paths.namedconf, setup_path, realm=names.realm,
1361 dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
1362
1363 create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
1364 dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
1365 keytab_name=paths.dns_keytab)
1366 message("See %s for an example configuration include file for BIND" % paths.namedconf)
1367 message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
1368
1369 create_krb5_conf(paths.krb5conf, setup_path,
1370 dnsdomain=names.dnsdomain, hostname=names.hostname,
1371 realm=names.realm)
1372 message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
1373
1374 #Now commit the secrets.ldb to disk
1375 secrets_ldb.transaction_commit()
1376
1377 if provision_backend is not None:
1378 if ldap_backend_type == "fedora-ds":
1379 ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
1380
1381 # delete default SASL mappings
1382 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
1383
1384 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
1385 for i in range (0, len(res)):
1386 dn = str(res[i]["dn"])
1387 ldapi_db.delete(dn)
1388
1389 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
1390
1391 m = ldb.Message()
1392 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
1393
1394 m.dn = ldb.Dn(1, names.domaindn)
1395 ldapi_db.modify(m)
1396
1397 m.dn = ldb.Dn(1, names.configdn)
1398 ldapi_db.modify(m)
1399
1400 m.dn = ldb.Dn(1, names.schemadn)
1401 ldapi_db.modify(m)
1402
1403 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
1404 if provision_backend.slapd.poll() is None:
1405 #Kill the slapd
1406 if hasattr(provision_backend.slapd, "terminate"):
1407 provision_backend.slapd.terminate()
1408 else:
1409 # Older python versions don't have .terminate()
1410 import signal
1411 os.kill(provision_backend.slapd.pid, signal.SIGTERM)
1412
1413 #and now wait for it to die
1414 provision_backend.slapd.communicate()
1415
1416 # now display slapd_command_file.txt to show how slapd must be started next time
1417 message("Use later the following commandline to start slapd, then Samba:")
1418 slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
1419 message(slapd_command)
1420 message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
1421
1422 setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
1423 "SLAPD_COMMAND" : slapd_command})
1424
1425
1426 create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
1427 ldapi_url)
1428
1429 message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
1430
1431 message("Once the above files are installed, your Samba4 server will be ready to use")
1432 message("Server Role: %s" % serverrole)
1433 message("Hostname: %s" % names.hostname)
1434 message("NetBIOS Domain: %s" % names.domain)
1435 message("DNS Domain: %s" % names.dnsdomain)
1436 message("DOMAIN SID: %s" % str(domainsid))
1437 if samdb_fill == FILL_FULL:
1438 message("Admin password: %s" % adminpass)
1439 if provision_backend:
1440 if provision_backend.credentials.get_bind_dn() is not None:
1441 message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
1442 else:
1443 message("LDAP Admin User: %s" % provision_backend.credentials.get_username())
1444
1445 message("LDAP Admin Password: %s" % provision_backend.credentials.get_password())
1446
1447 result = ProvisionResult()
1448 result.domaindn = domaindn
1449 result.paths = paths
1450 result.lp = lp
1451 result.samdb = samdb
1452 return result
1453
1454
1455
1456def provision_become_dc(setup_dir=None,
1457 smbconf=None, targetdir=None, realm=None,
1458 rootdn=None, domaindn=None, schemadn=None,
1459 configdn=None, serverdn=None,
1460 domain=None, hostname=None, domainsid=None,
1461 adminpass=None, krbtgtpass=None, domainguid=None,
1462 policyguid=None, policyguid_dc=None, invocationid=None,
1463 machinepass=None,
1464 dnspass=None, root=None, nobody=None, users=None,
1465 wheel=None, backup=None, serverrole=None,
1466 ldap_backend=None, ldap_backend_type=None,
1467 sitename=None, debuglevel=1):
1468
1469 def message(text):
1470 """print a message if quiet is not set."""
1471 print text
1472
1473 glue.set_debug_level(debuglevel)
1474
1475 return provision(setup_dir, message, system_session(), None,
1476 smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
1477 realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
1478 configdn=configdn, serverdn=serverdn, domain=domain,
1479 hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
1480 machinepass=machinepass, serverrole="domain controller",
1481 sitename=sitename)
1482
1483
1484def setup_db_config(setup_path, dbdir):
1485 """Setup a Berkeley database.
1486
1487 :param setup_path: Setup path function.
1488 :param dbdir: Database directory."""
1489 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
1490 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
1491 if not os.path.isdir(os.path.join(dbdir, "tmp")):
1492 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
1493
1494 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
1495 {"LDAPDBDIR": dbdir})
1496
1497class ProvisionBackend(object):
1498 def __init__(self, paths=None, setup_path=None, lp=None, credentials=None,
1499 names=None, message=None,
1500 hostname=None, root=None,
1501 schema=None, ldapadminpass=None,
1502 ldap_backend_type=None, ldap_backend_extra_port=None,
1503 ol_mmr_urls=None,
1504 setup_ds_path=None, slapd_path=None,
1505 nosync=False, ldap_dryrun_mode=False):
1506 """Provision an LDAP backend for samba4
1507
1508 This works for OpenLDAP and Fedora DS
1509 """
1510
1511 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
1512
1513 if not os.path.isdir(paths.ldapdir):
1514 os.makedirs(paths.ldapdir, 0700)
1515
1516 if ldap_backend_type == "existing":
1517 #Check to see that this 'existing' LDAP backend in fact exists
1518 ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
1519 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1520 expression="(objectClass=OpenLDAProotDSE)")
1521
1522 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
1523 # This caused them to be set into the long-term database later in the script.
1524 self.credentials = credentials
1525 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
1526 return
1527
1528 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
1529 # if another instance of slapd is already running
1530 try:
1531 ldapi_db = Ldb(self.ldapi_uri)
1532 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1533 expression="(objectClass=OpenLDAProotDSE)");
1534 try:
1535 f = open(paths.slapdpid, "r")
1536 p = f.read()
1537 f.close()
1538 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
1539 except:
1540 pass
1541
1542 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
1543
1544 except LdbError, e:
1545 pass
1546
1547 # Try to print helpful messages when the user has not specified the path to slapd
1548 if slapd_path is None:
1549 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
1550 if not os.path.exists(slapd_path):
1551 message (slapd_path)
1552 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1553
1554 schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
1555 try:
1556 os.unlink(schemadb_path)
1557 except OSError:
1558 pass
1559
1560
1561 # Put the LDIF of the schema into a database so we can search on
1562 # it to generate schema-dependent configurations in Fedora DS and
1563 # OpenLDAP
1564 os.path.join(paths.ldapdir, "schema-tmp.ldb")
1565 schema.ldb.connect(schemadb_path)
1566 schema.ldb.transaction_start()
1567
1568 # These bits of LDIF are supplied when the Schema object is created
1569 schema.ldb.add_ldif(schema.schema_dn_add)
1570 schema.ldb.modify_ldif(schema.schema_dn_modify)
1571 schema.ldb.add_ldif(schema.schema_data)
1572 schema.ldb.transaction_commit()
1573
1574 self.credentials = Credentials()
1575 self.credentials.guess(lp)
1576 #Kerberos to an ldapi:// backend makes no sense
1577 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
1578
1579 self.adminCredentials = Credentials()
1580 self.adminCredentials.guess(lp)
1581 #Kerberos to an ldapi:// backend makes no sense
1582 self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
1583
1584 self.ldap_backend_type = ldap_backend_type
1585
1586 if ldap_backend_type == "fedora-ds":
1587 provision_fds_backend(self, paths=paths, setup_path=setup_path,
1588 names=names, message=message,
1589 hostname=hostname,
1590 ldapadminpass=ldapadminpass, root=root,
1591 schema=schema,
1592 ldap_backend_extra_port=ldap_backend_extra_port,
1593 setup_ds_path=setup_ds_path,
1594 slapd_path=slapd_path,
1595 nosync=nosync,
1596 ldap_dryrun_mode=ldap_dryrun_mode)
1597
1598 elif ldap_backend_type == "openldap":
1599 provision_openldap_backend(self, paths=paths, setup_path=setup_path,
1600 names=names, message=message,
1601 hostname=hostname,
1602 ldapadminpass=ldapadminpass, root=root,
1603 schema=schema,
1604 ldap_backend_extra_port=ldap_backend_extra_port,
1605 ol_mmr_urls=ol_mmr_urls,
1606 slapd_path=slapd_path,
1607 nosync=nosync,
1608 ldap_dryrun_mode=ldap_dryrun_mode)
1609 else:
1610 raise ProvisioningError("Unknown LDAP backend type selected")
1611
1612 self.credentials.set_password(ldapadminpass)
1613 self.adminCredentials.set_username("samba-admin")
1614 self.adminCredentials.set_password(ldapadminpass)
1615
1616 # Now start the slapd, so we can provision onto it. We keep the
1617 # subprocess context around, to kill this off at the successful
1618 # end of the script
1619 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
1620
1621 while self.slapd.poll() is None:
1622 # Wait until the socket appears
1623 try:
1624 ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
1625 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
1626 expression="(objectClass=OpenLDAProotDSE)")
1627 # If we have got here, then we must have a valid connection to the LDAP server!
1628 return
1629 except LdbError, e:
1630 time.sleep(1)
1631 pass
1632
1633 raise ProvisioningError("slapd died before we could make a connection to it")
1634
1635
1636def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
1637 message=None,
1638 hostname=None, ldapadminpass=None, root=None,
1639 schema=None,
1640 ldap_backend_extra_port=None,
1641 ol_mmr_urls=None,
1642 slapd_path=None, nosync=False,
1643 ldap_dryrun_mode=False):
1644
1645 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
1646 nosync_config = ""
1647 if nosync:
1648 nosync_config = "dbnosync"
1649
1650 lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
1651 refint_attributes = ""
1652 memberof_config = "# Generated from Samba4 schema\n"
1653 for att in lnkattr.keys():
1654 if lnkattr[att] is not None:
1655 refint_attributes = refint_attributes + " " + att
1656
1657 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
1658 { "MEMBER_ATTR" : att ,
1659 "MEMBEROF_ATTR" : lnkattr[att] })
1660
1661 refint_config = read_and_sub_file(setup_path("refint.conf"),
1662 { "LINK_ATTRS" : refint_attributes})
1663
1664 attrs = ["linkID", "lDAPDisplayName"]
1665 res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
1666 index_config = ""
1667 for i in range (0, len(res)):
1668 index_attr = res[i]["lDAPDisplayName"][0]
1669 if index_attr == "objectGUID":
1670 index_attr = "entryUUID"
1671
1672 index_config += "index " + index_attr + " eq\n"
1673
1674# generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
1675 mmr_on_config = ""
1676 mmr_replicator_acl = ""
1677 mmr_serverids_config = ""
1678 mmr_syncrepl_schema_config = ""
1679 mmr_syncrepl_config_config = ""
1680 mmr_syncrepl_user_config = ""
1681
1682
1683 if ol_mmr_urls is not None:
1684 # For now, make these equal
1685 mmr_pass = ldapadminpass
1686
1687 url_list=filter(None,ol_mmr_urls.split(' '))
1688 if (len(url_list) == 1):
1689 url_list=filter(None,ol_mmr_urls.split(','))
1690
1691
1692 mmr_on_config = "MirrorMode On"
1693 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
1694 serverid=0
1695 for url in url_list:
1696 serverid=serverid+1
1697 mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
1698 { "SERVERID" : str(serverid),
1699 "LDAPSERVER" : url })
1700 rid=serverid*10
1701 rid=rid+1
1702 mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1703 { "RID" : str(rid),
1704 "MMRDN": names.schemadn,
1705 "LDAPSERVER" : url,
1706 "MMR_PASSWORD": mmr_pass})
1707
1708 rid=rid+1
1709 mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1710 { "RID" : str(rid),
1711 "MMRDN": names.configdn,
1712 "LDAPSERVER" : url,
1713 "MMR_PASSWORD": mmr_pass})
1714
1715 rid=rid+1
1716 mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
1717 { "RID" : str(rid),
1718 "MMRDN": names.domaindn,
1719 "LDAPSERVER" : url,
1720 "MMR_PASSWORD": mmr_pass })
1721 # OpenLDAP cn=config initialisation
1722 olc_syncrepl_config = ""
1723 olc_mmr_config = ""
1724 # if mmr = yes, generate cn=config-replication directives
1725 # and olc_seed.lif for the other mmr-servers
1726 if ol_mmr_urls is not None:
1727 serverid=0
1728 olc_serverids_config = ""
1729 olc_syncrepl_seed_config = ""
1730 olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
1731 rid=1000
1732 for url in url_list:
1733 serverid=serverid+1
1734 olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
1735 { "SERVERID" : str(serverid),
1736 "LDAPSERVER" : url })
1737
1738 rid=rid+1
1739 olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
1740 { "RID" : str(rid),
1741 "LDAPSERVER" : url,
1742 "MMR_PASSWORD": mmr_pass})
1743
1744 olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
1745 { "RID" : str(rid),
1746 "LDAPSERVER" : url})
1747
1748 setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
1749 {"OLC_SERVER_ID_CONF": olc_serverids_config,
1750 "OLC_PW": ldapadminpass,
1751 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
1752 # end olc
1753
1754 setup_file(setup_path("slapd.conf"), paths.slapdconf,
1755 {"DNSDOMAIN": names.dnsdomain,
1756 "LDAPDIR": paths.ldapdir,
1757 "DOMAINDN": names.domaindn,
1758 "CONFIGDN": names.configdn,
1759 "SCHEMADN": names.schemadn,
1760 "MEMBEROF_CONFIG": memberof_config,
1761 "MIRRORMODE": mmr_on_config,
1762 "REPLICATOR_ACL": mmr_replicator_acl,
1763 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
1764 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
1765 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
1766 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
1767 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
1768 "OLC_MMR_CONFIG": olc_mmr_config,
1769 "REFINT_CONFIG": refint_config,
1770 "INDEX_CONFIG": index_config,
1771 "NOSYNC": nosync_config})
1772
1773 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
1774 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
1775 setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
1776
1777 if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
1778 os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
1779
1780 setup_file(setup_path("cn=samba.ldif"),
1781 os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
1782 { "UUID": str(uuid.uuid4()),
1783 "LDAPTIME": timestring(int(time.time()))} )
1784 setup_file(setup_path("cn=samba-admin.ldif"),
1785 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
1786 {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
1787 "UUID": str(uuid.uuid4()),
1788 "LDAPTIME": timestring(int(time.time()))} )
1789
1790 if ol_mmr_urls is not None:
1791 setup_file(setup_path("cn=replicator.ldif"),
1792 os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
1793 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
1794 "UUID": str(uuid.uuid4()),
1795 "LDAPTIME": timestring(int(time.time()))} )
1796
1797
1798 mapping = "schema-map-openldap-2.3"
1799 backend_schema = "backend-schema.schema"
1800
1801 backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
1802 assert backend_schema_data is not None
1803 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1804
1805 # now we generate the needed strings to start slapd automatically,
1806 # first ldapi_uri...
1807 if ldap_backend_extra_port is not None:
1808 # When we use MMR, we can't use 0.0.0.0 as it uses the name
1809 # specified there as part of it's clue as to it's own name,
1810 # and not to replicate to itself
1811 if ol_mmr_urls is None:
1812 server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
1813 else:
1814 server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
1815 else:
1816 server_port_string = ""
1817
1818 # Prepare the 'result' information - the commands to return in particular
1819 result.slapd_provision_command = [slapd_path]
1820
1821 result.slapd_provision_command.append("-F" + paths.olcdir)
1822
1823 result.slapd_provision_command.append("-h")
1824
1825 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
1826 result.slapd_command = list(result.slapd_provision_command)
1827
1828 result.slapd_provision_command.append(result.ldapi_uri)
1829 result.slapd_provision_command.append("-d0")
1830
1831 uris = result.ldapi_uri
1832 if server_port_string is not "":
1833 uris = uris + " " + server_port_string
1834
1835 result.slapd_command.append(uris)
1836
1837 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
1838 result.credentials.set_username("samba-admin")
1839
1840 # If we were just looking for crashes up to this point, it's a
1841 # good time to exit before we realise we don't have OpenLDAP on
1842 # this system
1843 if ldap_dryrun_mode:
1844 sys.exit(0)
1845
1846 # Finally, convert the configuration into cn=config style!
1847 if not os.path.isdir(paths.olcdir):
1848 os.makedirs(paths.olcdir, 0770)
1849
1850 retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
1851
1852# We can't do this, as OpenLDAP is strange. It gives an error
1853# output to the above, but does the conversion sucessfully...
1854#
1855# if retcode != 0:
1856# raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1857
1858 if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
1859 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
1860
1861 # Don't confuse the admin by leaving the slapd.conf around
1862 os.remove(paths.slapdconf)
1863
1864
1865def provision_fds_backend(result, paths=None, setup_path=None, names=None,
1866 message=None,
1867 hostname=None, ldapadminpass=None, root=None,
1868 schema=None,
1869 ldap_backend_extra_port=None,
1870 setup_ds_path=None,
1871 slapd_path=None,
1872 nosync=False,
1873 ldap_dryrun_mode=False):
1874
1875 if ldap_backend_extra_port is not None:
1876 serverport = "ServerPort=%d" % ldap_backend_extra_port
1877 else:
1878 serverport = ""
1879
1880 setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
1881 {"ROOT": root,
1882 "HOSTNAME": hostname,
1883 "DNSDOMAIN": names.dnsdomain,
1884 "LDAPDIR": paths.ldapdir,
1885 "DOMAINDN": names.domaindn,
1886 "LDAPMANAGERDN": names.ldapmanagerdn,
1887 "LDAPMANAGERPASS": ldapadminpass,
1888 "SERVERPORT": serverport})
1889
1890 setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
1891 {"CONFIGDN": names.configdn,
1892 "SCHEMADN": names.schemadn,
1893 "SAMBADN": names.sambadn,
1894 })
1895
1896 setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl,
1897 {"SAMBADN": names.sambadn,
1898 })
1899
1900 setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
1901 {"SAMBADN": names.sambadn,
1902 "LDAPADMINPASS": ldapadminpass
1903 })
1904
1905 mapping = "schema-map-fedora-ds-1.0"
1906 backend_schema = "99_ad.ldif"
1907
1908 # Build a schema file in Fedora DS format
1909 backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
1910 assert backend_schema_data is not None
1911 open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
1912
1913 result.credentials.set_bind_dn(names.ldapmanagerdn)
1914
1915 # Destory the target directory, or else setup-ds.pl will complain
1916 fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
1917 shutil.rmtree(fedora_ds_dir, True)
1918
1919 result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
1920 #In the 'provision' command line, stay in the foreground so we can easily kill it
1921 result.slapd_provision_command.append("-d0")
1922
1923 #the command for the final run is the normal script
1924 result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
1925
1926 # If we were just looking for crashes up to this point, it's a
1927 # good time to exit before we realise we don't have Fedora DS on
1928 if ldap_dryrun_mode:
1929 sys.exit(0)
1930
1931 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
1932 if setup_ds_path is None:
1933 raise ProvisioningError("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
1934 if not os.path.exists(setup_ds_path):
1935 message (setup_ds_path)
1936 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
1937
1938 # Run the Fedora DS setup utility
1939 retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
1940 if retcode != 0:
1941 raise ProvisioningError("setup-ds failed")
1942
1943 # Load samba-admin
1944 retcode = subprocess.call([
1945 os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
1946 close_fds=True, shell=False)
1947 if retcode != 0:
1948 raise("ldib2db failed")
1949
1950def create_phpldapadmin_config(path, setup_path, ldapi_uri):
1951 """Create a PHP LDAP admin configuration file.
1952
1953 :param path: Path to write the configuration to.
1954 :param setup_path: Function to generate setup paths.
1955 """
1956 setup_file(setup_path("phpldapadmin-config.php"), path,
1957 {"S4_LDAPI_URI": ldapi_uri})
1958
1959
1960def create_zone_file(path, setup_path, dnsdomain, domaindn,
1961 hostip, hostip6, hostname, dnspass, realm, domainguid,
1962 ntdsguid):
1963 """Write out a DNS zone file, from the info in the current database.
1964
1965 :param path: Path of the new zone file.
1966 :param setup_path: Setup path function.
1967 :param dnsdomain: DNS Domain name
1968 :param domaindn: DN of the Domain
1969 :param hostip: Local IPv4 IP
1970 :param hostip6: Local IPv6 IP
1971 :param hostname: Local hostname
1972 :param dnspass: Password for DNS
1973 :param realm: Realm name
1974 :param domainguid: GUID of the domain.
1975 :param ntdsguid: GUID of the hosts nTDSDSA record.
1976 """
1977 assert isinstance(domainguid, str)
1978
1979 if hostip6 is not None:
1980 hostip6_base_line = " IN AAAA " + hostip6
1981 hostip6_host_line = hostname + " IN AAAA " + hostip6
1982 else:
1983 hostip6_base_line = ""
1984 hostip6_host_line = ""
1985
1986 if hostip is not None:
1987 hostip_base_line = " IN A " + hostip
1988 hostip_host_line = hostname + " IN A " + hostip
1989 else:
1990 hostip_base_line = ""
1991 hostip_host_line = ""
1992
1993 setup_file(setup_path("provision.zone"), path, {
1994 "DNSPASS_B64": b64encode(dnspass),
1995 "HOSTNAME": hostname,
1996 "DNSDOMAIN": dnsdomain,
1997 "REALM": realm,
1998 "HOSTIP_BASE_LINE": hostip_base_line,
1999 "HOSTIP_HOST_LINE": hostip_host_line,
2000 "DOMAINGUID": domainguid,
2001 "DATESTRING": time.strftime("%Y%m%d%H"),
2002 "DEFAULTSITE": DEFAULTSITE,
2003 "NTDSGUID": ntdsguid,
2004 "HOSTIP6_BASE_LINE": hostip6_base_line,
2005 "HOSTIP6_HOST_LINE": hostip6_host_line,
2006 })
2007
2008
2009def create_named_conf(path, setup_path, realm, dnsdomain,
2010 private_dir):
2011 """Write out a file containing zone statements suitable for inclusion in a
2012 named.conf file (including GSS-TSIG configuration).
2013
2014 :param path: Path of the new named.conf file.
2015 :param setup_path: Setup path function.
2016 :param realm: Realm name
2017 :param dnsdomain: DNS Domain name
2018 :param private_dir: Path to private directory
2019 :param keytab_name: File name of DNS keytab file
2020 """
2021
2022 setup_file(setup_path("named.conf"), path, {
2023 "DNSDOMAIN": dnsdomain,
2024 "REALM": realm,
2025 "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
2026 "PRIVATE_DIR": private_dir
2027 })
2028
2029def create_named_txt(path, setup_path, realm, dnsdomain,
2030 private_dir, keytab_name):
2031 """Write out a file containing zone statements suitable for inclusion in a
2032 named.conf file (including GSS-TSIG configuration).
2033
2034 :param path: Path of the new named.conf file.
2035 :param setup_path: Setup path function.
2036 :param realm: Realm name
2037 :param dnsdomain: DNS Domain name
2038 :param private_dir: Path to private directory
2039 :param keytab_name: File name of DNS keytab file
2040 """
2041
2042 setup_file(setup_path("named.txt"), path, {
2043 "DNSDOMAIN": dnsdomain,
2044 "REALM": realm,
2045 "DNS_KEYTAB": keytab_name,
2046 "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
2047 "PRIVATE_DIR": private_dir
2048 })
2049
2050def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
2051 """Write out a file containing zone statements suitable for inclusion in a
2052 named.conf file (including GSS-TSIG configuration).
2053
2054 :param path: Path of the new named.conf file.
2055 :param setup_path: Setup path function.
2056 :param dnsdomain: DNS Domain name
2057 :param hostname: Local hostname
2058 :param realm: Realm name
2059 """
2060
2061 setup_file(setup_path("krb5.conf"), path, {
2062 "DNSDOMAIN": dnsdomain,
2063 "HOSTNAME": hostname,
2064 "REALM": realm,
2065 })
2066
2067
Note: See TracBrowser for help on using the repository browser.