1295016Sjkim/* dso_dl.c */
2280304Sjkim/*
3280304Sjkim * Written by Richard Levitte (richard@levitte.org) for the OpenSSL project
4280304Sjkim * 2000.
568651Skris */
668651Skris/* ====================================================================
768651Skris * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
868651Skris *
968651Skris * Redistribution and use in source and binary forms, with or without
1068651Skris * modification, are permitted provided that the following conditions
1168651Skris * are met:
1268651Skris *
1368651Skris * 1. Redistributions of source code must retain the above copyright
14280304Sjkim *    notice, this list of conditions and the following disclaimer.
1568651Skris *
1668651Skris * 2. Redistributions in binary form must reproduce the above copyright
1768651Skris *    notice, this list of conditions and the following disclaimer in
1868651Skris *    the documentation and/or other materials provided with the
1968651Skris *    distribution.
2068651Skris *
2168651Skris * 3. All advertising materials mentioning features or use of this
2268651Skris *    software must display the following acknowledgment:
2368651Skris *    "This product includes software developed by the OpenSSL Project
2468651Skris *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
2568651Skris *
2668651Skris * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
2768651Skris *    endorse or promote products derived from this software without
2868651Skris *    prior written permission. For written permission, please contact
2968651Skris *    licensing@OpenSSL.org.
3068651Skris *
3168651Skris * 5. Products derived from this software may not be called "OpenSSL"
3268651Skris *    nor may "OpenSSL" appear in their names without prior written
3368651Skris *    permission of the OpenSSL Project.
3468651Skris *
3568651Skris * 6. Redistributions of any form whatsoever must retain the following
3668651Skris *    acknowledgment:
3768651Skris *    "This product includes software developed by the OpenSSL Project
3868651Skris *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
3968651Skris *
4068651Skris * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
4168651Skris * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4268651Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
4368651Skris * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
4468651Skris * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
4568651Skris * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
4668651Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
4768651Skris * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4868651Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
4968651Skris * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
5068651Skris * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
5168651Skris * OF THE POSSIBILITY OF SUCH DAMAGE.
5268651Skris * ====================================================================
5368651Skris *
5468651Skris * This product includes cryptographic software written by Eric Young
5568651Skris * (eay@cryptsoft.com).  This product includes software written by Tim
5668651Skris * Hudson (tjh@cryptsoft.com).
5768651Skris *
5868651Skris */
5968651Skris
6068651Skris#include <stdio.h>
6168651Skris#include "cryptlib.h"
6268651Skris#include <openssl/dso.h>
6368651Skris
6468651Skris#ifndef DSO_DL
6568651SkrisDSO_METHOD *DSO_METHOD_dl(void)
66280304Sjkim{
67280304Sjkim    return NULL;
68280304Sjkim}
6968651Skris#else
7068651Skris
71280304Sjkim# include <dl.h>
7268651Skris
7368651Skris/* Part of the hack in "dl_load" ... */
74280304Sjkim# define DSO_MAX_TRANSLATED_SIZE 256
7568651Skris
76109998Smarkmstatic int dl_load(DSO *dso);
7768651Skrisstatic int dl_unload(DSO *dso);
7868651Skrisstatic void *dl_bind_var(DSO *dso, const char *symname);
7968651Skrisstatic DSO_FUNC_TYPE dl_bind_func(DSO *dso, const char *symname);
80280304Sjkim# if 0
8168651Skrisstatic int dl_unbind_var(DSO *dso, char *symname, void *symptr);
8268651Skrisstatic int dl_unbind_func(DSO *dso, char *symname, DSO_FUNC_TYPE symptr);
8368651Skrisstatic int dl_init(DSO *dso);
8468651Skrisstatic int dl_finish(DSO *dso);
85109998Smarkmstatic int dl_ctrl(DSO *dso, int cmd, long larg, void *parg);
86280304Sjkim# endif
87109998Smarkmstatic char *dl_name_converter(DSO *dso, const char *filename);
88280304Sjkimstatic char *dl_merger(DSO *dso, const char *filespec1,
89280304Sjkim                       const char *filespec2);
90280304Sjkimstatic int dl_pathbyaddr(void *addr, char *path, int sz);
91238405Sjkimstatic void *dl_globallookup(const char *name);
9268651Skris
9368651Skrisstatic DSO_METHOD dso_meth_dl = {
94280304Sjkim    "OpenSSL 'dl' shared library method",
95280304Sjkim    dl_load,
96280304Sjkim    dl_unload,
97280304Sjkim    dl_bind_var,
98280304Sjkim    dl_bind_func,
9968651Skris/* For now, "unbind" doesn't exist */
100280304Sjkim# if 0
101280304Sjkim    NULL,                       /* unbind_var */
102280304Sjkim    NULL,                       /* unbind_func */
103280304Sjkim# endif
104280304Sjkim    NULL,                       /* ctrl */
105280304Sjkim    dl_name_converter,
106280304Sjkim    dl_merger,
107280304Sjkim    NULL,                       /* init */
108280304Sjkim    NULL,                       /* finish */
109280304Sjkim    dl_pathbyaddr,
110280304Sjkim    dl_globallookup
111280304Sjkim};
11268651Skris
11368651SkrisDSO_METHOD *DSO_METHOD_dl(void)
114280304Sjkim{
115280304Sjkim    return (&dso_meth_dl);
116280304Sjkim}
11768651Skris
118280304Sjkim/*
119280304Sjkim * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
120280304Sjkim * (shl_t) returned from shl_load(). NB: I checked on HPUX11 and shl_t is
121280304Sjkim * itself a pointer type so the cast is safe.
12268651Skris */
12368651Skris
124109998Smarkmstatic int dl_load(DSO *dso)
125280304Sjkim{
126280304Sjkim    shl_t ptr = NULL;
127280304Sjkim    /*
128280304Sjkim     * We don't do any fancy retries or anything, just take the method's (or
129280304Sjkim     * DSO's if it has the callback set) best translation of the
130280304Sjkim     * platform-independant filename and try once with that.
131280304Sjkim     */
132280304Sjkim    char *filename = DSO_convert_filename(dso, NULL);
13368651Skris
134280304Sjkim    if (filename == NULL) {
135280304Sjkim        DSOerr(DSO_F_DL_LOAD, DSO_R_NO_FILENAME);
136280304Sjkim        goto err;
137280304Sjkim    }
138280304Sjkim    ptr = shl_load(filename, BIND_IMMEDIATE |
139280304Sjkim                   (dso->flags & DSO_FLAG_NO_NAME_TRANSLATION ? 0 :
140280304Sjkim                    DYNAMIC_PATH), 0L);
141280304Sjkim    if (ptr == NULL) {
142280304Sjkim        DSOerr(DSO_F_DL_LOAD, DSO_R_LOAD_FAILED);
143280304Sjkim        ERR_add_error_data(4, "filename(", filename, "): ", strerror(errno));
144280304Sjkim        goto err;
145280304Sjkim    }
146280304Sjkim    if (!sk_push(dso->meth_data, (char *)ptr)) {
147280304Sjkim        DSOerr(DSO_F_DL_LOAD, DSO_R_STACK_ERROR);
148280304Sjkim        goto err;
149280304Sjkim    }
150280304Sjkim    /*
151280304Sjkim     * Success, stick the converted filename we've loaded under into the DSO
152280304Sjkim     * (it also serves as the indicator that we are currently loaded).
153280304Sjkim     */
154280304Sjkim    dso->loaded_filename = filename;
155280304Sjkim    return (1);
156280304Sjkim err:
157280304Sjkim    /* Cleanup! */
158280304Sjkim    if (filename != NULL)
159280304Sjkim        OPENSSL_free(filename);
160280304Sjkim    if (ptr != NULL)
161280304Sjkim        shl_unload(ptr);
162280304Sjkim    return (0);
163280304Sjkim}
16468651Skris
16568651Skrisstatic int dl_unload(DSO *dso)
166280304Sjkim{
167280304Sjkim    shl_t ptr;
168280304Sjkim    if (dso == NULL) {
169280304Sjkim        DSOerr(DSO_F_DL_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
170280304Sjkim        return (0);
171280304Sjkim    }
172280304Sjkim    if (sk_num(dso->meth_data) < 1)
173280304Sjkim        return (1);
174280304Sjkim    /* Is this statement legal? */
175280304Sjkim    ptr = (shl_t) sk_pop(dso->meth_data);
176280304Sjkim    if (ptr == NULL) {
177280304Sjkim        DSOerr(DSO_F_DL_UNLOAD, DSO_R_NULL_HANDLE);
178280304Sjkim        /*
179280304Sjkim         * Should push the value back onto the stack in case of a retry.
180280304Sjkim         */
181280304Sjkim        sk_push(dso->meth_data, (char *)ptr);
182280304Sjkim        return (0);
183280304Sjkim    }
184280304Sjkim    shl_unload(ptr);
185280304Sjkim    return (1);
186280304Sjkim}
18768651Skris
18868651Skrisstatic void *dl_bind_var(DSO *dso, const char *symname)
189280304Sjkim{
190280304Sjkim    shl_t ptr;
191280304Sjkim    void *sym;
19268651Skris
193280304Sjkim    if ((dso == NULL) || (symname == NULL)) {
194280304Sjkim        DSOerr(DSO_F_DL_BIND_VAR, ERR_R_PASSED_NULL_PARAMETER);
195280304Sjkim        return (NULL);
196280304Sjkim    }
197280304Sjkim    if (sk_num(dso->meth_data) < 1) {
198280304Sjkim        DSOerr(DSO_F_DL_BIND_VAR, DSO_R_STACK_ERROR);
199280304Sjkim        return (NULL);
200280304Sjkim    }
201280304Sjkim    ptr = (shl_t) sk_value(dso->meth_data, sk_num(dso->meth_data) - 1);
202280304Sjkim    if (ptr == NULL) {
203280304Sjkim        DSOerr(DSO_F_DL_BIND_VAR, DSO_R_NULL_HANDLE);
204280304Sjkim        return (NULL);
205280304Sjkim    }
206280304Sjkim    if (shl_findsym(&ptr, symname, TYPE_UNDEFINED, &sym) < 0) {
207280304Sjkim        DSOerr(DSO_F_DL_BIND_VAR, DSO_R_SYM_FAILURE);
208280304Sjkim        ERR_add_error_data(4, "symname(", symname, "): ", strerror(errno));
209280304Sjkim        return (NULL);
210280304Sjkim    }
211280304Sjkim    return (sym);
212280304Sjkim}
21368651Skris
21468651Skrisstatic DSO_FUNC_TYPE dl_bind_func(DSO *dso, const char *symname)
215280304Sjkim{
216280304Sjkim    shl_t ptr;
217280304Sjkim    void *sym;
21868651Skris
219280304Sjkim    if ((dso == NULL) || (symname == NULL)) {
220280304Sjkim        DSOerr(DSO_F_DL_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
221280304Sjkim        return (NULL);
222280304Sjkim    }
223280304Sjkim    if (sk_num(dso->meth_data) < 1) {
224280304Sjkim        DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_STACK_ERROR);
225280304Sjkim        return (NULL);
226280304Sjkim    }
227280304Sjkim    ptr = (shl_t) sk_value(dso->meth_data, sk_num(dso->meth_data) - 1);
228280304Sjkim    if (ptr == NULL) {
229280304Sjkim        DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_NULL_HANDLE);
230280304Sjkim        return (NULL);
231280304Sjkim    }
232280304Sjkim    if (shl_findsym(&ptr, symname, TYPE_UNDEFINED, &sym) < 0) {
233280304Sjkim        DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_SYM_FAILURE);
234280304Sjkim        ERR_add_error_data(4, "symname(", symname, "): ", strerror(errno));
235280304Sjkim        return (NULL);
236280304Sjkim    }
237280304Sjkim    return ((DSO_FUNC_TYPE)sym);
238280304Sjkim}
23968651Skris
240160814Ssimonstatic char *dl_merger(DSO *dso, const char *filespec1, const char *filespec2)
241280304Sjkim{
242280304Sjkim    char *merged;
243160814Ssimon
244280304Sjkim    if (!filespec1 && !filespec2) {
245280304Sjkim        DSOerr(DSO_F_DL_MERGER, ERR_R_PASSED_NULL_PARAMETER);
246280304Sjkim        return (NULL);
247280304Sjkim    }
248280304Sjkim    /*
249280304Sjkim     * If the first file specification is a rooted path, it rules. same goes
250280304Sjkim     * if the second file specification is missing.
251280304Sjkim     */
252280304Sjkim    if (!filespec2 || filespec1[0] == '/') {
253280304Sjkim        merged = OPENSSL_malloc(strlen(filespec1) + 1);
254280304Sjkim        if (!merged) {
255280304Sjkim            DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
256280304Sjkim            return (NULL);
257280304Sjkim        }
258280304Sjkim        strcpy(merged, filespec1);
259280304Sjkim    }
260280304Sjkim    /*
261280304Sjkim     * If the first file specification is missing, the second one rules.
262280304Sjkim     */
263280304Sjkim    else if (!filespec1) {
264280304Sjkim        merged = OPENSSL_malloc(strlen(filespec2) + 1);
265280304Sjkim        if (!merged) {
266280304Sjkim            DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
267280304Sjkim            return (NULL);
268280304Sjkim        }
269280304Sjkim        strcpy(merged, filespec2);
270280304Sjkim    } else
271280304Sjkim        /*
272280304Sjkim         * This part isn't as trivial as it looks.  It assumes that the
273280304Sjkim         * second file specification really is a directory, and makes no
274280304Sjkim         * checks whatsoever.  Therefore, the result becomes the
275280304Sjkim         * concatenation of filespec2 followed by a slash followed by
276280304Sjkim         * filespec1.
277280304Sjkim         */
278280304Sjkim    {
279280304Sjkim        int spec2len, len;
280160814Ssimon
281280304Sjkim        spec2len = (filespec2 ? strlen(filespec2) : 0);
282280304Sjkim        len = spec2len + (filespec1 ? strlen(filespec1) : 0);
283160814Ssimon
284280304Sjkim        if (filespec2 && filespec2[spec2len - 1] == '/') {
285280304Sjkim            spec2len--;
286280304Sjkim            len--;
287280304Sjkim        }
288280304Sjkim        merged = OPENSSL_malloc(len + 2);
289280304Sjkim        if (!merged) {
290280304Sjkim            DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
291280304Sjkim            return (NULL);
292280304Sjkim        }
293280304Sjkim        strcpy(merged, filespec2);
294280304Sjkim        merged[spec2len] = '/';
295280304Sjkim        strcpy(&merged[spec2len + 1], filespec1);
296280304Sjkim    }
297280304Sjkim    return (merged);
298280304Sjkim}
299160814Ssimon
300280304Sjkim/*
301280304Sjkim * This function is identical to the one in dso_dlfcn.c, but as it is highly
302280304Sjkim * unlikely that both the "dl" *and* "dlfcn" variants are being compiled at
303280304Sjkim * the same time, there's no great duplicating the code. Figuring out an
304280304Sjkim * elegant way to share one copy of the code would be more difficult and
305280304Sjkim * would not leave the implementations independant.
306280304Sjkim */
307280304Sjkim# if defined(__hpux)
308109998Smarkmstatic const char extension[] = ".sl";
309280304Sjkim# else
310109998Smarkmstatic const char extension[] = ".so";
311280304Sjkim# endif
312109998Smarkmstatic char *dl_name_converter(DSO *dso, const char *filename)
313280304Sjkim{
314280304Sjkim    char *translated;
315280304Sjkim    int len, rsize, transform;
316109998Smarkm
317280304Sjkim    len = strlen(filename);
318280304Sjkim    rsize = len + 1;
319280304Sjkim    transform = (strstr(filename, "/") == NULL);
320280304Sjkim    {
321280304Sjkim        /* We will convert this to "%s.s?" or "lib%s.s?" */
322280304Sjkim        rsize += strlen(extension); /* The length of ".s?" */
323280304Sjkim        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
324280304Sjkim            rsize += 3;         /* The length of "lib" */
325280304Sjkim    }
326280304Sjkim    translated = OPENSSL_malloc(rsize);
327280304Sjkim    if (translated == NULL) {
328280304Sjkim        DSOerr(DSO_F_DL_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
329280304Sjkim        return (NULL);
330280304Sjkim    }
331280304Sjkim    if (transform) {
332280304Sjkim        if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
333280304Sjkim            sprintf(translated, "lib%s%s", filename, extension);
334280304Sjkim        else
335280304Sjkim            sprintf(translated, "%s%s", filename, extension);
336280304Sjkim    } else
337280304Sjkim        sprintf(translated, "%s", filename);
338280304Sjkim    return (translated);
339280304Sjkim}
34068651Skris
341280304Sjkimstatic int dl_pathbyaddr(void *addr, char *path, int sz)
342280304Sjkim{
343280304Sjkim    struct shl_descriptor inf;
344280304Sjkim    int i, len;
345238405Sjkim
346280304Sjkim    if (addr == NULL) {
347280304Sjkim        union {
348280304Sjkim            int (*f) (void *, char *, int);
349280304Sjkim            void *p;
350280304Sjkim        } t = {
351280304Sjkim            dl_pathbyaddr
352280304Sjkim        };
353280304Sjkim        addr = t.p;
354280304Sjkim    }
355238405Sjkim
356280304Sjkim    for (i = -1; shl_get_r(i, &inf) == 0; i++) {
357280304Sjkim        if (((size_t)addr >= inf.tstart && (size_t)addr < inf.tend) ||
358280304Sjkim            ((size_t)addr >= inf.dstart && (size_t)addr < inf.dend)) {
359280304Sjkim            len = (int)strlen(inf.filename);
360280304Sjkim            if (sz <= 0)
361280304Sjkim                return len + 1;
362280304Sjkim            if (len >= sz)
363280304Sjkim                len = sz - 1;
364280304Sjkim            memcpy(path, inf.filename, len);
365280304Sjkim            path[len++] = 0;
366280304Sjkim            return len;
367280304Sjkim        }
368280304Sjkim    }
369238405Sjkim
370280304Sjkim    return -1;
371280304Sjkim}
372238405Sjkim
373238405Sjkimstatic void *dl_globallookup(const char *name)
374280304Sjkim{
375280304Sjkim    void *ret;
376280304Sjkim    shl_t h = NULL;
377238405Sjkim
378280304Sjkim    return shl_findsym(&h, name, TYPE_UNDEFINED, &ret) ? NULL : ret;
379280304Sjkim}
380280304Sjkim#endif                          /* DSO_DL */
381