1d6e12adc5a57a5d15601218cf550bb76e0ab2a5
[openssl.git] / crypto / dso / dso_dl.c
1 /*
2  * Written by Richard Levitte (richard@levitte.org) for the OpenSSL project
3  * 2000.
4  */
5 /* ====================================================================
6  * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. All advertising materials mentioning features or use of this
21  *    software must display the following acknowledgment:
22  *    "This product includes software developed by the OpenSSL Project
23  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24  *
25  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26  *    endorse or promote products derived from this software without
27  *    prior written permission. For written permission, please contact
28  *    licensing@OpenSSL.org.
29  *
30  * 5. Products derived from this software may not be called "OpenSSL"
31  *    nor may "OpenSSL" appear in their names without prior written
32  *    permission of the OpenSSL Project.
33  *
34  * 6. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by the OpenSSL Project
37  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38  *
39  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50  * OF THE POSSIBILITY OF SUCH DAMAGE.
51  * ====================================================================
52  *
53  * This product includes cryptographic software written by Eric Young
54  * (eay@cryptsoft.com).  This product includes software written by Tim
55  * Hudson (tjh@cryptsoft.com).
56  *
57  */
58
59 #include "dso_locl.h"
60
61 #ifndef DSO_DL
62 DSO_METHOD *DSO_METHOD_dl(void)
63 {
64     return NULL;
65 }
66 #else
67
68 # include <dl.h>
69
70 /* Part of the hack in "dl_load" ... */
71 # define DSO_MAX_TRANSLATED_SIZE 256
72
73 static int dl_load(DSO *dso);
74 static int dl_unload(DSO *dso);
75 static void *dl_bind_var(DSO *dso, const char *symname);
76 static DSO_FUNC_TYPE dl_bind_func(DSO *dso, const char *symname);
77 static char *dl_name_converter(DSO *dso, const char *filename);
78 static char *dl_merger(DSO *dso, const char *filespec1,
79                        const char *filespec2);
80 static int dl_pathbyaddr(void *addr, char *path, int sz);
81 static void *dl_globallookup(const char *name);
82
83 static DSO_METHOD dso_meth_dl = {
84     "OpenSSL 'dl' shared library method",
85     dl_load,
86     dl_unload,
87     dl_bind_var,
88     dl_bind_func,
89     NULL,                       /* ctrl */
90     dl_name_converter,
91     dl_merger,
92     NULL,                       /* init */
93     NULL,                       /* finish */
94     dl_pathbyaddr,
95     dl_globallookup
96 };
97
98 DSO_METHOD *DSO_METHOD_dl(void)
99 {
100     return (&dso_meth_dl);
101 }
102
103 /*
104  * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
105  * (shl_t) returned from shl_load(). NB: I checked on HPUX11 and shl_t is
106  * itself a pointer type so the cast is safe.
107  */
108
109 static int dl_load(DSO *dso)
110 {
111     shl_t ptr = NULL;
112     /*
113      * We don't do any fancy retries or anything, just take the method's (or
114      * DSO's if it has the callback set) best translation of the
115      * platform-independent filename and try once with that.
116      */
117     char *filename = DSO_convert_filename(dso, NULL);
118
119     if (filename == NULL) {
120         DSOerr(DSO_F_DL_LOAD, DSO_R_NO_FILENAME);
121         goto err;
122     }
123     ptr = shl_load(filename, BIND_IMMEDIATE |
124                    (dso->flags & DSO_FLAG_NO_NAME_TRANSLATION ? 0 :
125                     DYNAMIC_PATH), 0L);
126     if (ptr == NULL) {
127         DSOerr(DSO_F_DL_LOAD, DSO_R_LOAD_FAILED);
128         ERR_add_error_data(4, "filename(", filename, "): ", strerror(errno));
129         goto err;
130     }
131     if (!sk_push(dso->meth_data, (char *)ptr)) {
132         DSOerr(DSO_F_DL_LOAD, DSO_R_STACK_ERROR);
133         goto err;
134     }
135     /*
136      * Success, stick the converted filename we've loaded under into the DSO
137      * (it also serves as the indicator that we are currently loaded).
138      */
139     dso->loaded_filename = filename;
140     return (1);
141  err:
142     /* Cleanup! */
143     OPENSSL_free(filename);
144     if (ptr != NULL)
145         shl_unload(ptr);
146     return (0);
147 }
148
149 static int dl_unload(DSO *dso)
150 {
151     shl_t ptr;
152     if (dso == NULL) {
153         DSOerr(DSO_F_DL_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
154         return (0);
155     }
156     if (sk_num(dso->meth_data) < 1)
157         return (1);
158     /* Is this statement legal? */
159     ptr = (shl_t) sk_pop(dso->meth_data);
160     if (ptr == NULL) {
161         DSOerr(DSO_F_DL_UNLOAD, DSO_R_NULL_HANDLE);
162         /*
163          * Should push the value back onto the stack in case of a retry.
164          */
165         sk_push(dso->meth_data, (char *)ptr);
166         return (0);
167     }
168     shl_unload(ptr);
169     return (1);
170 }
171
172 static void *dl_bind_var(DSO *dso, const char *symname)
173 {
174     shl_t ptr;
175     void *sym;
176
177     if ((dso == NULL) || (symname == NULL)) {
178         DSOerr(DSO_F_DL_BIND_VAR, ERR_R_PASSED_NULL_PARAMETER);
179         return (NULL);
180     }
181     if (sk_num(dso->meth_data) < 1) {
182         DSOerr(DSO_F_DL_BIND_VAR, DSO_R_STACK_ERROR);
183         return (NULL);
184     }
185     ptr = (shl_t) sk_value(dso->meth_data, sk_num(dso->meth_data) - 1);
186     if (ptr == NULL) {
187         DSOerr(DSO_F_DL_BIND_VAR, DSO_R_NULL_HANDLE);
188         return (NULL);
189     }
190     if (shl_findsym(&ptr, symname, TYPE_UNDEFINED, &sym) < 0) {
191         DSOerr(DSO_F_DL_BIND_VAR, DSO_R_SYM_FAILURE);
192         ERR_add_error_data(4, "symname(", symname, "): ", strerror(errno));
193         return (NULL);
194     }
195     return (sym);
196 }
197
198 static DSO_FUNC_TYPE dl_bind_func(DSO *dso, const char *symname)
199 {
200     shl_t ptr;
201     void *sym;
202
203     if ((dso == NULL) || (symname == NULL)) {
204         DSOerr(DSO_F_DL_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
205         return (NULL);
206     }
207     if (sk_num(dso->meth_data) < 1) {
208         DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_STACK_ERROR);
209         return (NULL);
210     }
211     ptr = (shl_t) sk_value(dso->meth_data, sk_num(dso->meth_data) - 1);
212     if (ptr == NULL) {
213         DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_NULL_HANDLE);
214         return (NULL);
215     }
216     if (shl_findsym(&ptr, symname, TYPE_UNDEFINED, &sym) < 0) {
217         DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_SYM_FAILURE);
218         ERR_add_error_data(4, "symname(", symname, "): ", strerror(errno));
219         return (NULL);
220     }
221     return ((DSO_FUNC_TYPE)sym);
222 }
223
224 static char *dl_merger(DSO *dso, const char *filespec1, const char *filespec2)
225 {
226     char *merged;
227
228     if (!filespec1 && !filespec2) {
229         DSOerr(DSO_F_DL_MERGER, ERR_R_PASSED_NULL_PARAMETER);
230         return (NULL);
231     }
232     /*
233      * If the first file specification is a rooted path, it rules. same goes
234      * if the second file specification is missing.
235      */
236     if (!filespec2 || filespec1[0] == '/') {
237         merged = OPENSSL_strdup(filespec1);
238         if (merged == NULL) {
239             DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
240             return (NULL);
241         }
242     }
243     /*
244      * If the first file specification is missing, the second one rules.
245      */
246     else if (!filespec1) {
247         merged = OPENSSL_strdup(filespec2);
248         if (merged == NULL) {
249             DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
250             return (NULL);
251         }
252     } else
253         /*
254          * This part isn't as trivial as it looks.  It assumes that the
255          * second file specification really is a directory, and makes no
256          * checks whatsoever.  Therefore, the result becomes the
257          * concatenation of filespec2 followed by a slash followed by
258          * filespec1.
259          */
260     {
261         int spec2len, len;
262
263         spec2len = (filespec2 ? strlen(filespec2) : 0);
264         len = spec2len + (filespec1 ? strlen(filespec1) : 0);
265
266         if (spec2len && filespec2[spec2len - 1] == '/') {
267             spec2len--;
268             len--;
269         }
270         merged = OPENSSL_malloc(len + 2);
271         if (merged == NULL) {
272             DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
273             return (NULL);
274         }
275         strcpy(merged, filespec2);
276         merged[spec2len] = '/';
277         strcpy(&merged[spec2len + 1], filespec1);
278     }
279     return (merged);
280 }
281
282 /*
283  * This function is identical to the one in dso_dlfcn.c, but as it is highly
284  * unlikely that both the "dl" *and* "dlfcn" variants are being compiled at
285  * the same time, there's no great duplicating the code. Figuring out an
286  * elegant way to share one copy of the code would be more difficult and
287  * would not leave the implementations independent.
288  */
289 static char *dl_name_converter(DSO *dso, const char *filename)
290 {
291     char *translated;
292     int len, rsize, transform;
293
294     len = strlen(filename);
295     rsize = len + 1;
296     transform = (strstr(filename, "/") == NULL);
297     {
298         /* We will convert this to "%s.s?" or "lib%s.s?" */
299         rsize += strlen(DSO_EXTENSION); /* The length of ".s?" */
300         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
301             rsize += 3;         /* The length of "lib" */
302     }
303     translated = OPENSSL_malloc(rsize);
304     if (translated == NULL) {
305         DSOerr(DSO_F_DL_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
306         return (NULL);
307     }
308     if (transform) {
309         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
310             sprintf(translated, "lib%s%s", filename, DSO_EXTENSION);
311         else
312             sprintf(translated, "%s%s", filename, DSO_EXTENSION);
313     } else
314         sprintf(translated, "%s", filename);
315     return (translated);
316 }
317
318 static int dl_pathbyaddr(void *addr, char *path, int sz)
319 {
320     struct shl_descriptor inf;
321     int i, len;
322
323     if (addr == NULL) {
324         union {
325             int (*f) (void *, char *, int);
326             void *p;
327         } t = {
328             dl_pathbyaddr
329         };
330         addr = t.p;
331     }
332
333     for (i = -1; shl_get_r(i, &inf) == 0; i++) {
334         if (((size_t)addr >= inf.tstart && (size_t)addr < inf.tend) ||
335             ((size_t)addr >= inf.dstart && (size_t)addr < inf.dend)) {
336             len = (int)strlen(inf.filename);
337             if (sz <= 0)
338                 return len + 1;
339             if (len >= sz)
340                 len = sz - 1;
341             memcpy(path, inf.filename, len);
342             path[len++] = 0;
343             return len;
344         }
345     }
346
347     return -1;
348 }
349
350 static void *dl_globallookup(const char *name)
351 {
352     void *ret;
353     shl_t h = NULL;
354
355     return shl_findsym(&h, name, TYPE_UNDEFINED, &ret) ? NULL : ret;
356 }
357 #endif                          /* DSO_DL */