Home | History | Annotate | Line # | Download | only in dso
      1 /*
      2  * Copyright 2000-2024 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 #include "dso_local.h"
     11 
     12 #ifdef DSO_DL
     13 
     14 #include <dl.h>
     15 
     16 /* Part of the hack in "dl_load" ... */
     17 #define DSO_MAX_TRANSLATED_SIZE 256
     18 
     19 static int dl_load(DSO *dso);
     20 static int dl_unload(DSO *dso);
     21 static DSO_FUNC_TYPE dl_bind_func(DSO *dso, const char *symname);
     22 static char *dl_name_converter(DSO *dso, const char *filename);
     23 static char *dl_merger(DSO *dso, const char *filespec1,
     24     const char *filespec2);
     25 static int dl_pathbyaddr(void *addr, char *path, int sz);
     26 static void *dl_globallookup(const char *name);
     27 
     28 static DSO_METHOD dso_meth_dl = {
     29     "OpenSSL 'dl' shared library method",
     30     dl_load,
     31     dl_unload,
     32     dl_bind_func,
     33     NULL, /* ctrl */
     34     dl_name_converter,
     35     dl_merger,
     36     NULL, /* init */
     37     NULL, /* finish */
     38     dl_pathbyaddr,
     39     dl_globallookup
     40 };
     41 
     42 DSO_METHOD *DSO_METHOD_openssl(void)
     43 {
     44     return &dso_meth_dl;
     45 }
     46 
     47 /*
     48  * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
     49  * (shl_t) returned from shl_load(). NB: I checked on HPUX11 and shl_t is
     50  * itself a pointer type so the cast is safe.
     51  */
     52 
     53 static int dl_load(DSO *dso)
     54 {
     55     shl_t ptr = NULL;
     56     /*
     57      * We don't do any fancy retries or anything, just take the method's (or
     58      * DSO's if it has the callback set) best translation of the
     59      * platform-independent filename and try once with that.
     60      */
     61     char *filename = DSO_convert_filename(dso, NULL);
     62 
     63     if (filename == NULL) {
     64         ERR_raise(ERR_LIB_DSO, DSO_R_NO_FILENAME);
     65         goto err;
     66     }
     67     ptr = shl_load(filename, BIND_IMMEDIATE | (dso->flags & DSO_FLAG_NO_NAME_TRANSLATION ? 0 : DYNAMIC_PATH), 0L);
     68     if (ptr == NULL) {
     69         char errbuf[160];
     70 
     71         if (openssl_strerror_r(errno, errbuf, sizeof(errbuf)))
     72             ERR_raise_data(ERR_LIB_DSO, DSO_R_LOAD_FAILED,
     73                 "filename(%s): %s", filename, errbuf);
     74         else
     75             ERR_raise_data(ERR_LIB_DSO, DSO_R_LOAD_FAILED,
     76                 "filename(%s): errno %d", filename, errno);
     77         goto err;
     78     }
     79     if (!sk_push(dso->meth_data, (char *)ptr)) {
     80         ERR_raise(ERR_LIB_DSO, DSO_R_STACK_ERROR);
     81         goto err;
     82     }
     83     /*
     84      * Success, stick the converted filename we've loaded under into the DSO
     85      * (it also serves as the indicator that we are currently loaded).
     86      */
     87     dso->loaded_filename = filename;
     88     return 1;
     89 err:
     90     /* Cleanup! */
     91     OPENSSL_free(filename);
     92     if (ptr != NULL)
     93         shl_unload(ptr);
     94     return 0;
     95 }
     96 
     97 static int dl_unload(DSO *dso)
     98 {
     99     shl_t ptr;
    100     if (dso == NULL) {
    101         ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER);
    102         return 0;
    103     }
    104     if (sk_num(dso->meth_data) < 1)
    105         return 1;
    106     /* Is this statement legal? */
    107     ptr = (shl_t)sk_pop(dso->meth_data);
    108     if (ptr == NULL) {
    109         ERR_raise(ERR_LIB_DSO, DSO_R_NULL_HANDLE);
    110         /*
    111          * Should push the value back onto the stack in case of a retry.
    112          */
    113         sk_push(dso->meth_data, (char *)ptr);
    114         return 0;
    115     }
    116     shl_unload(ptr);
    117     return 1;
    118 }
    119 
    120 static DSO_FUNC_TYPE dl_bind_func(DSO *dso, const char *symname)
    121 {
    122     shl_t ptr;
    123     void *sym;
    124 
    125     if ((dso == NULL) || (symname == NULL)) {
    126         ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER);
    127         return NULL;
    128     }
    129     if (sk_num(dso->meth_data) < 1) {
    130         ERR_raise(ERR_LIB_DSO, DSO_R_STACK_ERROR);
    131         return NULL;
    132     }
    133     ptr = (shl_t)sk_value(dso->meth_data, sk_num(dso->meth_data) - 1);
    134     if (ptr == NULL) {
    135         ERR_raise(ERR_LIB_DSO, DSO_R_NULL_HANDLE);
    136         return NULL;
    137     }
    138     if (shl_findsym(&ptr, symname, TYPE_UNDEFINED, &sym) < 0) {
    139         char errbuf[160];
    140 
    141         if (openssl_strerror_r(errno, errbuf, sizeof(errbuf)))
    142             ERR_raise_data(ERR_LIB_DSO, DSO_R_SYM_FAILURE,
    143                 "symname(%s): %s", symname, errbuf);
    144         else
    145             ERR_raise_data(ERR_LIB_DSO, DSO_R_SYM_FAILURE,
    146                 "symname(%s): errno %d", symname, errno);
    147         return NULL;
    148     }
    149     return (DSO_FUNC_TYPE)sym;
    150 }
    151 
    152 static char *dl_merger(DSO *dso, const char *filespec1, const char *filespec2)
    153 {
    154     char *merged;
    155 
    156     if (!filespec1 && !filespec2) {
    157         ERR_raise(ERR_LIB_DSO, ERR_R_PASSED_NULL_PARAMETER);
    158         return NULL;
    159     }
    160     /*
    161      * If the first file specification is a rooted path, it rules. same goes
    162      * if the second file specification is missing.
    163      */
    164     if (!filespec2 || filespec1[0] == '/') {
    165         merged = OPENSSL_strdup(filespec1);
    166         if (merged == NULL)
    167             return NULL;
    168     }
    169     /*
    170      * If the first file specification is missing, the second one rules.
    171      */
    172     else if (!filespec1) {
    173         merged = OPENSSL_strdup(filespec2);
    174         if (merged == NULL)
    175             return NULL;
    176     } else
    177     /*
    178      * This part isn't as trivial as it looks.  It assumes that the
    179      * second file specification really is a directory, and makes no
    180      * checks whatsoever.  Therefore, the result becomes the
    181      * concatenation of filespec2 followed by a slash followed by
    182      * filespec1.
    183      */
    184     {
    185         int spec2len, len;
    186 
    187         spec2len = (filespec2 ? strlen(filespec2) : 0);
    188         len = spec2len + (filespec1 ? strlen(filespec1) : 0);
    189 
    190         if (spec2len && filespec2[spec2len - 1] == '/') {
    191             spec2len--;
    192             len--;
    193         }
    194         merged = OPENSSL_malloc(len + 2);
    195         if (merged == NULL)
    196             return NULL;
    197         strcpy(merged, filespec2);
    198         merged[spec2len] = '/';
    199         strcpy(&merged[spec2len + 1], filespec1);
    200     }
    201     return merged;
    202 }
    203 
    204 /*
    205  * This function is identical to the one in dso_dlfcn.c, but as it is highly
    206  * unlikely that both the "dl" *and* "dlfcn" variants are being compiled at
    207  * the same time, there's no great duplicating the code. Figuring out an
    208  * elegant way to share one copy of the code would be more difficult and
    209  * would not leave the implementations independent.
    210  */
    211 static char *dl_name_converter(DSO *dso, const char *filename)
    212 {
    213     char *translated;
    214     int len, rsize, transform;
    215 
    216     len = strlen(filename);
    217     rsize = len + 1;
    218     transform = (strchr(filename, '/') == NULL);
    219     if (transform) {
    220         /* We will convert this to "%s.s?" or "lib%s.s?" */
    221         rsize += strlen(DSO_EXTENSION); /* The length of ".s?" */
    222         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
    223             rsize += 3; /* The length of "lib" */
    224     }
    225     translated = OPENSSL_malloc(rsize);
    226     if (translated == NULL) {
    227         ERR_raise(ERR_LIB_DSO, DSO_R_NAME_TRANSLATION_FAILED);
    228         return NULL;
    229     }
    230     if (transform)
    231         BIO_snprintf(translated, rsize,
    232             (DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0
    233                 ? "lib%s%s"
    234                 : "%s%s",
    235             filename, DSO_EXTENSION);
    236     else
    237         BIO_snprintf(translated, rsize, "%s", filename);
    238     return translated;
    239 }
    240 
    241 static int dl_pathbyaddr(void *addr, char *path, int sz)
    242 {
    243     struct shl_descriptor inf;
    244     int i, len;
    245 
    246     if (addr == NULL) {
    247         union {
    248             int (*f)(void *, char *, int);
    249             void *p;
    250         } t = {
    251             dl_pathbyaddr
    252         };
    253         addr = t.p;
    254     }
    255 
    256     for (i = -1; shl_get_r(i, &inf) == 0; i++) {
    257         if (((size_t)addr >= inf.tstart && (size_t)addr < inf.tend) || ((size_t)addr >= inf.dstart && (size_t)addr < inf.dend)) {
    258             len = (int)strlen(inf.filename);
    259             if (sz <= 0)
    260                 return len + 1;
    261             if (len >= sz)
    262                 len = sz - 1;
    263             memcpy(path, inf.filename, len);
    264             path[len++] = 0;
    265             return len;
    266         }
    267     }
    268 
    269     return -1;
    270 }
    271 
    272 static void *dl_globallookup(const char *name)
    273 {
    274     void *ret;
    275     shl_t h = NULL;
    276 
    277     return shl_findsym(&h, name, TYPE_UNDEFINED, &ret) ? NULL : ret;
    278 }
    279 #endif /* DSO_DL */
    280