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