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