s4-drs: Add DRSUAPI_DRS_NONGC_RO_REP bit to DRS_OPTIONS
[Samba/fernandojvsilva.git] / source4 / scripting / python / samba / provisionbackend.py
blob3dff6eded4b71beb029f9b010764f695072fe0e2
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 (LDB and LDAP backends)."""
28 from base64 import b64encode
29 import ldb
30 import os
31 import sys
32 import uuid
33 import time
34 import shutil
35 import subprocess
36 import urllib
38 from ldb import SCOPE_BASE, SCOPE_ONELEVEL, LdbError, timestring
40 from samba import Ldb, read_and_sub_file, setup_file
41 from samba.credentials import Credentials, DONT_USE_KERBEROS
42 from samba.schema import Schema
43 from samba.provisionexceptions import ProvisioningError
45 def setup_db_config(setup_path, dbdir):
46 """Setup a Berkeley database.
48 :param setup_path: Setup path function.
49 :param dbdir: Database directory."""
50 if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
51 os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
52 if not os.path.isdir(os.path.join(dbdir, "tmp")):
53 os.makedirs(os.path.join(dbdir, "tmp"), 0700)
55 setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
56 {"LDAPDBDIR": dbdir})
58 class ProvisionBackend(object):
59 def __init__(self, backend_type, paths=None, setup_path=None, lp=None, credentials=None,
60 names=None, message=None):
61 """Provision a backend for samba4"""
62 self.paths = paths
63 self.setup_path = setup_path
64 self.lp = lp
65 self.credentials = credentials
66 self.names = names
67 self.message = message
69 self.type = backend_type
71 # Set a default - the code for "existing" below replaces this
72 self.ldap_backend_type = backend_type
74 def init(self):
75 pass
77 def start(self):
78 pass
80 def shutdown(self):
81 pass
83 def post_setup(self):
84 pass
87 class LDBBackend(ProvisionBackend):
88 def __init__(self, backend_type, paths=None, setup_path=None, lp=None, credentials=None,
89 names=None, message=None):
91 super(LDBBackend, self).__init__(
92 backend_type=backend_type,
93 paths=paths, setup_path=setup_path,
94 lp=lp, credentials=credentials,
95 names=names,
96 message=message)
98 def init(self):
99 self.credentials = None
100 self.secrets_credentials = None
102 # Wipe the old sam.ldb databases away
103 shutil.rmtree(self.paths.samdb + ".d", True)
106 class ExistingBackend(ProvisionBackend):
107 def __init__(self, backend_type, paths=None, setup_path=None, lp=None, credentials=None,
108 names=None, message=None):
110 super(ExistingBackend, self).__init__(
111 backend_type=backend_type,
112 paths=paths, setup_path=setup_path,
113 lp=lp, credentials=credentials,
114 names=names,
115 message=message)
117 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
119 def init(self):
120 #Check to see that this 'existing' LDAP backend in fact exists
121 ldapi_db = Ldb(self.ldapi_uri, credentials=self.credentials)
122 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
123 expression="(objectClass=OpenLDAProotDSE)")
125 # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
126 # This caused them to be set into the long-term database later in the script.
127 self.secrets_credentials = self.credentials
129 self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
132 class LDAPBackend(ProvisionBackend):
133 def __init__(self, backend_type, paths=None, setup_path=None, lp=None, credentials=None,
134 names=None, message=None,
135 domainsid=None,
136 schema=None,
137 hostname=None,
138 ldapadminpass=None,
139 slapd_path=None,
140 ldap_backend_extra_port=None,
141 ldap_dryrun_mode=False):
143 super(LDAPBackend, self).__init__(
144 backend_type=backend_type,
145 paths=paths, setup_path=setup_path,
146 lp=lp, credentials=credentials,
147 names=names,
148 message=message)
150 self.domainsid = domainsid
151 self.schema = schema
152 self.hostname = hostname
153 self.ldapadminpass = ldapadminpass
155 self.slapd_path = slapd_path
156 self.slapd_command = None
157 self.slapd_command_escaped = None
159 self.ldap_backend_extra_port = ldap_backend_extra_port
160 self.ldap_dryrun_mode = ldap_dryrun_mode
162 self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
164 if not os.path.exists(self.paths.ldapdir):
165 os.mkdir(self.paths.ldapdir)
167 def init(self):
168 # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
169 # if another instance of slapd is already running
170 try:
171 ldapi_db = Ldb(self.ldapi_uri)
172 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
173 expression="(objectClass=OpenLDAProotDSE)");
174 try:
175 f = open(self.paths.slapdpid, "r")
176 p = f.read()
177 f.close()
178 self.message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
179 except:
180 pass
182 raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
184 except LdbError, e:
185 pass
187 # Try to print helpful messages when the user has not specified the path to slapd
188 if self.slapd_path is None:
189 raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
190 if not os.path.exists(self.slapd_path):
191 self.message (self.slapd_path)
192 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
195 if not os.path.isdir(self.paths.ldapdir):
196 os.makedirs(self.paths.ldapdir, 0700)
198 # Put the LDIF of the schema into a database so we can search on
199 # it to generate schema-dependent configurations in Fedora DS and
200 # OpenLDAP
201 schemadb_path = os.path.join(self.paths.ldapdir, "schema-tmp.ldb")
202 try:
203 os.unlink(schemadb_path)
204 except OSError:
205 pass
207 self.schema.write_to_tmp_ldb(schemadb_path);
209 self.credentials = Credentials()
210 self.credentials.guess(self.lp)
211 #Kerberos to an ldapi:// backend makes no sense
212 self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
213 self.credentials.set_password(self.ldapadminpass)
215 self.secrets_credentials = Credentials()
216 self.secrets_credentials.guess(self.lp)
217 #Kerberos to an ldapi:// backend makes no sense
218 self.secrets_credentials.set_kerberos_state(DONT_USE_KERBEROS)
219 self.secrets_credentials.set_username("samba-admin")
220 self.secrets_credentials.set_password(self.ldapadminpass)
222 self.provision()
224 def provision(self):
225 pass
227 def start(self):
228 self.slapd_command_escaped = "\'" + "\' \'".join(self.slapd_command) + "\'"
229 open(self.paths.ldapdir + "/ldap_backend_startup.sh", 'w').write("#!/bin/sh\n" + self.slapd_command_escaped + "\n")
231 # Now start the slapd, so we can provision onto it. We keep the
232 # subprocess context around, to kill this off at the successful
233 # end of the script
234 self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
236 while self.slapd.poll() is None:
237 # Wait until the socket appears
238 try:
239 ldapi_db = Ldb(self.ldapi_uri, lp=self.lp, credentials=self.credentials)
240 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
241 expression="(objectClass=OpenLDAProotDSE)")
242 # If we have got here, then we must have a valid connection to the LDAP server!
243 return
244 except LdbError, e:
245 time.sleep(1)
246 pass
248 raise ProvisioningError("slapd died before we could make a connection to it")
250 def shutdown(self):
251 # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
252 if self.slapd.poll() is None:
253 #Kill the slapd
254 if hasattr(self.slapd, "terminate"):
255 self.slapd.terminate()
256 else:
257 # Older python versions don't have .terminate()
258 import signal
259 os.kill(self.slapd.pid, signal.SIGTERM)
261 #and now wait for it to die
262 self.slapd.communicate()
265 class OpenLDAPBackend(LDAPBackend):
266 def __init__(self, backend_type, paths=None, setup_path=None, lp=None, credentials=None,
267 names=None, message=None,
268 domainsid=None,
269 schema=None,
270 hostname=None,
271 ldapadminpass=None,
272 slapd_path=None,
273 ldap_backend_extra_port=None,
274 ldap_dryrun_mode=False,
275 ol_mmr_urls=None,
276 nosync=False):
278 super(OpenLDAPBackend, self).__init__(
279 backend_type=backend_type,
280 paths=paths, setup_path=setup_path,
281 lp=lp, credentials=credentials,
282 names=names,
283 message=message,
284 domainsid=domainsid,
285 schema=schema,
286 hostname=hostname,
287 ldapadminpass=ldapadminpass,
288 slapd_path=slapd_path,
289 ldap_backend_extra_port=ldap_backend_extra_port,
290 ldap_dryrun_mode=ldap_dryrun_mode)
292 self.ol_mmr_urls = ol_mmr_urls
293 self.nosync = nosync
295 self.schema = Schema(
296 self.setup_path,
297 self.domainsid,
298 schemadn=self.names.schemadn,
299 serverdn=self.names.serverdn,
300 files=[setup_path("schema_samba4.ldif")]);
302 def provision(self):
303 # Wipe the directories so we can start
304 shutil.rmtree(os.path.join(self.paths.ldapdir, "db"), True)
306 #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
307 nosync_config = ""
308 if self.nosync:
309 nosync_config = "dbnosync"
311 lnkattr = self.schema.linked_attributes()
312 refint_attributes = ""
313 memberof_config = "# Generated from Samba4 schema\n"
314 for att in lnkattr.keys():
315 if lnkattr[att] is not None:
316 refint_attributes = refint_attributes + " " + att
318 memberof_config += read_and_sub_file(self.setup_path("memberof.conf"),
319 { "MEMBER_ATTR" : att ,
320 "MEMBEROF_ATTR" : lnkattr[att] })
322 refint_config = read_and_sub_file(self.setup_path("refint.conf"),
323 { "LINK_ATTRS" : refint_attributes})
325 attrs = ["linkID", "lDAPDisplayName"]
326 res = self.schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=self.names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
327 index_config = ""
328 for i in range (0, len(res)):
329 index_attr = res[i]["lDAPDisplayName"][0]
330 if index_attr == "objectGUID":
331 index_attr = "entryUUID"
333 index_config += "index " + index_attr + " eq\n"
335 # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
336 mmr_on_config = ""
337 mmr_replicator_acl = ""
338 mmr_serverids_config = ""
339 mmr_syncrepl_schema_config = ""
340 mmr_syncrepl_config_config = ""
341 mmr_syncrepl_user_config = ""
344 if self.ol_mmr_urls is not None:
345 # For now, make these equal
346 mmr_pass = self.ldapadminpass
348 url_list=filter(None,self.ol_mmr_urls.split(' '))
349 if (len(url_list) == 1):
350 url_list=filter(None,self.ol_mmr_urls.split(','))
353 mmr_on_config = "MirrorMode On"
354 mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
355 serverid=0
356 for url in url_list:
357 serverid=serverid+1
358 mmr_serverids_config += read_and_sub_file(self.setup_path("mmr_serverids.conf"),
359 { "SERVERID" : str(serverid),
360 "LDAPSERVER" : url })
361 rid=serverid*10
362 rid=rid+1
363 mmr_syncrepl_schema_config += read_and_sub_file(self.setup_path("mmr_syncrepl.conf"),
364 { "RID" : str(rid),
365 "MMRDN": self.names.schemadn,
366 "LDAPSERVER" : url,
367 "MMR_PASSWORD": mmr_pass})
369 rid=rid+1
370 mmr_syncrepl_config_config += read_and_sub_file(self.setup_path("mmr_syncrepl.conf"),
371 { "RID" : str(rid),
372 "MMRDN": self.names.configdn,
373 "LDAPSERVER" : url,
374 "MMR_PASSWORD": mmr_pass})
376 rid=rid+1
377 mmr_syncrepl_user_config += read_and_sub_file(self.setup_path("mmr_syncrepl.conf"),
378 { "RID" : str(rid),
379 "MMRDN": self.names.domaindn,
380 "LDAPSERVER" : url,
381 "MMR_PASSWORD": mmr_pass })
382 # OpenLDAP cn=config initialisation
383 olc_syncrepl_config = ""
384 olc_mmr_config = ""
385 # if mmr = yes, generate cn=config-replication directives
386 # and olc_seed.lif for the other mmr-servers
387 if self.ol_mmr_urls is not None:
388 serverid=0
389 olc_serverids_config = ""
390 olc_syncrepl_seed_config = ""
391 olc_mmr_config += read_and_sub_file(self.setup_path("olc_mmr.conf"),{})
392 rid=1000
393 for url in url_list:
394 serverid=serverid+1
395 olc_serverids_config += read_and_sub_file(self.setup_path("olc_serverid.conf"),
396 { "SERVERID" : str(serverid),
397 "LDAPSERVER" : url })
399 rid=rid+1
400 olc_syncrepl_config += read_and_sub_file(self.setup_path("olc_syncrepl.conf"),
401 { "RID" : str(rid),
402 "LDAPSERVER" : url,
403 "MMR_PASSWORD": mmr_pass})
405 olc_syncrepl_seed_config += read_and_sub_file(self.setup_path("olc_syncrepl_seed.conf"),
406 { "RID" : str(rid),
407 "LDAPSERVER" : url})
409 setup_file(self.setup_path("olc_seed.ldif"), self.paths.olcseedldif,
410 {"OLC_SERVER_ID_CONF": olc_serverids_config,
411 "OLC_PW": self.ldapadminpass,
412 "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
413 # end olc
415 setup_file(self.setup_path("slapd.conf"), self.paths.slapdconf,
416 {"DNSDOMAIN": self.names.dnsdomain,
417 "LDAPDIR": self.paths.ldapdir,
418 "DOMAINDN": self.names.domaindn,
419 "CONFIGDN": self.names.configdn,
420 "SCHEMADN": self.names.schemadn,
421 "MEMBEROF_CONFIG": memberof_config,
422 "MIRRORMODE": mmr_on_config,
423 "REPLICATOR_ACL": mmr_replicator_acl,
424 "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
425 "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
426 "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
427 "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
428 "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
429 "OLC_MMR_CONFIG": olc_mmr_config,
430 "REFINT_CONFIG": refint_config,
431 "INDEX_CONFIG": index_config,
432 "NOSYNC": nosync_config})
434 setup_db_config(self.setup_path, os.path.join(self.paths.ldapdir, "db", "user"))
435 setup_db_config(self.setup_path, os.path.join(self.paths.ldapdir, "db", "config"))
436 setup_db_config(self.setup_path, os.path.join(self.paths.ldapdir, "db", "schema"))
438 if not os.path.exists(os.path.join(self.paths.ldapdir, "db", "samba", "cn=samba")):
439 os.makedirs(os.path.join(self.paths.ldapdir, "db", "samba", "cn=samba"), 0700)
441 setup_file(self.setup_path("cn=samba.ldif"),
442 os.path.join(self.paths.ldapdir, "db", "samba", "cn=samba.ldif"),
443 { "UUID": str(uuid.uuid4()),
444 "LDAPTIME": timestring(int(time.time()))} )
445 setup_file(self.setup_path("cn=samba-admin.ldif"),
446 os.path.join(self.paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
447 {"LDAPADMINPASS_B64": b64encode(self.ldapadminpass),
448 "UUID": str(uuid.uuid4()),
449 "LDAPTIME": timestring(int(time.time()))} )
451 if self.ol_mmr_urls is not None:
452 setup_file(self.setup_path("cn=replicator.ldif"),
453 os.path.join(self.paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
454 {"MMR_PASSWORD_B64": b64encode(mmr_pass),
455 "UUID": str(uuid.uuid4()),
456 "LDAPTIME": timestring(int(time.time()))} )
459 mapping = "schema-map-openldap-2.3"
460 backend_schema = "backend-schema.schema"
462 backend_schema_data = self.schema.ldb.convert_schema_to_openldap("openldap", open(self.setup_path(mapping), 'r').read())
463 assert backend_schema_data is not None
464 open(os.path.join(self.paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
466 # now we generate the needed strings to start slapd automatically,
467 # first ldapi_uri...
468 if self.ldap_backend_extra_port is not None:
469 # When we use MMR, we can't use 0.0.0.0 as it uses the name
470 # specified there as part of it's clue as to it's own name,
471 # and not to replicate to itself
472 if self.ol_mmr_urls is None:
473 server_port_string = "ldap://0.0.0.0:%d" % self.ldap_backend_extra_port
474 else:
475 server_port_string = "ldap://" + self.names.hostname + "." + self.names.dnsdomain +":%d" % self.ldap_backend_extra_port
476 else:
477 server_port_string = ""
479 # Prepare the 'result' information - the commands to return in particular
480 self.slapd_provision_command = [self.slapd_path]
482 self.slapd_provision_command.append("-F" + self.paths.olcdir)
484 self.slapd_provision_command.append("-h")
486 # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
487 self.slapd_command = list(self.slapd_provision_command)
489 self.slapd_provision_command.append(self.ldapi_uri)
490 self.slapd_provision_command.append("-d0")
492 uris = self.ldapi_uri
493 if server_port_string is not "":
494 uris = uris + " " + server_port_string
496 self.slapd_command.append(uris)
498 # Set the username - done here because Fedora DS still uses the admin DN and simple bind
499 self.credentials.set_username("samba-admin")
501 # If we were just looking for crashes up to this point, it's a
502 # good time to exit before we realise we don't have OpenLDAP on
503 # this system
504 if self.ldap_dryrun_mode:
505 sys.exit(0)
507 # Finally, convert the configuration into cn=config style!
508 if not os.path.isdir(self.paths.olcdir):
509 os.makedirs(self.paths.olcdir, 0770)
511 retcode = subprocess.call([self.slapd_path, "-Ttest", "-f", self.paths.slapdconf, "-F", self.paths.olcdir], close_fds=True, shell=False)
513 # We can't do this, as OpenLDAP is strange. It gives an error
514 # output to the above, but does the conversion sucessfully...
516 # if retcode != 0:
517 # raise ProvisioningError("conversion from slapd.conf to cn=config failed")
519 if not os.path.exists(os.path.join(self.paths.olcdir, "cn=config.ldif")):
520 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
522 # Don't confuse the admin by leaving the slapd.conf around
523 os.remove(self.paths.slapdconf)
526 class FDSBackend(LDAPBackend):
527 def __init__(self, backend_type, paths=None, setup_path=None, lp=None, credentials=None,
528 names=None, message=None,
529 domainsid=None,
530 schema=None,
531 hostname=None,
532 ldapadminpass=None,
533 slapd_path=None,
534 ldap_backend_extra_port=None,
535 ldap_dryrun_mode=False,
536 root=None,
537 setup_ds_path=None):
539 super(FDSBackend, self).__init__(
540 backend_type=backend_type,
541 paths=paths, setup_path=setup_path,
542 lp=lp, credentials=credentials,
543 names=names,
544 message=message,
545 domainsid=domainsid,
546 schema=schema,
547 hostname=hostname,
548 ldapadminpass=ldapadminpass,
549 slapd_path=slapd_path,
550 ldap_backend_extra_port=ldap_backend_extra_port,
551 ldap_dryrun_mode=ldap_dryrun_mode)
553 self.root = root
554 self.setup_ds_path = setup_ds_path
556 self.sambadn = "CN=Samba"
558 self.fedoradsinf = os.path.join(paths.ldapdir, "fedorads.inf")
559 self.partitions_ldif = os.path.join(paths.ldapdir, "fedorads-partitions.ldif")
560 self.sasl_ldif = os.path.join(paths.ldapdir, "fedorads-sasl.ldif")
561 self.dna_ldif = os.path.join(paths.ldapdir, "fedorads-dna.ldif")
562 self.pam_ldif = os.path.join(paths.ldapdir, "fedorads-pam.ldif")
563 self.refint_ldif = os.path.join(paths.ldapdir, "fedorads-refint.ldif")
564 self.linked_attrs_ldif = os.path.join(paths.ldapdir, "fedorads-linked-attributes.ldif")
565 self.index_ldif = os.path.join(paths.ldapdir, "fedorads-index.ldif")
566 self.samba_ldif = os.path.join(paths.ldapdir, "fedorads-samba.ldif")
568 self.samba3_schema = self.setup_path("../../examples/LDAP/samba.schema")
569 self.samba3_ldif = os.path.join(self.paths.ldapdir, "samba3.ldif")
571 self.retcode = subprocess.call(["bin/oLschema2ldif", "-H", "NONE",
572 "-I", self.samba3_schema,
573 "-O", self.samba3_ldif,
574 "-b", self.names.domaindn],
575 close_fds=True, shell=False)
577 if self.retcode != 0:
578 raise Exception("Unable to convert Samba 3 schema.")
580 self.schema = Schema(
581 self.setup_path,
582 self.domainsid,
583 schemadn=self.names.schemadn,
584 serverdn=self.names.serverdn,
585 files=[setup_path("schema_samba4.ldif"), self.samba3_ldif],
586 prefixmap=["1000:1.3.6.1.4.1.7165.2.1", "1001:1.3.6.1.4.1.7165.2.2"])
588 def provision(self):
589 if self.ldap_backend_extra_port is not None:
590 serverport = "ServerPort=%d" % self.ldap_backend_extra_port
591 else:
592 serverport = ""
594 setup_file(self.setup_path("fedorads.inf"), self.fedoradsinf,
595 {"ROOT": self.root,
596 "HOSTNAME": self.hostname,
597 "DNSDOMAIN": self.names.dnsdomain,
598 "LDAPDIR": self.paths.ldapdir,
599 "DOMAINDN": self.names.domaindn,
600 "LDAPMANAGERDN": self.names.ldapmanagerdn,
601 "LDAPMANAGERPASS": self.ldapadminpass,
602 "SERVERPORT": serverport})
604 setup_file(self.setup_path("fedorads-partitions.ldif"), self.partitions_ldif,
605 {"CONFIGDN": self.names.configdn,
606 "SCHEMADN": self.names.schemadn,
607 "SAMBADN": self.sambadn,
610 setup_file(self.setup_path("fedorads-sasl.ldif"), self.sasl_ldif,
611 {"SAMBADN": self.sambadn,
614 setup_file(self.setup_path("fedorads-dna.ldif"), self.dna_ldif,
615 {"DOMAINDN": self.names.domaindn,
616 "SAMBADN": self.sambadn,
617 "DOMAINSID": str(self.domainsid),
620 setup_file(self.setup_path("fedorads-pam.ldif"), self.pam_ldif)
622 lnkattr = self.schema.linked_attributes()
624 refint_config = data = open(self.setup_path("fedorads-refint-delete.ldif"), 'r').read()
625 memberof_config = ""
626 index_config = ""
627 argnum = 3
629 for attr in lnkattr.keys():
630 if lnkattr[attr] is not None:
631 refint_config += read_and_sub_file(self.setup_path("fedorads-refint-add.ldif"),
632 { "ARG_NUMBER" : str(argnum) ,
633 "LINK_ATTR" : attr })
634 memberof_config += read_and_sub_file(self.setup_path("fedorads-linked-attributes.ldif"),
635 { "MEMBER_ATTR" : attr ,
636 "MEMBEROF_ATTR" : lnkattr[attr] })
637 index_config += read_and_sub_file(self.setup_path("fedorads-index.ldif"),
638 { "ATTR" : attr })
639 argnum += 1
641 open(self.refint_ldif, 'w').write(refint_config)
642 open(self.linked_attrs_ldif, 'w').write(memberof_config)
644 attrs = ["lDAPDisplayName"]
645 res = self.schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=self.names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
647 for i in range (0, len(res)):
648 attr = res[i]["lDAPDisplayName"][0]
650 if attr == "objectGUID":
651 attr = "nsUniqueId"
653 index_config += read_and_sub_file(self.setup_path("fedorads-index.ldif"),
654 { "ATTR" : attr })
656 open(self.index_ldif, 'w').write(index_config)
658 setup_file(self.setup_path("fedorads-samba.ldif"), self.samba_ldif,
659 {"SAMBADN": self.sambadn,
660 "LDAPADMINPASS": self.ldapadminpass
663 mapping = "schema-map-fedora-ds-1.0"
664 backend_schema = "99_ad.ldif"
666 # Build a schema file in Fedora DS format
667 backend_schema_data = self.schema.ldb.convert_schema_to_openldap("fedora-ds", open(self.setup_path(mapping), 'r').read())
668 assert backend_schema_data is not None
669 open(os.path.join(self.paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
671 self.credentials.set_bind_dn(self.names.ldapmanagerdn)
673 # Destory the target directory, or else setup-ds.pl will complain
674 fedora_ds_dir = os.path.join(self.paths.ldapdir, "slapd-samba4")
675 shutil.rmtree(fedora_ds_dir, True)
677 self.slapd_provision_command = [self.slapd_path, "-D", fedora_ds_dir, "-i", self.paths.slapdpid];
678 #In the 'provision' command line, stay in the foreground so we can easily kill it
679 self.slapd_provision_command.append("-d0")
681 #the command for the final run is the normal script
682 self.slapd_command = [os.path.join(self.paths.ldapdir, "slapd-samba4", "start-slapd")]
684 # If we were just looking for crashes up to this point, it's a
685 # good time to exit before we realise we don't have Fedora DS on
686 if self.ldap_dryrun_mode:
687 sys.exit(0)
689 # Try to print helpful messages when the user has not specified the path to the setup-ds tool
690 if self.setup_ds_path is None:
691 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\"!")
692 if not os.path.exists(self.setup_ds_path):
693 self.message (self.setup_ds_path)
694 raise ProvisioningError("Warning: Given Path to slapd does not exist!")
696 # Run the Fedora DS setup utility
697 retcode = subprocess.call([self.setup_ds_path, "--silent", "--file", self.fedoradsinf], close_fds=True, shell=False)
698 if retcode != 0:
699 raise ProvisioningError("setup-ds failed")
701 # Load samba-admin
702 retcode = subprocess.call([
703 os.path.join(self.paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", self.sambadn, "-i", self.samba_ldif],
704 close_fds=True, shell=False)
705 if retcode != 0:
706 raise ProvisioningError("ldif2db failed")
708 def post_setup(self):
709 ldapi_db = Ldb(self.ldapi_uri, credentials=self.credentials)
711 # delete default SASL mappings
712 res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
714 # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
715 for i in range (0, len(res)):
716 dn = str(res[i]["dn"])
717 ldapi_db.delete(dn)
719 aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % self.sambadn
721 m = ldb.Message()
722 m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
724 m.dn = ldb.Dn(ldapi_db, self.names.domaindn)
725 ldapi_db.modify(m)
727 m.dn = ldb.Dn(ldapi_db, self.names.configdn)
728 ldapi_db.modify(m)
730 m.dn = ldb.Dn(ldapi_db, self.names.schemadn)
731 ldapi_db.modify(m)