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