1 /* dso_dlfcn.c -*- mode:C; c-file-style: "eay" -*- */
2 /* Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL
3 * project 2000.
4 */
5 /* ====================================================================
6 * Copyright (c) 2000 The OpenSSL Project. 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. All advertising materials mentioning features or use of this
21 * software must display the following acknowledgment:
22 * "This product includes software developed by the OpenSSL Project
23 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24 *
25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26 * endorse or promote products derived from this software without
27 * prior written permission. For written permission, please contact
28 * licensing@OpenSSL.org.
29 *
30 * 5. Products derived from this software may not be called "OpenSSL"
31 * nor may "OpenSSL" appear in their names without prior written
32 * permission of the OpenSSL Project.
33 *
34 * 6. Redistributions of any form whatsoever must retain the following
35 * acknowledgment:
36 * "This product includes software developed by the OpenSSL Project
37 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50 * OF THE POSSIBILITY OF SUCH DAMAGE.
51 * ====================================================================
52 *
53 * This product includes cryptographic software written by Eric Young
54 * (eay@cryptsoft.com). This product includes software written by Tim
55 * Hudson (tjh@cryptsoft.com).
56 *
57 */
58
59 /* We need to do this early, because stdio.h includes the header files
60 that handle _GNU_SOURCE and other similar macros. Defining it later
61 is simply too late, because those headers are protected from re-
62 inclusion. */
63 #ifdef __linux
64 # ifndef _GNU_SOURCE
65 # define _GNU_SOURCE /* make sure dladdr is declared */
66 # endif
67 #endif
68
69 #include <stdio.h>
70 #include "cryptlib.h"
71 #include <openssl/dso.h>
72
73 #ifndef DSO_DLFCN
DSO_METHOD_dlfcn(void)74 DSO_METHOD *DSO_METHOD_dlfcn(void)
75 {
76 return NULL;
77 }
78 #else
79
80 #ifdef HAVE_DLFCN_H
81 # ifdef __osf__
82 # define __EXTENSIONS__
83 # endif
84 # include <dlfcn.h>
85 # define HAVE_DLINFO 1
86 # if defined(_AIX) || defined(__CYGWIN__) || \
87 defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
88 (defined(__OpenBSD__) && !defined(RTLD_SELF))
89 # undef HAVE_DLINFO
90 # endif
91 #endif
92
93 /* Part of the hack in "dlfcn_load" ... */
94 #define DSO_MAX_TRANSLATED_SIZE 256
95
96 static int dlfcn_load(DSO *dso);
97 static int dlfcn_unload(DSO *dso);
98 static void *dlfcn_bind_var(DSO *dso, const char *symname);
99 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
100 #if 0
101 static int dlfcn_unbind(DSO *dso, char *symname, void *symptr);
102 static int dlfcn_init(DSO *dso);
103 static int dlfcn_finish(DSO *dso);
104 static long dlfcn_ctrl(DSO *dso, int cmd, long larg, void *parg);
105 #endif
106 static char *dlfcn_name_converter(DSO *dso, const char *filename);
107 static char *dlfcn_merger(DSO *dso, const char *filespec1,
108 const char *filespec2);
109 static int dlfcn_pathbyaddr(void *addr,char *path,int sz);
110 static void *dlfcn_globallookup(const char *name);
111
112 static DSO_METHOD dso_meth_dlfcn = {
113 "OpenSSL 'dlfcn' shared library method",
114 dlfcn_load,
115 dlfcn_unload,
116 dlfcn_bind_var,
117 dlfcn_bind_func,
118 /* For now, "unbind" doesn't exist */
119 #if 0
120 NULL, /* unbind_var */
121 NULL, /* unbind_func */
122 #endif
123 NULL, /* ctrl */
124 dlfcn_name_converter,
125 dlfcn_merger,
126 NULL, /* init */
127 NULL, /* finish */
128 dlfcn_pathbyaddr,
129 dlfcn_globallookup
130 };
131
DSO_METHOD_dlfcn(void)132 DSO_METHOD *DSO_METHOD_dlfcn(void)
133 {
134 return(&dso_meth_dlfcn);
135 }
136
137 /* Prior to using the dlopen() function, we should decide on the flag
138 * we send. There's a few different ways of doing this and it's a
139 * messy venn-diagram to match up which platforms support what. So
140 * as we don't have autoconf yet, I'm implementing a hack that could
141 * be hacked further relatively easily to deal with cases as we find
142 * them. Initially this is to cope with OpenBSD. */
143 #if defined(__OpenBSD__) || defined(__NetBSD__)
144 # ifdef DL_LAZY
145 # define DLOPEN_FLAG DL_LAZY
146 # else
147 # ifdef RTLD_NOW
148 # define DLOPEN_FLAG RTLD_NOW
149 # else
150 # define DLOPEN_FLAG 0
151 # endif
152 # endif
153 #else
154 # ifdef OPENSSL_SYS_SUNOS
155 # define DLOPEN_FLAG 1
156 # else
157 # define DLOPEN_FLAG RTLD_NOW /* Hope this works everywhere else */
158 # endif
159 #endif
160
161 /* For this DSO_METHOD, our meth_data STACK will contain;
162 * (i) the handle (void*) returned from dlopen().
163 */
164
dlfcn_load(DSO * dso)165 static int dlfcn_load(DSO *dso)
166 {
167 void *ptr = NULL;
168 /* See applicable comments in dso_dl.c */
169 char *filename = DSO_convert_filename(dso, NULL);
170 int flags = DLOPEN_FLAG;
171
172 if(filename == NULL)
173 {
174 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_NO_FILENAME);
175 goto err;
176 }
177
178 #ifdef RTLD_GLOBAL
179 if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
180 flags |= RTLD_GLOBAL;
181 #endif
182 ptr = dlopen(filename, flags);
183 if(ptr == NULL)
184 {
185 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_LOAD_FAILED);
186 ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
187 goto err;
188 }
189 if(!sk_void_push(dso->meth_data, (char *)ptr))
190 {
191 DSOerr(DSO_F_DLFCN_LOAD,DSO_R_STACK_ERROR);
192 goto err;
193 }
194 /* Success */
195 dso->loaded_filename = filename;
196 return(1);
197 err:
198 /* Cleanup! */
199 if(filename != NULL)
200 OPENSSL_free(filename);
201 if(ptr != NULL)
202 dlclose(ptr);
203 return(0);
204 }
205
dlfcn_unload(DSO * dso)206 static int dlfcn_unload(DSO *dso)
207 {
208 void *ptr;
209 if(dso == NULL)
210 {
211 DSOerr(DSO_F_DLFCN_UNLOAD,ERR_R_PASSED_NULL_PARAMETER);
212 return(0);
213 }
214 if(sk_void_num(dso->meth_data) < 1)
215 return(1);
216 ptr = sk_void_pop(dso->meth_data);
217 if(ptr == NULL)
218 {
219 DSOerr(DSO_F_DLFCN_UNLOAD,DSO_R_NULL_HANDLE);
220 /* Should push the value back onto the stack in
221 * case of a retry. */
222 sk_void_push(dso->meth_data, ptr);
223 return(0);
224 }
225 /* For now I'm not aware of any errors associated with dlclose() */
226 dlclose(ptr);
227 return(1);
228 }
229
dlfcn_bind_var(DSO * dso,const char * symname)230 static void *dlfcn_bind_var(DSO *dso, const char *symname)
231 {
232 void *ptr, *sym;
233
234 if((dso == NULL) || (symname == NULL))
235 {
236 DSOerr(DSO_F_DLFCN_BIND_VAR,ERR_R_PASSED_NULL_PARAMETER);
237 return(NULL);
238 }
239 if(sk_void_num(dso->meth_data) < 1)
240 {
241 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_STACK_ERROR);
242 return(NULL);
243 }
244 ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
245 if(ptr == NULL)
246 {
247 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_NULL_HANDLE);
248 return(NULL);
249 }
250 sym = dlsym(ptr, symname);
251 if(sym == NULL)
252 {
253 DSOerr(DSO_F_DLFCN_BIND_VAR,DSO_R_SYM_FAILURE);
254 ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
255 return(NULL);
256 }
257 return(sym);
258 }
259
dlfcn_bind_func(DSO * dso,const char * symname)260 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
261 {
262 void *ptr;
263 union {
264 DSO_FUNC_TYPE sym;
265 void *dlret;
266 } u;
267
268 if((dso == NULL) || (symname == NULL))
269 {
270 DSOerr(DSO_F_DLFCN_BIND_FUNC,ERR_R_PASSED_NULL_PARAMETER);
271 return(NULL);
272 }
273 if(sk_void_num(dso->meth_data) < 1)
274 {
275 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_STACK_ERROR);
276 return(NULL);
277 }
278 ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
279 if(ptr == NULL)
280 {
281 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_NULL_HANDLE);
282 return(NULL);
283 }
284 u.dlret = dlsym(ptr, symname);
285 if(u.dlret == NULL)
286 {
287 DSOerr(DSO_F_DLFCN_BIND_FUNC,DSO_R_SYM_FAILURE);
288 ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
289 return(NULL);
290 }
291 return u.sym;
292 }
293
dlfcn_merger(DSO * dso,const char * filespec1,const char * filespec2)294 static char *dlfcn_merger(DSO *dso, const char *filespec1,
295 const char *filespec2)
296 {
297 char *merged;
298
299 if(!filespec1 && !filespec2)
300 {
301 DSOerr(DSO_F_DLFCN_MERGER,
302 ERR_R_PASSED_NULL_PARAMETER);
303 return(NULL);
304 }
305 /* If the first file specification is a rooted path, it rules.
306 same goes if the second file specification is missing. */
307 if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/'))
308 {
309 merged = OPENSSL_malloc(strlen(filespec1) + 1);
310 if(!merged)
311 {
312 DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
313 return(NULL);
314 }
315 strcpy(merged, filespec1);
316 }
317 /* If the first file specification is missing, the second one rules. */
318 else if (!filespec1)
319 {
320 merged = OPENSSL_malloc(strlen(filespec2) + 1);
321 if(!merged)
322 {
323 DSOerr(DSO_F_DLFCN_MERGER,
324 ERR_R_MALLOC_FAILURE);
325 return(NULL);
326 }
327 strcpy(merged, filespec2);
328 }
329 else
330 /* This part isn't as trivial as it looks. It assumes that
331 the second file specification really is a directory, and
332 makes no checks whatsoever. Therefore, the result becomes
333 the concatenation of filespec2 followed by a slash followed
334 by filespec1. */
335 {
336 int spec2len, len;
337
338 spec2len = strlen(filespec2);
339 len = spec2len + (filespec1 ? strlen(filespec1) : 0);
340
341 if(filespec2 && filespec2[spec2len - 1] == '/')
342 {
343 spec2len--;
344 len--;
345 }
346 merged = OPENSSL_malloc(len + 2);
347 if(!merged)
348 {
349 DSOerr(DSO_F_DLFCN_MERGER,
350 ERR_R_MALLOC_FAILURE);
351 return(NULL);
352 }
353 strcpy(merged, filespec2);
354 merged[spec2len] = '/';
355 strcpy(&merged[spec2len + 1], filespec1);
356 }
357 return(merged);
358 }
359
360 #ifdef OPENSSL_SYS_MACOSX
361 #define DSO_ext ".dylib"
362 #define DSO_extlen 6
363 #else
364 #define DSO_ext ".so"
365 #define DSO_extlen 3
366 #endif
367
368
dlfcn_name_converter(DSO * dso,const char * filename)369 static char *dlfcn_name_converter(DSO *dso, const char *filename)
370 {
371 char *translated;
372 int len, rsize, transform;
373
374 len = strlen(filename);
375 rsize = len + 1;
376 transform = (strstr(filename, "/") == NULL);
377 if(transform)
378 {
379 /* We will convert this to "%s.so" or "lib%s.so" etc */
380 rsize += DSO_extlen; /* The length of ".so" */
381 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
382 rsize += 3; /* The length of "lib" */
383 }
384 translated = OPENSSL_malloc(rsize);
385 if(translated == NULL)
386 {
387 DSOerr(DSO_F_DLFCN_NAME_CONVERTER,
388 DSO_R_NAME_TRANSLATION_FAILED);
389 return(NULL);
390 }
391 if(transform)
392 {
393 if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
394 sprintf(translated, "lib%s" DSO_ext, filename);
395 else
396 sprintf(translated, "%s" DSO_ext, filename);
397 }
398 else
399 sprintf(translated, "%s", filename);
400 return(translated);
401 }
402
403 #ifdef __sgi
404 /*
405 This is a quote from IRIX manual for dladdr(3c):
406
407 <dlfcn.h> does not contain a prototype for dladdr or definition of
408 Dl_info. The #include <dlfcn.h> in the SYNOPSIS line is traditional,
409 but contains no dladdr prototype and no IRIX library contains an
410 implementation. Write your own declaration based on the code below.
411
412 The following code is dependent on internal interfaces that are not
413 part of the IRIX compatibility guarantee; however, there is no future
414 intention to change this interface, so on a practical level, the code
415 below is safe to use on IRIX.
416 */
417 #include <rld_interface.h>
418 #ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
419 #define _RLD_INTERFACE_DLFCN_H_DLADDR
420 typedef struct Dl_info {
421 const char * dli_fname;
422 void * dli_fbase;
423 const char * dli_sname;
424 void * dli_saddr;
425 int dli_version;
426 int dli_reserved1;
427 long dli_reserved[4];
428 } Dl_info;
429 #else
430 typedef struct Dl_info Dl_info;
431 #endif
432 #define _RLD_DLADDR 14
433
dladdr(void * address,Dl_info * dl)434 static int dladdr(void *address, Dl_info *dl)
435 {
436 void *v;
437 v = _rld_new_interface(_RLD_DLADDR,address,dl);
438 return (int)v;
439 }
440 #endif /* __sgi */
441
dlfcn_pathbyaddr(void * addr,char * path,int sz)442 static int dlfcn_pathbyaddr(void *addr,char *path,int sz)
443 {
444 #ifdef HAVE_DLINFO
445 Dl_info dli;
446 int len;
447
448 if (addr == NULL)
449 {
450 union { int(*f)(void*,char*,int); void *p; } t =
451 { dlfcn_pathbyaddr };
452 addr = t.p;
453 }
454
455 if (dladdr(addr,&dli))
456 {
457 len = (int)strlen(dli.dli_fname);
458 if (sz <= 0) return len+1;
459 if (len >= sz) len=sz-1;
460 memcpy(path,dli.dli_fname,len);
461 path[len++]=0;
462 return len;
463 }
464
465 ERR_add_error_data(4, "dlfcn_pathbyaddr(): ", dlerror());
466 #endif
467 return -1;
468 }
469
dlfcn_globallookup(const char * name)470 static void *dlfcn_globallookup(const char *name)
471 {
472 void *ret = NULL,*handle = dlopen(NULL,RTLD_LAZY);
473
474 if (handle)
475 {
476 ret = dlsym(handle,name);
477 dlclose(handle);
478 }
479
480 return ret;
481 }
482 #endif /* DSO_DLFCN */
483