s4:provision - Moved provision_xxx_backend() into backend-specific provision() method.
[ira/wip.git] / source4 / scripting / python / samba / provisionbackend.py
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 (LDB and LDAP backends)."""
27
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
37 from samba import read_and_sub_file
38 from samba import Ldb
39 import urllib
40 from ldb import SCOPE_BASE, SCOPE_ONELEVEL, LdbError, timestring
41 from credentials import Credentials, DONT_USE_KERBEROS
42 from samba import setup_file
43
44 def setup_db_config(setup_path, dbdir):
45     """Setup a Berkeley database.
46     
47     :param setup_path: Setup path function.
48     :param dbdir: Database directory."""
49     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
50         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
51         if not os.path.isdir(os.path.join(dbdir, "tmp")):
52             os.makedirs(os.path.join(dbdir, "tmp"), 0700)
53
54     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
55                {"LDAPDBDIR": dbdir})
56
57 class ProvisionBackend(object):
58     def __init__(self, backend_type, paths=None, setup_path=None, lp=None, credentials=None, 
59                  names=None, message=None, 
60                  hostname=None, root=None, 
61                  schema=None, ldapadminpass=None,
62                  ldap_backend_extra_port=None,
63                  ol_mmr_urls=None, 
64                  setup_ds_path=None, slapd_path=None, 
65                  nosync=False, ldap_dryrun_mode=False,
66                  domainsid=None):
67         """Provision an LDAP backend for samba4
68         
69         This works for OpenLDAP and Fedora DS
70         """
71         self.paths = paths
72         self.setup_path = setup_path
73         self.slapd_command = None
74         self.slapd_command_escaped = None
75         self.lp = lp
76         self.names = names
77         self.message = message
78         self.hostname = hostname
79         self.root = root
80         self.schema = schema
81         self.ldapadminpass = ldapadminpass
82         self.ldap_backend_extra_port = ldap_backend_extra_port
83         self.ol_mmr_urls = ol_mmr_urls
84         self.setup_ds_path = setup_ds_path
85         self.slapd_path = slapd_path
86         self.nosync = nosync
87         self.ldap_dryrun_mode = ldap_dryrun_mode
88         self.domainsid = domainsid
89
90         self.type = backend_type
91         
92         # Set a default - the code for "existing" below replaces this
93         self.ldap_backend_type = backend_type
94
95         if self.type is "ldb":
96             self.credentials = None
97             self.secrets_credentials = None
98     
99             # Wipe the old sam.ldb databases away
100             shutil.rmtree(paths.samdb + ".d", True)
101             return
102
103         self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
104         
105         if self.type == "existing":
106             #Check to see that this 'existing' LDAP backend in fact exists
107             ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
108             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
109                                                 expression="(objectClass=OpenLDAProotDSE)")
110
111             # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
112             self.credentials = credentials
113             # This caused them to be set into the long-term database later in the script.
114             self.secrets_credentials = credentials
115
116             self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
117             return
118     
119         # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
120         # if another instance of slapd is already running 
121         try:
122             ldapi_db = Ldb(self.ldapi_uri)
123             search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
124                                                 expression="(objectClass=OpenLDAProotDSE)");
125             try:
126                 f = open(paths.slapdpid, "r")
127                 p = f.read()
128                 f.close()
129                 message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
130             except:
131                 pass
132             
133             raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
134         
135         except LdbError, e:
136             pass
137
138         # Try to print helpful messages when the user has not specified the path to slapd
139         if slapd_path is None:
140             raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
141         if not os.path.exists(slapd_path):
142             message (slapd_path)
143             raise ProvisioningError("Warning: Given Path to slapd does not exist!")
144
145
146         if not os.path.isdir(paths.ldapdir):
147             os.makedirs(paths.ldapdir, 0700)
148
149         # Put the LDIF of the schema into a database so we can search on
150         # it to generate schema-dependent configurations in Fedora DS and
151         # OpenLDAP
152         schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
153         try:
154             os.unlink(schemadb_path)
155         except OSError:
156             pass
157
158         schema.write_to_tmp_ldb(schemadb_path);
159
160         self.credentials = Credentials()
161         self.credentials.guess(lp)
162         #Kerberos to an ldapi:// backend makes no sense
163         self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
164         self.credentials.set_password(ldapadminpass)
165
166         self.secrets_credentials = Credentials()
167         self.secrets_credentials.guess(lp)
168         #Kerberos to an ldapi:// backend makes no sense
169         self.secrets_credentials.set_kerberos_state(DONT_USE_KERBEROS)
170         self.secrets_credentials.set_username("samba-admin")
171         self.secrets_credentials.set_password(ldapadminpass)
172
173     def provision(self):
174         pass
175
176     def start(self):
177         pass
178
179     def shutdown(self):
180         pass
181
182     def post_setup(self):
183         pass
184
185
186 class LDAPBackend(ProvisionBackend):
187     def start(self):
188         self.slapd_command_escaped = "\'" + "\' \'".join(self.slapd_command) + "\'"
189         setup_file(self.setup_path("ldap_backend_startup.sh"), self.paths.ldapdir + "/ldap_backend_startup.sh", {
190                 "SLAPD_COMMAND" : self.slapd_command_escaped})
191
192         # Now start the slapd, so we can provision onto it.  We keep the
193         # subprocess context around, to kill this off at the successful
194         # end of the script
195         self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
196     
197         while self.slapd.poll() is None:
198             # Wait until the socket appears
199             try:
200                 ldapi_db = Ldb(self.ldapi_uri, lp=self.lp, credentials=self.credentials)
201                 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
202                                                     expression="(objectClass=OpenLDAProotDSE)")
203                 # If we have got here, then we must have a valid connection to the LDAP server!
204                 return
205             except LdbError, e:
206                 time.sleep(1)
207                 pass
208         
209         raise ProvisioningError("slapd died before we could make a connection to it")
210
211     def shutdown(self):
212         # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
213         if self.slapd.poll() is None:
214             #Kill the slapd
215             if hasattr(self.slapd, "terminate"):
216                 self.slapd.terminate()
217             else:
218                 # Older python versions don't have .terminate()
219                 import signal
220                 os.kill(self.slapd.pid, signal.SIGTERM)
221     
222             #and now wait for it to die
223             self.slapd.communicate()
224
225
226 class OpenLDAPBackend(LDAPBackend):
227     def provision(self):
228         # Wipe the directories so we can start
229         shutil.rmtree(os.path.join(self.paths.ldapdir, "db"), True)
230
231         #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
232         nosync_config = ""
233         if self.nosync:
234             nosync_config = "dbnosync"
235         
236         lnkattr = self.schema.linked_attributes()
237         refint_attributes = ""
238         memberof_config = "# Generated from Samba4 schema\n"
239         for att in  lnkattr.keys():
240             if lnkattr[att] is not None:
241                 refint_attributes = refint_attributes + " " + att 
242             
243                 memberof_config += read_and_sub_file(self.setup_path("memberof.conf"),
244                                                  { "MEMBER_ATTR" : att ,
245                                                    "MEMBEROF_ATTR" : lnkattr[att] })
246             
247         refint_config = read_and_sub_file(self.setup_path("refint.conf"),
248                                       { "LINK_ATTRS" : refint_attributes})
249     
250         attrs = ["linkID", "lDAPDisplayName"]
251         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)
252         index_config = ""
253         for i in range (0, len(res)):
254             index_attr = res[i]["lDAPDisplayName"][0]
255             if index_attr == "objectGUID":
256                 index_attr = "entryUUID"
257             
258             index_config += "index " + index_attr + " eq\n"
259
260         # generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
261         mmr_on_config = ""
262         mmr_replicator_acl = ""
263         mmr_serverids_config = ""
264         mmr_syncrepl_schema_config = "" 
265         mmr_syncrepl_config_config = "" 
266         mmr_syncrepl_user_config = "" 
267        
268     
269         if self.ol_mmr_urls is not None:
270             # For now, make these equal
271             mmr_pass = self.ldapadminpass
272         
273             url_list=filter(None,self.ol_mmr_urls.split(' ')) 
274             if (len(url_list) == 1):
275                 url_list=filter(None,self.ol_mmr_urls.split(',')) 
276                      
277             
278                 mmr_on_config = "MirrorMode On"
279                 mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
280                 serverid=0
281                 for url in url_list:
282                     serverid=serverid+1
283                     mmr_serverids_config += read_and_sub_file(self.setup_path("mmr_serverids.conf"),
284                                                           { "SERVERID" : str(serverid),
285                                                             "LDAPSERVER" : url })
286                     rid=serverid*10
287                     rid=rid+1
288                     mmr_syncrepl_schema_config += read_and_sub_file(self.setup_path("mmr_syncrepl.conf"),
289                                                                 {  "RID" : str(rid),
290                                                                    "MMRDN": self.names.schemadn,
291                                                                    "LDAPSERVER" : url,
292                                                                    "MMR_PASSWORD": mmr_pass})
293                 
294                     rid=rid+1
295                     mmr_syncrepl_config_config += read_and_sub_file(self.setup_path("mmr_syncrepl.conf"),
296                                                                 {  "RID" : str(rid),
297                                                                    "MMRDN": self.names.configdn,
298                                                                    "LDAPSERVER" : url,
299                                                                    "MMR_PASSWORD": mmr_pass})
300                 
301                     rid=rid+1
302                     mmr_syncrepl_user_config += read_and_sub_file(self.setup_path("mmr_syncrepl.conf"),
303                                                               {  "RID" : str(rid),
304                                                                  "MMRDN": self.names.domaindn,
305                                                                  "LDAPSERVER" : url,
306                                                                  "MMR_PASSWORD": mmr_pass })
307         # OpenLDAP cn=config initialisation
308         olc_syncrepl_config = ""
309         olc_mmr_config = "" 
310         # if mmr = yes, generate cn=config-replication directives
311         # and olc_seed.lif for the other mmr-servers
312         if self.ol_mmr_urls is not None:
313             serverid=0
314             olc_serverids_config = ""
315             olc_syncrepl_seed_config = ""
316             olc_mmr_config += read_and_sub_file(self.setup_path("olc_mmr.conf"),{})
317             rid=1000
318             for url in url_list:
319                 serverid=serverid+1
320                 olc_serverids_config += read_and_sub_file(self.setup_path("olc_serverid.conf"),
321                                                       { "SERVERID" : str(serverid),
322                                                         "LDAPSERVER" : url })
323             
324                 rid=rid+1
325                 olc_syncrepl_config += read_and_sub_file(self.setup_path("olc_syncrepl.conf"),
326                                                      {  "RID" : str(rid),
327                                                         "LDAPSERVER" : url,
328                                                         "MMR_PASSWORD": mmr_pass})
329             
330                 olc_syncrepl_seed_config += read_and_sub_file(self.setup_path("olc_syncrepl_seed.conf"),
331                                                           {  "RID" : str(rid),
332                                                              "LDAPSERVER" : url})
333                 
334             setup_file(self.setup_path("olc_seed.ldif"), self.paths.olcseedldif,
335                        {"OLC_SERVER_ID_CONF": olc_serverids_config,
336                         "OLC_PW": self.ldapadminpass,
337                         "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
338         # end olc
339                 
340         setup_file(self.setup_path("slapd.conf"), self.paths.slapdconf,
341                    {"DNSDOMAIN": self.names.dnsdomain,
342                     "LDAPDIR": self.paths.ldapdir,
343                     "DOMAINDN": self.names.domaindn,
344                     "CONFIGDN": self.names.configdn,
345                     "SCHEMADN": self.names.schemadn,
346                     "MEMBEROF_CONFIG": memberof_config,
347                     "MIRRORMODE": mmr_on_config,
348                     "REPLICATOR_ACL": mmr_replicator_acl,
349                     "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
350                     "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
351                     "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
352                     "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
353                     "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
354                     "OLC_MMR_CONFIG": olc_mmr_config,
355                     "REFINT_CONFIG": refint_config,
356                     "INDEX_CONFIG": index_config,
357                     "NOSYNC": nosync_config})
358         
359         setup_db_config(self.setup_path, os.path.join(self.paths.ldapdir, "db", "user"))
360         setup_db_config(self.setup_path, os.path.join(self.paths.ldapdir, "db", "config"))
361         setup_db_config(self.setup_path, os.path.join(self.paths.ldapdir, "db", "schema"))
362     
363         if not os.path.exists(os.path.join(self.paths.ldapdir, "db", "samba",  "cn=samba")):
364             os.makedirs(os.path.join(self.paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
365         
366         setup_file(self.setup_path("cn=samba.ldif"), 
367                    os.path.join(self.paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
368                    { "UUID": str(uuid.uuid4()), 
369                      "LDAPTIME": timestring(int(time.time()))} )
370         setup_file(self.setup_path("cn=samba-admin.ldif"), 
371                    os.path.join(self.paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
372                    {"LDAPADMINPASS_B64": b64encode(self.ldapadminpass),
373                     "UUID": str(uuid.uuid4()), 
374                     "LDAPTIME": timestring(int(time.time()))} )
375     
376         if self.ol_mmr_urls is not None:
377             setup_file(self.setup_path("cn=replicator.ldif"),
378                        os.path.join(self.paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
379                        {"MMR_PASSWORD_B64": b64encode(mmr_pass),
380                         "UUID": str(uuid.uuid4()),
381                         "LDAPTIME": timestring(int(time.time()))} )
382         
383
384         mapping = "schema-map-openldap-2.3"
385         backend_schema = "backend-schema.schema"
386
387         backend_schema_data = self.schema.ldb.convert_schema_to_openldap("openldap", open(self.setup_path(mapping), 'r').read())
388         assert backend_schema_data is not None
389         open(os.path.join(self.paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
390
391         # now we generate the needed strings to start slapd automatically,
392         # first ldapi_uri...
393         if self.ldap_backend_extra_port is not None:
394             # When we use MMR, we can't use 0.0.0.0 as it uses the name
395             # specified there as part of it's clue as to it's own name,
396             # and not to replicate to itself
397             if self.ol_mmr_urls is None:
398                 server_port_string = "ldap://0.0.0.0:%d" % self.ldap_backend_extra_port
399             else:
400                 server_port_string = "ldap://" + self.names.hostname + "." + self.names.dnsdomain +":%d" % self.ldap_backend_extra_port
401         else:
402             server_port_string = ""
403
404         # Prepare the 'result' information - the commands to return in particular
405         self.slapd_provision_command = [self.slapd_path]
406
407         self.slapd_provision_command.append("-F" + self.paths.olcdir)
408
409         self.slapd_provision_command.append("-h")
410
411         # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
412         self.slapd_command = list(self.slapd_provision_command)
413     
414         self.slapd_provision_command.append(self.ldapi_uri)
415         self.slapd_provision_command.append("-d0")
416
417         uris = self.ldapi_uri
418         if server_port_string is not "":
419             uris = uris + " " + server_port_string
420
421         self.slapd_command.append(uris)
422
423         # Set the username - done here because Fedora DS still uses the admin DN and simple bind
424         self.credentials.set_username("samba-admin")
425     
426         # If we were just looking for crashes up to this point, it's a
427         # good time to exit before we realise we don't have OpenLDAP on
428         # this system
429         if self.ldap_dryrun_mode:
430             sys.exit(0)
431
432         # Finally, convert the configuration into cn=config style!
433         if not os.path.isdir(self.paths.olcdir):
434             os.makedirs(self.paths.olcdir, 0770)
435
436             retcode = subprocess.call([self.slapd_path, "-Ttest", "-f", self.paths.slapdconf, "-F", self.paths.olcdir], close_fds=True, shell=False)
437
438 #            We can't do this, as OpenLDAP is strange.  It gives an error
439 #            output to the above, but does the conversion sucessfully...
440 #
441 #            if retcode != 0:
442 #                raise ProvisioningError("conversion from slapd.conf to cn=config failed")
443
444             if not os.path.exists(os.path.join(self.paths.olcdir, "cn=config.ldif")):
445                 raise ProvisioningError("conversion from slapd.conf to cn=config failed")
446
447             # Don't confuse the admin by leaving the slapd.conf around
448             os.remove(self.paths.slapdconf)        
449
450
451 class FDSBackend(LDAPBackend):
452     def provision(self):
453         if self.ldap_backend_extra_port is not None:
454             serverport = "ServerPort=%d" % self.ldap_backend_extra_port
455         else:
456             serverport = ""
457         
458         setup_file(self.setup_path("fedorads.inf"), self.paths.fedoradsinf, 
459                    {"ROOT": self.root,
460                     "HOSTNAME": self.hostname,
461                     "DNSDOMAIN": self.names.dnsdomain,
462                     "LDAPDIR": self.paths.ldapdir,
463                     "DOMAINDN": self.names.domaindn,
464                     "LDAPMANAGERDN": self.names.ldapmanagerdn,
465                     "LDAPMANAGERPASS": self.ldapadminpass, 
466                     "SERVERPORT": serverport})
467
468         setup_file(self.setup_path("fedorads-partitions.ldif"), self.paths.fedoradspartitions, 
469                    {"CONFIGDN": self.names.configdn,
470                     "SCHEMADN": self.names.schemadn,
471                     "SAMBADN": self.names.sambadn,
472                     })
473
474         setup_file(self.setup_path("fedorads-sasl.ldif"), self.paths.fedoradssasl, 
475                    {"SAMBADN": self.names.sambadn,
476                     })
477
478         setup_file(self.setup_path("fedorads-dna.ldif"), self.paths.fedoradsdna, 
479                    {"DOMAINDN": self.names.domaindn,
480                     "SAMBADN": self.names.sambadn,
481                     "DOMAINSID": str(self.domainsid),
482                     })
483
484         setup_file(self.setup_path("fedorads-pam.ldif"), self.paths.fedoradspam)
485
486         lnkattr = self.schema.linked_attributes()
487
488         refint_config = data = open(self.setup_path("fedorads-refint-delete.ldif"), 'r').read()
489         memberof_config = ""
490         index_config = ""
491         argnum = 3
492
493         for attr in lnkattr.keys():
494             if lnkattr[attr] is not None:
495                 refint_config += read_and_sub_file(self.setup_path("fedorads-refint-add.ldif"),
496                                                  { "ARG_NUMBER" : str(argnum) ,
497                                                    "LINK_ATTR" : attr })
498                 memberof_config += read_and_sub_file(self.setup_path("fedorads-linked-attributes.ldif"),
499                                                  { "MEMBER_ATTR" : attr ,
500                                                    "MEMBEROF_ATTR" : lnkattr[attr] })
501                 index_config += read_and_sub_file(self.setup_path("fedorads-index.ldif"),
502                                                  { "ATTR" : attr })
503                 argnum += 1
504
505         open(self.paths.fedoradsrefint, 'w').write(refint_config)
506         open(self.paths.fedoradslinkedattributes, 'w').write(memberof_config)
507
508         attrs = ["lDAPDisplayName"]
509         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)
510
511         for i in range (0, len(res)):
512             attr = res[i]["lDAPDisplayName"][0]
513
514             if attr == "objectGUID":
515                 attr = "nsUniqueId"
516
517             index_config += read_and_sub_file(self.setup_path("fedorads-index.ldif"),
518                                              { "ATTR" : attr })
519
520         open(self.paths.fedoradsindex, 'w').write(index_config)
521
522         setup_file(self.setup_path("fedorads-samba.ldif"), self.paths.fedoradssamba,
523                     {"SAMBADN": self.names.sambadn, 
524                      "LDAPADMINPASS": self.ldapadminpass
525                     })
526
527         mapping = "schema-map-fedora-ds-1.0"
528         backend_schema = "99_ad.ldif"
529     
530         # Build a schema file in Fedora DS format
531         backend_schema_data = self.schema.ldb.convert_schema_to_openldap("fedora-ds", open(self.setup_path(mapping), 'r').read())
532         assert backend_schema_data is not None
533         open(os.path.join(self.paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
534
535         self.credentials.set_bind_dn(self.names.ldapmanagerdn)
536
537         # Destory the target directory, or else setup-ds.pl will complain
538         fedora_ds_dir = os.path.join(self.paths.ldapdir, "slapd-samba4")
539         shutil.rmtree(fedora_ds_dir, True)
540
541         self.slapd_provision_command = [self.slapd_path, "-D", fedora_ds_dir, "-i", self.paths.slapdpid];
542         #In the 'provision' command line, stay in the foreground so we can easily kill it
543         self.slapd_provision_command.append("-d0")
544
545         #the command for the final run is the normal script
546         self.slapd_command = [os.path.join(self.paths.ldapdir, "slapd-samba4", "start-slapd")]
547
548         # If we were just looking for crashes up to this point, it's a
549         # good time to exit before we realise we don't have Fedora DS on
550         if self.ldap_dryrun_mode:
551             sys.exit(0)
552
553         # Try to print helpful messages when the user has not specified the path to the setup-ds tool
554         if self.setup_ds_path is None:
555             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\"!")
556         if not os.path.exists(self.setup_ds_path):
557             self.message (self.setup_ds_path)
558             raise ProvisioningError("Warning: Given Path to slapd does not exist!")
559
560         # Run the Fedora DS setup utility
561         retcode = subprocess.call([self.setup_ds_path, "--silent", "--file", self.paths.fedoradsinf], close_fds=True, shell=False)
562         if retcode != 0:
563             raise ProvisioningError("setup-ds failed")
564
565         # Load samba-admin
566         retcode = subprocess.call([
567             os.path.join(self.paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", self.names.sambadn, "-i", self.paths.fedoradssamba],
568             close_fds=True, shell=False)
569         if retcode != 0:
570             raise("ldib2db failed")
571
572     def post_setup(self):
573         ldapi_db = Ldb(self.ldapi_uri, credentials=self.credentials)
574
575         # delete default SASL mappings
576         res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
577     
578         # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
579         for i in range (0, len(res)):
580             dn = str(res[i]["dn"])
581             ldapi_db.delete(dn)
582             
583             aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % self.names.sambadn
584         
585             m = ldb.Message()
586             m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
587
588             m.dn = ldb.Dn(1, self.names.domaindn)
589             ldapi_db.modify(m)
590             
591             m.dn = ldb.Dn(1, self.names.configdn)
592             ldapi_db.modify(m)
593             
594             m.dn = ldb.Dn(1, self.names.schemadn)
595             ldapi_db.modify(m)