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