Blame crypto/dso/dso_dlfcn.c

Packit c4476c
/*
Packit c4476c
 * Copyright 2000-2019 The OpenSSL Project Authors. All Rights Reserved.
Packit c4476c
 *
Packit c4476c
 * Licensed under the OpenSSL license (the "License").  You may not use
Packit c4476c
 * this file except in compliance with the License.  You can obtain a copy
Packit c4476c
 * in the file LICENSE in the source distribution or at
Packit c4476c
 * https://www.openssl.org/source/license.html
Packit c4476c
 */
Packit c4476c
Packit c4476c
/*
Packit c4476c
 * We need to do this early, because stdio.h includes the header files that
Packit c4476c
 * handle _GNU_SOURCE and other similar macros.  Defining it later is simply
Packit c4476c
 * too late, because those headers are protected from re- inclusion.
Packit c4476c
 */
Packit c4476c
#ifndef _GNU_SOURCE
Packit c4476c
# define _GNU_SOURCE            /* make sure dladdr is declared */
Packit c4476c
#endif
Packit c4476c
Packit c4476c
#include "dso_local.h"
Packit c4476c
#include "e_os.h"
Packit c4476c
Packit c4476c
#ifdef DSO_DLFCN
Packit c4476c
Packit c4476c
# ifdef HAVE_DLFCN_H
Packit c4476c
#  ifdef __osf__
Packit c4476c
#   define __EXTENSIONS__
Packit c4476c
#  endif
Packit c4476c
#  include <dlfcn.h>
Packit c4476c
#  define HAVE_DLINFO 1
Packit c4476c
#  if defined(__SCO_VERSION__) || defined(_SCO_ELF) || \
Packit c4476c
     (defined(__osf__) && !defined(RTLD_NEXT))     || \
Packit c4476c
     (defined(__OpenBSD__) && !defined(RTLD_SELF)) || \
Packit c4476c
        defined(__ANDROID__)
Packit c4476c
#   undef HAVE_DLINFO
Packit c4476c
#  endif
Packit c4476c
# endif
Packit c4476c
Packit c4476c
/* Part of the hack in "dlfcn_load" ... */
Packit c4476c
# define DSO_MAX_TRANSLATED_SIZE 256
Packit c4476c
Packit c4476c
static int dlfcn_load(DSO *dso);
Packit c4476c
static int dlfcn_unload(DSO *dso);
Packit c4476c
static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname);
Packit c4476c
static char *dlfcn_name_converter(DSO *dso, const char *filename);
Packit c4476c
static char *dlfcn_merger(DSO *dso, const char *filespec1,
Packit c4476c
                          const char *filespec2);
Packit c4476c
static int dlfcn_pathbyaddr(void *addr, char *path, int sz);
Packit c4476c
static void *dlfcn_globallookup(const char *name);
Packit c4476c
Packit c4476c
static DSO_METHOD dso_meth_dlfcn = {
Packit c4476c
    "OpenSSL 'dlfcn' shared library method",
Packit c4476c
    dlfcn_load,
Packit c4476c
    dlfcn_unload,
Packit c4476c
    dlfcn_bind_func,
Packit c4476c
    NULL,                       /* ctrl */
Packit c4476c
    dlfcn_name_converter,
Packit c4476c
    dlfcn_merger,
Packit c4476c
    NULL,                       /* init */
Packit c4476c
    NULL,                       /* finish */
Packit c4476c
    dlfcn_pathbyaddr,
Packit c4476c
    dlfcn_globallookup
Packit c4476c
};
Packit c4476c
Packit c4476c
DSO_METHOD *DSO_METHOD_openssl(void)
Packit c4476c
{
Packit c4476c
    return &dso_meth_dlfcn;
Packit c4476c
}
Packit c4476c
Packit c4476c
/*
Packit c4476c
 * Prior to using the dlopen() function, we should decide on the flag we
Packit c4476c
 * send. There's a few different ways of doing this and it's a messy
Packit c4476c
 * venn-diagram to match up which platforms support what. So as we don't have
Packit c4476c
 * autoconf yet, I'm implementing a hack that could be hacked further
Packit c4476c
 * relatively easily to deal with cases as we find them. Initially this is to
Packit c4476c
 * cope with OpenBSD.
Packit c4476c
 */
Packit c4476c
# if defined(__OpenBSD__) || defined(__NetBSD__)
Packit c4476c
#  ifdef DL_LAZY
Packit c4476c
#   define DLOPEN_FLAG DL_LAZY
Packit c4476c
#  else
Packit c4476c
#   ifdef RTLD_NOW
Packit c4476c
#    define DLOPEN_FLAG RTLD_NOW
Packit c4476c
#   else
Packit c4476c
#    define DLOPEN_FLAG 0
Packit c4476c
#   endif
Packit c4476c
#  endif
Packit c4476c
# else
Packit c4476c
#  define DLOPEN_FLAG RTLD_NOW  /* Hope this works everywhere else */
Packit c4476c
# endif
Packit c4476c
Packit c4476c
/*
Packit c4476c
 * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
Packit c4476c
 * (void*) returned from dlopen().
Packit c4476c
 */
Packit c4476c
Packit c4476c
static int dlfcn_load(DSO *dso)
Packit c4476c
{
Packit c4476c
    void *ptr = NULL;
Packit c4476c
    /* See applicable comments in dso_dl.c */
Packit c4476c
    char *filename = DSO_convert_filename(dso, NULL);
Packit c4476c
    int flags = DLOPEN_FLAG;
Packit c4476c
    int saveerrno = get_last_sys_error();
Packit c4476c
Packit c4476c
    if (filename == NULL) {
Packit c4476c
        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_NO_FILENAME);
Packit c4476c
        goto err;
Packit c4476c
    }
Packit c4476c
# ifdef RTLD_GLOBAL
Packit c4476c
    if (dso->flags & DSO_FLAG_GLOBAL_SYMBOLS)
Packit c4476c
        flags |= RTLD_GLOBAL;
Packit c4476c
# endif
Packit c4476c
# ifdef _AIX
Packit c4476c
    if (filename[strlen(filename) - 1] == ')')
Packit c4476c
        flags |= RTLD_MEMBER;
Packit c4476c
# endif
Packit c4476c
    ptr = dlopen(filename, flags);
Packit c4476c
    if (ptr == NULL) {
Packit c4476c
        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_LOAD_FAILED);
Packit c4476c
        ERR_add_error_data(4, "filename(", filename, "): ", dlerror());
Packit c4476c
        goto err;
Packit c4476c
    }
Packit c4476c
    /*
Packit c4476c
     * Some dlopen() implementations (e.g. solaris) do no preserve errno, even
Packit c4476c
     * on a successful call.
Packit c4476c
     */
Packit c4476c
    set_sys_error(saveerrno);
Packit c4476c
    if (!sk_void_push(dso->meth_data, (char *)ptr)) {
Packit c4476c
        DSOerr(DSO_F_DLFCN_LOAD, DSO_R_STACK_ERROR);
Packit c4476c
        goto err;
Packit c4476c
    }
Packit c4476c
    /* Success */
Packit c4476c
    dso->loaded_filename = filename;
Packit c4476c
    return 1;
Packit c4476c
 err:
Packit c4476c
    /* Cleanup! */
Packit c4476c
    OPENSSL_free(filename);
Packit c4476c
    if (ptr != NULL)
Packit c4476c
        dlclose(ptr);
Packit c4476c
    return 0;
Packit c4476c
}
Packit c4476c
Packit c4476c
static int dlfcn_unload(DSO *dso)
Packit c4476c
{
Packit c4476c
    void *ptr;
Packit c4476c
    if (dso == NULL) {
Packit c4476c
        DSOerr(DSO_F_DLFCN_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
Packit c4476c
        return 0;
Packit c4476c
    }
Packit c4476c
    if (sk_void_num(dso->meth_data) < 1)
Packit c4476c
        return 1;
Packit c4476c
    ptr = sk_void_pop(dso->meth_data);
Packit c4476c
    if (ptr == NULL) {
Packit c4476c
        DSOerr(DSO_F_DLFCN_UNLOAD, DSO_R_NULL_HANDLE);
Packit c4476c
        /*
Packit c4476c
         * Should push the value back onto the stack in case of a retry.
Packit c4476c
         */
Packit c4476c
        sk_void_push(dso->meth_data, ptr);
Packit c4476c
        return 0;
Packit c4476c
    }
Packit c4476c
    /* For now I'm not aware of any errors associated with dlclose() */
Packit c4476c
    dlclose(ptr);
Packit c4476c
    return 1;
Packit c4476c
}
Packit c4476c
Packit c4476c
static DSO_FUNC_TYPE dlfcn_bind_func(DSO *dso, const char *symname)
Packit c4476c
{
Packit c4476c
    void *ptr;
Packit c4476c
    union {
Packit c4476c
        DSO_FUNC_TYPE sym;
Packit c4476c
        void *dlret;
Packit c4476c
    } u;
Packit c4476c
Packit c4476c
    if ((dso == NULL) || (symname == NULL)) {
Packit c4476c
        DSOerr(DSO_F_DLFCN_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
Packit c4476c
        return NULL;
Packit c4476c
    }
Packit c4476c
    if (sk_void_num(dso->meth_data) < 1) {
Packit c4476c
        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_STACK_ERROR);
Packit c4476c
        return NULL;
Packit c4476c
    }
Packit c4476c
    ptr = sk_void_value(dso->meth_data, sk_void_num(dso->meth_data) - 1);
Packit c4476c
    if (ptr == NULL) {
Packit c4476c
        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_NULL_HANDLE);
Packit c4476c
        return NULL;
Packit c4476c
    }
Packit c4476c
    u.dlret = dlsym(ptr, symname);
Packit c4476c
    if (u.dlret == NULL) {
Packit c4476c
        DSOerr(DSO_F_DLFCN_BIND_FUNC, DSO_R_SYM_FAILURE);
Packit c4476c
        ERR_add_error_data(4, "symname(", symname, "): ", dlerror());
Packit c4476c
        return NULL;
Packit c4476c
    }
Packit c4476c
    return u.sym;
Packit c4476c
}
Packit c4476c
Packit c4476c
static char *dlfcn_merger(DSO *dso, const char *filespec1,
Packit c4476c
                          const char *filespec2)
Packit c4476c
{
Packit c4476c
    char *merged;
Packit c4476c
Packit c4476c
    if (!filespec1 && !filespec2) {
Packit c4476c
        DSOerr(DSO_F_DLFCN_MERGER, ERR_R_PASSED_NULL_PARAMETER);
Packit c4476c
        return NULL;
Packit c4476c
    }
Packit c4476c
    /*
Packit c4476c
     * If the first file specification is a rooted path, it rules. same goes
Packit c4476c
     * if the second file specification is missing.
Packit c4476c
     */
Packit c4476c
    if (!filespec2 || (filespec1 != NULL && filespec1[0] == '/')) {
Packit c4476c
        merged = OPENSSL_strdup(filespec1);
Packit c4476c
        if (merged == NULL) {
Packit c4476c
            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
Packit c4476c
            return NULL;
Packit c4476c
        }
Packit c4476c
    }
Packit c4476c
    /*
Packit c4476c
     * If the first file specification is missing, the second one rules.
Packit c4476c
     */
Packit c4476c
    else if (!filespec1) {
Packit c4476c
        merged = OPENSSL_strdup(filespec2);
Packit c4476c
        if (merged == NULL) {
Packit c4476c
            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
Packit c4476c
            return NULL;
Packit c4476c
        }
Packit c4476c
    } else {
Packit c4476c
        /*
Packit c4476c
         * This part isn't as trivial as it looks.  It assumes that the
Packit c4476c
         * second file specification really is a directory, and makes no
Packit c4476c
         * checks whatsoever.  Therefore, the result becomes the
Packit c4476c
         * concatenation of filespec2 followed by a slash followed by
Packit c4476c
         * filespec1.
Packit c4476c
         */
Packit c4476c
        int spec2len, len;
Packit c4476c
Packit c4476c
        spec2len = strlen(filespec2);
Packit c4476c
        len = spec2len + strlen(filespec1);
Packit c4476c
Packit c4476c
        if (spec2len && filespec2[spec2len - 1] == '/') {
Packit c4476c
            spec2len--;
Packit c4476c
            len--;
Packit c4476c
        }
Packit c4476c
        merged = OPENSSL_malloc(len + 2);
Packit c4476c
        if (merged == NULL) {
Packit c4476c
            DSOerr(DSO_F_DLFCN_MERGER, ERR_R_MALLOC_FAILURE);
Packit c4476c
            return NULL;
Packit c4476c
        }
Packit c4476c
        strcpy(merged, filespec2);
Packit c4476c
        merged[spec2len] = '/';
Packit c4476c
        strcpy(&merged[spec2len + 1], filespec1);
Packit c4476c
    }
Packit c4476c
    return merged;
Packit c4476c
}
Packit c4476c
Packit c4476c
static char *dlfcn_name_converter(DSO *dso, const char *filename)
Packit c4476c
{
Packit c4476c
    char *translated;
Packit c4476c
    int len, rsize, transform;
Packit c4476c
Packit c4476c
    len = strlen(filename);
Packit c4476c
    rsize = len + 1;
Packit c4476c
    transform = (strstr(filename, "/") == NULL);
Packit c4476c
    if (transform) {
Packit c4476c
        /* We will convert this to "%s.so" or "lib%s.so" etc */
Packit c4476c
        rsize += strlen(DSO_EXTENSION);    /* The length of ".so" */
Packit c4476c
        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
Packit c4476c
            rsize += 3;         /* The length of "lib" */
Packit c4476c
    }
Packit c4476c
    translated = OPENSSL_malloc(rsize);
Packit c4476c
    if (translated == NULL) {
Packit c4476c
        DSOerr(DSO_F_DLFCN_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
Packit c4476c
        return NULL;
Packit c4476c
    }
Packit c4476c
    if (transform) {
Packit c4476c
        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
Packit c4476c
            sprintf(translated, "lib%s" DSO_EXTENSION, filename);
Packit c4476c
        else
Packit c4476c
            sprintf(translated, "%s" DSO_EXTENSION, filename);
Packit c4476c
    } else
Packit c4476c
        sprintf(translated, "%s", filename);
Packit c4476c
    return translated;
Packit c4476c
}
Packit c4476c
Packit c4476c
# ifdef __sgi
Packit c4476c
/*-
Packit c4476c
This is a quote from IRIX manual for dladdr(3c):
Packit c4476c
Packit c4476c
     <dlfcn.h> does not contain a prototype for dladdr or definition of
Packit c4476c
     Dl_info.  The #include <dlfcn.h>  in the SYNOPSIS line is traditional,
Packit c4476c
     but contains no dladdr prototype and no IRIX library contains an
Packit c4476c
     implementation.  Write your own declaration based on the code below.
Packit c4476c
Packit c4476c
     The following code is dependent on internal interfaces that are not
Packit c4476c
     part of the IRIX compatibility guarantee; however, there is no future
Packit c4476c
     intention to change this interface, so on a practical level, the code
Packit c4476c
     below is safe to use on IRIX.
Packit c4476c
*/
Packit c4476c
#  include <rld_interface.h>
Packit c4476c
#  ifndef _RLD_INTERFACE_DLFCN_H_DLADDR
Packit c4476c
#   define _RLD_INTERFACE_DLFCN_H_DLADDR
Packit c4476c
typedef struct Dl_info {
Packit c4476c
    const char *dli_fname;
Packit c4476c
    void *dli_fbase;
Packit c4476c
    const char *dli_sname;
Packit c4476c
    void *dli_saddr;
Packit c4476c
    int dli_version;
Packit c4476c
    int dli_reserved1;
Packit c4476c
    long dli_reserved[4];
Packit c4476c
} Dl_info;
Packit c4476c
#  else
Packit c4476c
typedef struct Dl_info Dl_info;
Packit c4476c
#  endif
Packit c4476c
#  define _RLD_DLADDR             14
Packit c4476c
Packit c4476c
static int dladdr(void *address, Dl_info *dl)
Packit c4476c
{
Packit c4476c
    void *v;
Packit c4476c
    v = _rld_new_interface(_RLD_DLADDR, address, dl);
Packit c4476c
    return (int)v;
Packit c4476c
}
Packit c4476c
# endif                         /* __sgi */
Packit c4476c
Packit c4476c
# ifdef _AIX
Packit c4476c
/*-
Packit c4476c
 * See IBM's AIX Version 7.2, Technical Reference:
Packit c4476c
 *  Base Operating System and Extensions, Volume 1 and 2
Packit c4476c
 *  https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.base/technicalreferences.htm
Packit c4476c
 */
Packit c4476c
#  include <sys/ldr.h>
Packit c4476c
#  include <errno.h>
Packit c4476c
/* ~ 64 * (sizeof(struct ld_info) + _XOPEN_PATH_MAX + _XOPEN_NAME_MAX) */
Packit c4476c
#  define DLFCN_LDINFO_SIZE 86976
Packit c4476c
typedef struct Dl_info {
Packit c4476c
    const char *dli_fname;
Packit c4476c
} Dl_info;
Packit c4476c
/*
Packit c4476c
 * This dladdr()-implementation will also find the ptrgl (Pointer Glue) virtual
Packit c4476c
 * address of a function, which is just located in the DATA segment instead of
Packit c4476c
 * the TEXT segment.
Packit c4476c
 */
Packit c4476c
static int dladdr(void *ptr, Dl_info *dl)
Packit c4476c
{
Packit c4476c
    uintptr_t addr = (uintptr_t)ptr;
Packit c4476c
    unsigned int found = 0;
Packit c4476c
    struct ld_info *ldinfos, *next_ldi, *this_ldi;
Packit c4476c
Packit c4476c
    if ((ldinfos = OPENSSL_malloc(DLFCN_LDINFO_SIZE)) == NULL) {
Packit c4476c
        errno = ENOMEM;
Packit c4476c
        dl->dli_fname = NULL;
Packit c4476c
        return 0;
Packit c4476c
    }
Packit c4476c
Packit c4476c
    if ((loadquery(L_GETINFO, (void *)ldinfos, DLFCN_LDINFO_SIZE)) < 0) {
Packit c4476c
        /*-
Packit c4476c
         * Error handling is done through errno and dlerror() reading errno:
Packit c4476c
         *  ENOMEM (ldinfos buffer is too small),
Packit c4476c
         *  EINVAL (invalid flags),
Packit c4476c
         *  EFAULT (invalid ldinfos ptr)
Packit c4476c
         */
Packit c4476c
        OPENSSL_free((void *)ldinfos);
Packit c4476c
        dl->dli_fname = NULL;
Packit c4476c
        return 0;
Packit c4476c
    }
Packit c4476c
    next_ldi = ldinfos;
Packit c4476c
Packit c4476c
    do {
Packit c4476c
        this_ldi = next_ldi;
Packit c4476c
        if (((addr >= (uintptr_t)this_ldi->ldinfo_textorg)
Packit c4476c
             && (addr < ((uintptr_t)this_ldi->ldinfo_textorg +
Packit c4476c
                         this_ldi->ldinfo_textsize)))
Packit c4476c
            || ((addr >= (uintptr_t)this_ldi->ldinfo_dataorg)
Packit c4476c
                && (addr < ((uintptr_t)this_ldi->ldinfo_dataorg +
Packit c4476c
                            this_ldi->ldinfo_datasize)))) {
Packit c4476c
            char *buffer, *member;
Packit c4476c
            size_t buffer_sz, member_len;
Packit c4476c
Packit c4476c
            buffer_sz = strlen(this_ldi->ldinfo_filename) + 1;
Packit c4476c
            member = this_ldi->ldinfo_filename + buffer_sz;
Packit c4476c
            if ((member_len = strlen(member)) > 0)
Packit c4476c
                buffer_sz += 1 + member_len + 1;
Packit c4476c
            found = 1;
Packit c4476c
            if ((buffer = OPENSSL_malloc(buffer_sz)) != NULL) {
Packit c4476c
                OPENSSL_strlcpy(buffer, this_ldi->ldinfo_filename, buffer_sz);
Packit c4476c
                if (member_len > 0) {
Packit c4476c
                    /*
Packit c4476c
                     * Need to respect a possible member name and not just
Packit c4476c
                     * returning the path name in this case. See docs:
Packit c4476c
                     * sys/ldr.h, loadquery() and dlopen()/RTLD_MEMBER.
Packit c4476c
                     */
Packit c4476c
                    OPENSSL_strlcat(buffer, "(", buffer_sz);
Packit c4476c
                    OPENSSL_strlcat(buffer, member, buffer_sz);
Packit c4476c
                    OPENSSL_strlcat(buffer, ")", buffer_sz);
Packit c4476c
                }
Packit c4476c
                dl->dli_fname = buffer;
Packit c4476c
            } else {
Packit c4476c
                errno = ENOMEM;
Packit c4476c
            }
Packit c4476c
        } else {
Packit c4476c
            next_ldi = (struct ld_info *)((uintptr_t)this_ldi +
Packit c4476c
                                          this_ldi->ldinfo_next);
Packit c4476c
        }
Packit c4476c
    } while (this_ldi->ldinfo_next && !found);
Packit c4476c
    OPENSSL_free((void *)ldinfos);
Packit c4476c
    return (found && dl->dli_fname != NULL);
Packit c4476c
}
Packit c4476c
# endif                         /* _AIX */
Packit c4476c
Packit c4476c
static int dlfcn_pathbyaddr(void *addr, char *path, int sz)
Packit c4476c
{
Packit c4476c
# ifdef HAVE_DLINFO
Packit c4476c
    Dl_info dli;
Packit c4476c
    int len;
Packit c4476c
Packit c4476c
    if (addr == NULL) {
Packit c4476c
        union {
Packit c4476c
            int (*f) (void *, char *, int);
Packit c4476c
            void *p;
Packit c4476c
        } t = {
Packit c4476c
            dlfcn_pathbyaddr
Packit c4476c
        };
Packit c4476c
        addr = t.p;
Packit c4476c
    }
Packit c4476c
Packit c4476c
    if (dladdr(addr, &dli)) {
Packit c4476c
        len = (int)strlen(dli.dli_fname);
Packit c4476c
        if (sz <= 0) {
Packit c4476c
#  ifdef _AIX
Packit c4476c
            OPENSSL_free((void *)dli.dli_fname);
Packit c4476c
#  endif
Packit c4476c
            return len + 1;
Packit c4476c
        }
Packit c4476c
        if (len >= sz)
Packit c4476c
            len = sz - 1;
Packit c4476c
        memcpy(path, dli.dli_fname, len);
Packit c4476c
        path[len++] = 0;
Packit c4476c
#  ifdef _AIX
Packit c4476c
        OPENSSL_free((void *)dli.dli_fname);
Packit c4476c
#  endif
Packit c4476c
        return len;
Packit c4476c
    }
Packit c4476c
Packit c4476c
    ERR_add_error_data(2, "dlfcn_pathbyaddr(): ", dlerror());
Packit c4476c
# endif
Packit c4476c
    return -1;
Packit c4476c
}
Packit c4476c
Packit c4476c
static void *dlfcn_globallookup(const char *name)
Packit c4476c
{
Packit c4476c
    void *ret = NULL, *handle = dlopen(NULL, RTLD_LAZY);
Packit c4476c
Packit c4476c
    if (handle) {
Packit c4476c
        ret = dlsym(handle, name);
Packit c4476c
        dlclose(handle);
Packit c4476c
    }
Packit c4476c
Packit c4476c
    return ret;
Packit c4476c
}
Packit c4476c
#endif                          /* DSO_DLFCN */