Preserve errno on dlopen
[openssl.git] / crypto / dso / dso_dlfcn.c
1 /*
2  * Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9
10 /*
11  * We need to do this early, because stdio.h includes the header files that
12  * handle _GNU_SOURCE and other similar macros.  Defining it later is simply
13  * too late, because those headers are protected from re- inclusion.
14  */
15 #ifndef _GNU_SOURCE
16 # define _GNU_SOURCE            /* make sure dladdr is declared */
17 #endif
18
19 #include "dso_locl.h"
20 #include "e_os.h"
21
22 #ifdef DSO_DLFCN
23
24 # ifdef HAVE_DLFCN_H
25 #  ifdef __osf__
26 #   define __EXTENSIONS__
27 #  endif
28 #  include <dlfcn.h>
29 #  define HAVE_DLINFO 1
30 #  if defined(__CYGWIN__) || \
31      defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
32      (defined(__osf__) && !defined(RTLD_NEXT))     || \
33      (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
34         defined(__ANDROID__)
35 #   undef HAVE_DLINFO
36 #  endif
37 # endif
38
39 /* Part of the hack in "dlfcn_load" ... */
40 # define DSO_MAX_TRANSLATED_SIZE 256
41
42 static int dlfcn_load(DSO *dso);
43 static int dlfcn_unload(DSO *dso);
44 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
45 static char *dlfcn_name_converter(DSO *dso, const char *filename);
46 static char *dlfcn_merger(DSO *dso, const char *filespec1,
47                           const char *filespec2);
48 static int dlfcn_pathbyaddr(void *addr, char *path, int sz);
49 static void *dlfcn_globallookup(const char *name);
50
51 static DSO_METHOD dso_meth_dlfcn = {
52     "OpenSSL 'dlfcn' shared library method",
53     dlfcn_load,
54     dlfcn_unload,
55     dlfcn_bind_func,
56     NULL,                       /* ctrl */
57     dlfcn_name_converter,
58     dlfcn_merger,
59     NULL,                       /* init */
60     NULL,                       /* finish */
61     dlfcn_pathbyaddr,
62     dlfcn_globallookup
63 };
64
65 DSO_METHOD *DSO_METHOD_openssl(void)
66 {
67     return &dso_meth_dlfcn;
68 }
69
70 /*
71  * Prior to using the dlopen() function, we should decide on the flag we
72  * send. There's a few different ways of doing this and it's a messy
73  * venn-diagram to match up which platforms support what. So as we don't have
74  * autoconf yet, I'm implementing a hack that could be hacked further
75  * relatively easily to deal with cases as we find them. Initially this is to
76  * cope with OpenBSD.
77  */
78 # if defined(__OpenBSD__) || defined(__NetBSD__)
79 #  ifdef DL_LAZY
80 #   define DLOPEN_FLAG DL_LAZY
81 #  else
82 #   ifdef RTLD_NOW
83 #    define DLOPEN_FLAG RTLD_NOW
84 #   else
85 #    define DLOPEN_FLAG 0
86 #   endif
87 #  endif
88 # else
89 #  define DLOPEN_FLAG RTLD_NOW  /* Hope this works everywhere else */
90 # endif
91
92 /*
93  * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
94  * (void*) returned from dlopen().
95  */
96
97 static int dlfcn_load(DSO *dso)
98 {
99     void *ptr = NULL;
100     /* See applicable comments in dso_dl.c */
101     char *filename = DSO_convert_filename(dso, NULL);
102     int flags = DLOPEN_FLAG;
103     int saveerrno = get_last_sys_error();
104
105     if (filename == NULL) {
106         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
107         goto err;
108     }
109 # ifdef RTLD_GLOBAL
110     if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
111         flags |= RTLD_GLOBAL;
112 # endif
113 # ifdef _AIX
114     if (filename[strlen(filename) - 1] == ')')
115         flags |= RTLD_MEMBER;
116 # endif
117     ptr = dlopen(filename, flags);
118     if (ptr == NULL) {
119         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
120         ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
121         goto err;
122     }
123     /*
124      * Some dlopen() implementations (e.g. solaris) do no preserve errno, even
125      * on a successful call.
126      */
127     set_sys_error(saveerrno);
128     if (!sk_void_push(dso->meth_data, (char *)ptr)) {
129         DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
130         goto err;
131     }
132     /* Success */
133     dso->loaded_filename = filename;
134     return 1;
135  err:
136     /* Cleanup! */
137     OPENSSL_free(filename);
138     if (ptr != NULL)
139         dlclose(ptr);
140     return 0;
141 }
142
143 static int dlfcn_unload(DSO *dso)
144 {
145     void *ptr;
146     if (dso == NULL) {
147         DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
148         return 0;
149     }
150     if (sk_void_num(dso->meth_data) < 1)
151         return 1;
152     ptr = sk_void_pop(dso->meth_data);
153     if (ptr == NULL) {
154         DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
155         /*
156          * Should push the value back onto the stack in case of a retry.
157          */
158         sk_void_push(dso->meth_data, ptr);
159         return 0;
160     }
161     /* For now I'm not aware of any errors associated with dlclose() */
162     dlclose(ptr);
163     return 1;
164 }
165
166 static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
167 {
168     void *ptr;
169     union {
170         DSO_FUNC_TYPE sym;
171         void *dlret;
172     } u;
173
174     if ((dso == NULL) || (symname == NULL)) {
175         DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
176         return NULL;
177     }
178     if (sk_void_num(dso->meth_data) < 1) {
179         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
180         return NULL;
181     }
182     ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
183     if (ptr == NULL) {
184         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
185         return NULL;
186     }
187     u.dlret = dlsym(ptr, symname);
188     if (u.dlret == NULL) {
189         DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
190         ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
191         return NULL;
192     }
193     return u.sym;
194 }
195
196 static char *dlfcn_merger(DSO *dso, const char *filespec1,
197                           const char *filespec2)
198 {
199     char *merged;
200
201     if (!filespec1 && !filespec2) {
202         DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
203         return NULL;
204     }
205     /*
206      * If the first file specification is a rooted path, it rules. same goes
207      * if the second file specification is missing.
208      */
209     if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
210         merged = OPENSSL_strdup(filespec1);
211         if (merged == NULL) {
212             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
213             return NULL;
214         }
215     }
216     /*
217      * If the first file specification is missing, the second one rules.
218      */
219     else if (!filespec1) {
220         merged = OPENSSL_strdup(filespec2);
221         if (merged == NULL) {
222             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
223             return NULL;
224         }
225     } else {
226         /*
227          * This part isn't as trivial as it looks.  It assumes that the
228          * second file specification really is a directory, and makes no
229          * checks whatsoever.  Therefore, the result becomes the
230          * concatenation of filespec2 followed by a slash followed by
231          * filespec1.
232          */
233         int spec2len, len;
234
235         spec2len = strlen(filespec2);
236         len = spec2len + strlen(filespec1);
237
238         if (spec2len && filespec2[spec2len - 1] == '/') {
239             spec2len--;
240             len--;
241         }
242         merged = OPENSSL_malloc(len + 2);
243         if (merged == NULL) {
244             DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
245             return NULL;
246         }
247         strcpy(merged, filespec2);
248         merged[spec2len] = '/';
249         strcpy(&merged[spec2len + 1], filespec1);
250     }
251     return merged;
252 }
253
254 static char *dlfcn_name_converter(DSO *dso, const char *filename)
255 {
256     char *translated;
257     int len, rsize, transform;
258
259     len = strlen(filename);
260     rsize = len + 1;
261     transform = (strstr(filename, "/") == NULL);
262     if (transform) {
263         /* We will convert this to "%s.so" or "lib%s.so" etc */
264         rsize += strlen(DSO_EXTENSION);    /* The length of ".so" */
265         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
266             rsize += 3;         /* The length of "lib" */
267     }
268     translated = OPENSSL_malloc(rsize);
269     if (translated == NULL) {
270         DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
271         return NULL;
272     }
273     if (transform) {
274         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
275             sprintf(translated, "lib%s" DSO_EXTENSION, filename);
276         else
277             sprintf(translated, "%s" DSO_EXTENSION, filename);
278     } else
279         sprintf(translated, "%s", filename);
280     return translated;
281 }
282
283 # ifdef __sgi
284 /*-
285 This is a quote from IRIX manual for dladdr(3c):
286
287      <dlfcn.h> does not contain a prototype for dladdr or definition of
288      Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
289      but contains no dladdr prototype and no IRIX library contains an
290      implementation.  Write your own declaration based on the code below.
291
292      The following code is dependent on internal interfaces that are not
293      part of the IRIX compatibility guarantee; however, there is no future
294      intention to change this interface, so on a practical level, the code
295      below is safe to use on IRIX.
296 */
297 #  include <rld_interface.h>
298 #  ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
299 #   define _RLD_INTERFACE_DLFCN_H_DLADDR
300 typedef struct Dl_info {
301     const char *dli_fname;
302     void *dli_fbase;
303     const char *dli_sname;
304     void *dli_saddr;
305     int dli_version;
306     int dli_reserved1;
307     long dli_reserved[4];
308 } Dl_info;
309 #  else
310 typedef struct Dl_info Dl_info;
311 #  endif
312 #  define _RLD_DLADDR             14
313
314 static int dladdr(void *address, Dl_info *dl)
315 {
316     void *v;
317     v = _rld_new_interface(_RLD_DLADDR, address, dl);
318     return (int)v;
319 }
320 # endif                         /* __sgi */
321
322 # ifdef _AIX
323 /*-
324  * See IBM's AIX Version 7.2, Technical Reference:
325  *  Base Operating System and Extensions, Volume 1 and 2
326  *  https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm
327  */
328 #  include <sys/ldr.h>
329 #  include <errno.h>
330 /* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */
331 #  define DLFCN_LDINFO_SIZE 86976
332 typedef struct Dl_info {
333     const char *dli_fname;
334 } Dl_info;
335 /*
336  * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual
337  * address of a function, which is just located in the DATA segment instead of
338  * the TEXT segment.
339  */
340 static int dladdr(void *ptr, Dl_info *dl)
341 {
342     uintptr_t addr = (uintptr_t)ptr;
343     unsigned int found = 0;
344     struct ld_info *ldinfos, *next_ldi, *this_ldi;
345
346     if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) {
347         errno = ENOMEM;
348         dl->dli_fname = NULL;
349         return 0;
350     }
351
352     if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) {
353         /*-
354          * Error handling is done through errno and dlerror() reading errno:
355          *  ENOMEM (ldinfos buffer is too small),
356          *  EINVAL (invalid flags),
357          *  EFAULT (invalid ldinfos ptr)
358          */
359         OPENSSL_free((void *)ldinfos);
360         dl->dli_fname = NULL;
361         return 0;
362     }
363     next_ldi = ldinfos;
364
365     do {
366         this_ldi = next_ldi;
367         if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg)
368              && (addr < ((uintptr_t)this_ldi->ldinfo_textorg +
369                          this_ldi->ldinfo_textsize)))
370             || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg)
371                 && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg +
372                             this_ldi->ldinfo_datasize)))) {
373             char *buffer, *member;
374             size_t buffer_sz, member_len;
375
376             buffer_sz = strlen(this_ldi->ldinfo_filename) + 1;
377             member = this_ldi->ldinfo_filename + buffer_sz;
378             if ((member_len = strlen(member)) > 0)
379                 buffer_sz += 1 + member_len + 1;
380             found = 1;
381             if ((buffer = OPENSSL_malloc(buffer_sz)) != NULL) {
382                 OPENSSL_strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz);
383                 if (member_len > 0) {
384                     /*
385                      * Need to respect a possible member name and not just
386                      * returning the path name in this case. See docs:
387                      * sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER.
388                      */
389                     OPENSSL_strlcat(buffer, "(", buffer_sz);
390                     OPENSSL_strlcat(buffer, member, buffer_sz);
391                     OPENSSL_strlcat(buffer, ")", buffer_sz);
392                 }
393                 dl->dli_fname = buffer;
394             } else {
395                 errno = ENOMEM;
396             }
397         } else {
398             next_ldi = (struct ld_info *)((uintptr_t)this_ldi +
399                                           this_ldi->ldinfo_next);
400         }
401     } while (this_ldi->ldinfo_next && !found);
402     OPENSSL_free((void *)ldinfos);
403     return (found && dl->dli_fname != NULL);
404 }
405 # endif                         /* _AIX */
406
407 static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
408 {
409 # ifdef HAVE_DLINFO
410     Dl_info dli;
411     int len;
412
413     if (addr == NULL) {
414         union {
415             int (*f) (void *, char *, int);
416             void *p;
417         } t = {
418             dlfcn_pathbyaddr
419         };
420         addr = t.p;
421     }
422
423     if (dladdr(addr, &dli)) {
424         len = (int)strlen(dli.dli_fname);
425         if (sz <= 0) {
426 #  ifdef _AIX
427             OPENSSL_free((void *)dli.dli_fname);
428 #  endif
429             return len + 1;
430         }
431         if (len >= sz)
432             len = sz - 1;
433         memcpy(path, dli.dli_fname, len);
434         path[len++] = 0;
435 #  ifdef _AIX
436         OPENSSL_free((void *)dli.dli_fname);
437 #  endif
438         return len;
439     }
440
441     ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
442 # endif
443     return -1;
444 }
445
446 static void *dlfcn_globallookup(const char *name)
447 {
448     void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
449
450     if (handle) {
451         ret = dlsym(handle, name);
452         dlclose(handle);
453     }
454
455     return ret;
456 }
457 #endif                          /* DSO_DLFCN */