GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / lib / dlopen.c
1 /* dlopen.c--Unix dlopen() dynamic loader interface
2  * Rob Siemborski
3  * Rob Earhart
4  * $Id: dlopen.c,v 1.49 2005/03/15 13:33:30 mel Exp $
5  */
6 /* 
7  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer. 
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  *
21  * 3. The name "Carnegie Mellon University" must not be used to
22  *    endorse or promote products derived from this software without
23  *    prior written permission. For permission or any other legal
24  *    details, please contact  
25  *      Office of Technology Transfer
26  *      Carnegie Mellon University
27  *      5000 Forbes Avenue
28  *      Pittsburgh, PA  15213-3890
29  *      (412) 268-4387, fax: (412) 268-7395
30  *      tech-transfer@andrew.cmu.edu
31  *
32  * 4. Redistributions of any form whatsoever must retain the following
33  *    acknowledgment:
34  *    "This product includes software developed by Computing Services
35  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
36  *
37  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
38  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
39  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
40  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
41  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
42  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
43  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
44  */
45
46 #include <config.h>
47 #ifdef HAVE_DLFCN_H
48 #include <dlfcn.h>
49 #endif
50
51 #include <stdlib.h>
52 #include <errno.h>
53 #include <stdio.h>
54 #include <limits.h>
55
56 #include <sasl.h>
57 #include "saslint.h"
58
59 #ifndef PIC
60 #include <saslplug.h>
61 #include "staticopen.h"
62 #endif
63
64 #ifdef DO_DLOPEN
65 #if HAVE_DIRENT_H
66 # include <dirent.h>
67 # define NAMLEN(dirent) strlen((dirent)->d_name)
68 #else /* HAVE_DIRENT_H */
69 # define dirent direct
70 # define NAMLEN(dirent) (dirent)->d_namlen
71 # if HAVE_SYS_NDIR_H
72 #  include <sys/ndir.h>
73 # endif
74 # if HAVE_SYS_DIR_H
75 #  include <sys/dir.h>
76 # endif
77 # if HAVE_NDIR_H
78 #  include <ndir.h>
79 # endif
80 #endif /* ! HAVE_DIRENT_H */
81
82 #ifndef NAME_MAX
83 # ifdef _POSIX_NAME_MAX
84 #  define NAME_MAX _POSIX_NAME_MAX
85 # else
86 #  define NAME_MAX 16
87 # endif
88 #endif
89  
90 #if NAME_MAX < 8
91 #  define NAME_MAX 8
92 #endif
93
94 #ifdef __hpux
95 #ifndef HAVE_DLFCN_H
96 #include <dl.h>
97
98 typedef shl_t dll_handle;
99 typedef void * dll_func;
100
101 dll_handle
102 dlopen(char *fname, int mode)
103 {
104     shl_t h = shl_load(fname, BIND_DEFERRED, 0L);
105     shl_t *hp = NULL;
106     
107     if (h) {
108         hp = (shl_t *)malloc(sizeof (shl_t));
109         if (!hp) {
110             shl_unload(h);
111         } else {
112             *hp = h;
113         }
114     }
115
116     return (dll_handle)hp;
117 }
118
119 int
120 dlclose(dll_handle h)
121 {
122     shl_t hp = *((shl_t *)h);
123     if (hp != NULL) free(hp);
124     return shl_unload(h);
125 }
126
127 dll_func
128 dlsym(dll_handle h, char *n)
129 {
130     dll_func handle;
131     
132     if (shl_findsym ((shl_t *)h, n, TYPE_PROCEDURE, &handle))
133         return NULL;
134     
135     return (dll_func)handle;
136 }
137
138 char *dlerror()
139 {
140     if (errno != 0) {
141         return strerror(errno);
142     }
143     return "Generic shared library error";
144 }
145
146 #endif /* HAVE_DLFCN_H */
147 #define SO_SUFFIX       ".sl"
148 #else /* __hpux */
149 #define SO_SUFFIX       ".so"
150 #endif /* __hpux */
151
152 #define LA_SUFFIX       ".la"
153
154 typedef struct lib_list 
155 {
156     struct lib_list *next;
157     void *library;
158 } lib_list_t;
159
160 static lib_list_t *lib_list_head = NULL;
161
162 #endif /* DO_DLOPEN */
163
164 int _sasl_locate_entry(void *library, const char *entryname,
165                        void **entry_point) 
166 {
167 #ifdef DO_DLOPEN
168 /* note that we still check for known problem systems in
169  * case we are cross-compiling */
170 #if defined(DLSYM_NEEDS_UNDERSCORE) || (defined(__OpenBSD__) && !defined(__ELF__))
171     char adj_entryname[1024];
172 #else
173 #define adj_entryname entryname
174 #endif
175
176     if(!entryname) {
177         _sasl_log(NULL, SASL_LOG_ERR,
178                   "no entryname in _sasl_locate_entry");
179         return SASL_BADPARAM;
180     }
181
182     if(!library) {
183         _sasl_log(NULL, SASL_LOG_ERR,
184                   "no library in _sasl_locate_entry");
185         return SASL_BADPARAM;
186     }
187
188     if(!entry_point) {
189         _sasl_log(NULL, SASL_LOG_ERR,
190                   "no entrypoint output pointer in _sasl_locate_entry");
191         return SASL_BADPARAM;
192     }
193
194 #if defined(DLSYM_NEEDS_UNDERSCORE) || (defined(__OpenBSD__) && !defined(__ELF__))
195     snprintf(adj_entryname, sizeof adj_entryname, "_%s", entryname);
196 #endif
197
198     *entry_point = NULL;
199     *entry_point = dlsym(library, adj_entryname);
200     if (*entry_point == NULL) {
201 #if 0 /* This message appears to confuse people */
202         _sasl_log(NULL, SASL_LOG_DEBUG,
203                   "unable to get entry point %s: %s", adj_entryname,
204                   dlerror());
205 #endif
206         return SASL_FAIL;
207     }
208
209     return SASL_OK;
210 #else
211     return SASL_FAIL;
212 #endif /* DO_DLOPEN */
213 }
214
215 #ifdef DO_DLOPEN
216
217 static int _sasl_plugin_load(char *plugin, void *library,
218                              const char *entryname,
219                              int (*add_plugin)(const char *, void *)) 
220 {
221     void *entry_point;
222     int result;
223     
224     result = _sasl_locate_entry(library, entryname, &entry_point);
225     if(result == SASL_OK) {
226         result = add_plugin(plugin, entry_point);
227         if(result != SASL_OK)
228             _sasl_log(NULL, SASL_LOG_DEBUG,
229                       "_sasl_plugin_load failed on %s for plugin: %s\n",
230                       entryname, plugin);
231     }
232
233     return result;
234 }
235
236 /* this returns the file to actually open.
237  *  out should be a buffer of size PATH_MAX
238  *  and may be the same as in. */
239
240 /* We'll use a static buffer for speed unless someone complains */
241 #define MAX_LINE 2048
242
243 static int _parse_la(const char *prefix, const char *in, char *out) 
244 {
245     FILE *file;
246     size_t length;
247     char line[MAX_LINE];
248     char *ntmp = NULL;
249
250     if(!in || !out || !prefix || out == in) return SASL_BADPARAM;
251
252     /* Set this so we can detect failure */
253     *out = '\0';
254
255     length = strlen(in);
256
257     if (strcmp(in + (length - strlen(LA_SUFFIX)), LA_SUFFIX)) {
258         if(!strcmp(in + (length - strlen(SO_SUFFIX)),SO_SUFFIX)) {
259             /* check for a .la file */
260             strcpy(line, prefix);
261             strcat(line, in);
262             length = strlen(line);
263             *(line + (length - strlen(SO_SUFFIX))) = '\0';
264             strcat(line, LA_SUFFIX);
265             file = fopen(line, "r");
266             if(file) {
267                 /* We'll get it on the .la open */
268                 fclose(file);
269                 return SASL_FAIL;
270             }
271         }
272         strcpy(out, prefix);
273         strcat(out, in);
274         return SASL_OK;
275     }
276
277     strcpy(line, prefix);
278     strcat(line, in);
279
280     file = fopen(line, "r");
281     if(!file) {
282         _sasl_log(NULL, SASL_LOG_WARN,
283                   "unable to open LA file: %s", line);
284         return SASL_FAIL;
285     }
286     
287     while(!feof(file)) {
288         if(!fgets(line, MAX_LINE, file)) break;
289         if(line[strlen(line) - 1] != '\n') {
290             _sasl_log(NULL, SASL_LOG_WARN,
291                       "LA file has too long of a line: %s", in);
292             return SASL_BUFOVER;
293         }
294         if(line[0] == '\n' || line[0] == '#') continue;
295         if(!strncmp(line, "dlname=", sizeof("dlname=") - 1)) {
296             /* We found the line with the name in it */
297             char *end;
298             char *start;
299             size_t len;
300             end = strrchr(line, '\'');
301             if(!end) continue;
302             start = &line[sizeof("dlname=")-1];
303             len = strlen(start);
304             if(len > 3 && start[0] == '\'') {
305                 ntmp=&start[1];
306                 *end='\0';
307                 /* Do we have dlname="" ? */
308                 if(ntmp == end) {
309                     _sasl_log(NULL, SASL_LOG_DEBUG,
310                               "dlname is empty in .la file: %s", in);
311                     return SASL_FAIL;
312                 }
313                 strcpy(out, prefix);
314                 strcat(out, ntmp);
315             }
316             break;
317         }
318     }
319     if(ferror(file) || feof(file)) {
320         _sasl_log(NULL, SASL_LOG_WARN,
321                   "Error reading .la: %s\n", in);
322         fclose(file);
323         return SASL_FAIL;
324     }
325     fclose(file);
326
327     if(!(*out)) {
328         _sasl_log(NULL, SASL_LOG_WARN,
329                   "Could not find a dlname line in .la file: %s", in);
330         return SASL_FAIL;
331     }
332
333     return SASL_OK;
334 }
335 #endif /* DO_DLOPEN */
336
337 /* loads a plugin library */
338 int _sasl_get_plugin(const char *file,
339                      const sasl_callback_t *verifyfile_cb,
340                      void **libraryptr)
341 {
342 #ifdef DO_DLOPEN
343     int r = 0;
344     int flag;
345     void *library;
346     lib_list_t *newhead;
347     
348     r = ((sasl_verifyfile_t *)(verifyfile_cb->proc))
349                     (verifyfile_cb->context, file, SASL_VRFY_PLUGIN);
350     if (r != SASL_OK) return r;
351
352 #ifdef RTLD_NOW
353     flag = RTLD_NOW;
354 #else
355     flag = 0;
356 #endif
357
358     newhead = sasl_ALLOC(sizeof(lib_list_t));
359     if(!newhead) return SASL_NOMEM;
360
361     if (!(library = dlopen(file, flag))) {
362         _sasl_log(NULL, SASL_LOG_ERR,
363                   "unable to dlopen %s: %s", file, dlerror());
364         sasl_FREE(newhead);
365         return SASL_FAIL;
366     }
367
368     newhead->library = library;
369     newhead->next = lib_list_head;
370     lib_list_head = newhead;
371
372     *libraryptr = library;
373     return SASL_OK;
374 #else
375     return SASL_FAIL;
376 #endif /* DO_DLOPEN */
377 }
378
379 /* gets the list of mechanisms */
380 int _sasl_load_plugins(const add_plugin_list_t *entrypoints,
381                        const sasl_callback_t *getpath_cb,
382                        const sasl_callback_t *verifyfile_cb)
383 {
384     int result;
385     const add_plugin_list_t *cur_ep;
386 #ifdef DO_DLOPEN
387     char str[PATH_MAX], tmp[PATH_MAX+2], prefix[PATH_MAX+2];
388                                 /* 1 for '/' 1 for trailing '\0' */
389     char c;
390     int pos;
391     const char *path=NULL;
392     int position;
393     DIR *dp;
394     struct dirent *dir;
395 #endif
396 #ifndef PIC
397     add_plugin_t *add_plugin;
398     _sasl_plug_type type;
399     _sasl_plug_rec *p;
400 #endif
401
402     if (! entrypoints
403         || ! getpath_cb
404         || getpath_cb->id != SASL_CB_GETPATH
405         || ! getpath_cb->proc
406         || ! verifyfile_cb
407         || verifyfile_cb->id != SASL_CB_VERIFYFILE
408         || ! verifyfile_cb->proc)
409         return SASL_BADPARAM;
410
411 #ifndef PIC
412     /* do all the static plugins first */
413
414     for(cur_ep = entrypoints; cur_ep->entryname; cur_ep++) {
415
416         /* What type of plugin are we looking for? */
417         if(!strcmp(cur_ep->entryname, "sasl_server_plug_init")) {
418             type = SERVER;
419             add_plugin = (add_plugin_t *)sasl_server_add_plugin;
420         } else if (!strcmp(cur_ep->entryname, "sasl_client_plug_init")) {
421             type = CLIENT;
422             add_plugin = (add_plugin_t *)sasl_client_add_plugin;
423         } else if (!strcmp(cur_ep->entryname, "sasl_auxprop_plug_init")) {
424             type = AUXPROP;
425             add_plugin = (add_plugin_t *)sasl_auxprop_add_plugin;
426         } else if (!strcmp(cur_ep->entryname, "sasl_canonuser_init")) {
427             type = CANONUSER;
428             add_plugin = (add_plugin_t *)sasl_canonuser_add_plugin;
429         } else {
430             /* What are we looking for then? */
431             return SASL_FAIL;
432         }
433         for (p=_sasl_static_plugins; p->type; p++) {
434             if(type == p->type)
435                 result = add_plugin(p->name, p->plug);
436         }
437     }
438 #endif /* !PIC */
439
440 /* only do the following if:
441  * 
442  * we support dlopen()
443  *  AND we are not staticly compiled
444  *      OR we are staticly compiled and TRY_DLOPEN_WHEN_STATIC is defined
445  */
446 #if defined(DO_DLOPEN) && (defined(PIC) || (!defined(PIC) && defined(TRY_DLOPEN_WHEN_STATIC)))
447     /* get the path to the plugins */
448     result = ((sasl_getpath_t *)(getpath_cb->proc))(getpath_cb->context,
449                                                     &path);
450     if (result != SASL_OK) return result;
451     if (! path) return SASL_FAIL;
452
453     if (strlen(path) >= PATH_MAX) { /* no you can't buffer overrun */
454         return SASL_FAIL;
455     }
456
457     position=0;
458     do {
459         pos=0;
460         do {
461             c=path[position];
462             position++;
463             str[pos]=c;
464             pos++;
465         } while ((c!=':') && (c!='=') && (c!=0));
466         str[pos-1]='\0';
467
468         strcpy(prefix,str);
469         strcat(prefix,"/");
470
471         if ((dp=opendir(str)) !=NULL) /* ignore errors */    
472         {
473             while ((dir=readdir(dp)) != NULL)
474             {
475                 size_t length;
476                 void *library;
477                 char *c;
478                 char plugname[PATH_MAX];
479                 char name[PATH_MAX];
480
481                 length = NAMLEN(dir);
482                 if (length < 4) 
483                     continue; /* can not possibly be what we're looking for */
484
485                 if (length + pos>=PATH_MAX) continue; /* too big */
486
487                 if (strcmp(dir->d_name + (length - strlen(SO_SUFFIX)),
488                            SO_SUFFIX)
489                     && strcmp(dir->d_name + (length - strlen(LA_SUFFIX)),
490                            LA_SUFFIX))
491                     continue;
492
493                 memcpy(name,dir->d_name,length);
494                 name[length]='\0';
495
496                 result = _parse_la(prefix, name, tmp);
497                 if(result != SASL_OK)
498                     continue;
499                 
500                 /* skip "lib" and cut off suffix --
501                    this only need be approximate */
502                 strcpy(plugname, name + 3);
503                 c = strchr(plugname, (int)'.');
504                 if(c) *c = '\0';
505
506                 result = _sasl_get_plugin(tmp, verifyfile_cb, &library);
507
508                 if(result != SASL_OK)
509                     continue;
510
511                 for(cur_ep = entrypoints; cur_ep->entryname; cur_ep++) {
512                         _sasl_plugin_load(plugname, library, cur_ep->entryname,
513                                           cur_ep->add_plugin);
514                         /* If this fails, it's not the end of the world */
515                 }
516             }
517
518             closedir(dp);
519         } else {
520             _sasl_log(NULL, SASL_LOG_DEBUG,
521                       "looking for plugins in '%s', failed to open directory, error: %s",
522                       str,
523                       strerror(errno));
524         }
525
526     } while ((c!='=') && (c!=0));
527 #endif /* defined(DO_DLOPEN) && (!defined(PIC) || (defined(PIC) && defined(TRY_DLOPEN_WHEN_STATIC))) */
528
529     return SASL_OK;
530 }
531
532 int
533 _sasl_done_with_plugins(void)
534 {
535 #ifdef DO_DLOPEN
536     lib_list_t *libptr, *libptr_next;
537     
538     for(libptr = lib_list_head; libptr; libptr = libptr_next) {
539         libptr_next = libptr->next;
540         if(libptr->library)
541             dlclose(libptr->library);
542         sasl_FREE(libptr);
543     }
544
545     lib_list_head = NULL;
546 #endif /* DO_DLOPEN */
547     return SASL_OK;
548 }