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