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