Copyright consolidation 07/10
[oweals/openssl.git] / crypto / dso / dso_dl.c
1 /*
2  * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (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_locl.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 void *dl_globallookup(const char *name);
26
27 static DSO_METHOD dso_meth_dl = {
28     "OpenSSL 'dl' shared library method",
29     dl_load,
30     dl_unload,
31     dl_bind_func,
32     NULL,                       /* ctrl */
33     dl_name_converter,
34     dl_merger,
35     NULL,                       /* init */
36     NULL,                       /* finish */
37     dl_globallookup
38 };
39
40 DSO_METHOD *DSO_METHOD_openssl(void)
41 {
42     return &dso_meth_dl;
43 }
44
45 /*
46  * For this DSO_METHOD, our meth_data STACK will contain; (i) the handle
47  * (shl_t) returned from shl_load(). NB: I checked on HPUX11 and shl_t is
48  * itself a pointer type so the cast is safe.
49  */
50
51 static int dl_load(DSO *dso)
52 {
53     shl_t ptr = NULL;
54     /*
55      * We don't do any fancy retries or anything, just take the method's (or
56      * DSO's if it has the callback set) best translation of the
57      * platform-independent filename and try once with that.
58      */
59     char *filename = DSO_convert_filename(dso, NULL);
60
61     if (filename == NULL) {
62         DSOerr(DSO_F_DL_LOAD, DSO_R_NO_FILENAME);
63         goto err;
64     }
65     ptr = shl_load(filename, BIND_IMMEDIATE |
66                    (dso->flags & DSO_FLAG_NO_NAME_TRANSLATION ? 0 :
67                     DYNAMIC_PATH), 0L);
68     if (ptr == NULL) {
69         DSOerr(DSO_F_DL_LOAD, DSO_R_LOAD_FAILED);
70         ERR_add_error_data(4, "filename(", filename, "): ", strerror(errno));
71         goto err;
72     }
73     if (!sk_push(dso->meth_data, (char *)ptr)) {
74         DSOerr(DSO_F_DL_LOAD, DSO_R_STACK_ERROR);
75         goto err;
76     }
77     /*
78      * Success, stick the converted filename we've loaded under into the DSO
79      * (it also serves as the indicator that we are currently loaded).
80      */
81     dso->loaded_filename = filename;
82     return (1);
83  err:
84     /* Cleanup! */
85     OPENSSL_free(filename);
86     if (ptr != NULL)
87         shl_unload(ptr);
88     return (0);
89 }
90
91 static int dl_unload(DSO *dso)
92 {
93     shl_t ptr;
94     if (dso == NULL) {
95         DSOerr(DSO_F_DL_UNLOAD, ERR_R_PASSED_NULL_PARAMETER);
96         return (0);
97     }
98     if (sk_num(dso->meth_data) < 1)
99         return (1);
100     /* Is this statement legal? */
101     ptr = (shl_t) sk_pop(dso->meth_data);
102     if (ptr == NULL) {
103         DSOerr(DSO_F_DL_UNLOAD, DSO_R_NULL_HANDLE);
104         /*
105          * Should push the value back onto the stack in case of a retry.
106          */
107         sk_push(dso->meth_data, (char *)ptr);
108         return (0);
109     }
110     shl_unload(ptr);
111     return (1);
112 }
113
114 static DSO_FUNC_TYPE dl_bind_func(DSO *dso, const char *symname)
115 {
116     shl_t ptr;
117     void *sym;
118
119     if ((dso == NULL) || (symname == NULL)) {
120         DSOerr(DSO_F_DL_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
121         return (NULL);
122     }
123     if (sk_num(dso->meth_data) < 1) {
124         DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_STACK_ERROR);
125         return (NULL);
126     }
127     ptr = (shl_t) sk_value(dso->meth_data, sk_num(dso->meth_data) - 1);
128     if (ptr == NULL) {
129         DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_NULL_HANDLE);
130         return (NULL);
131     }
132     if (shl_findsym(&ptr, symname, TYPE_UNDEFINED, &sym) < 0) {
133         DSOerr(DSO_F_DL_BIND_FUNC, DSO_R_SYM_FAILURE);
134         ERR_add_error_data(4, "symname(", symname, "): ", strerror(errno));
135         return (NULL);
136     }
137     return ((DSO_FUNC_TYPE)sym);
138 }
139
140 static char *dl_merger(DSO *dso, const char *filespec1, const char *filespec2)
141 {
142     char *merged;
143
144     if (!filespec1 && !filespec2) {
145         DSOerr(DSO_F_DL_MERGER, ERR_R_PASSED_NULL_PARAMETER);
146         return (NULL);
147     }
148     /*
149      * If the first file specification is a rooted path, it rules. same goes
150      * if the second file specification is missing.
151      */
152     if (!filespec2 || filespec1[0] == '/') {
153         merged = OPENSSL_strdup(filespec1);
154         if (merged == NULL) {
155             DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
156             return (NULL);
157         }
158     }
159     /*
160      * If the first file specification is missing, the second one rules.
161      */
162     else if (!filespec1) {
163         merged = OPENSSL_strdup(filespec2);
164         if (merged == NULL) {
165             DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
166             return (NULL);
167         }
168     } else
169         /*
170          * This part isn't as trivial as it looks.  It assumes that the
171          * second file specification really is a directory, and makes no
172          * checks whatsoever.  Therefore, the result becomes the
173          * concatenation of filespec2 followed by a slash followed by
174          * filespec1.
175          */
176     {
177         int spec2len, len;
178
179         spec2len = (filespec2 ? strlen(filespec2) : 0);
180         len = spec2len + (filespec1 ? strlen(filespec1) : 0);
181
182         if (spec2len && filespec2[spec2len - 1] == '/') {
183             spec2len--;
184             len--;
185         }
186         merged = OPENSSL_malloc(len + 2);
187         if (merged == NULL) {
188             DSOerr(DSO_F_DL_MERGER, ERR_R_MALLOC_FAILURE);
189             return (NULL);
190         }
191         strcpy(merged, filespec2);
192         merged[spec2len] = '/';
193         strcpy(&merged[spec2len + 1], filespec1);
194     }
195     return (merged);
196 }
197
198 /*
199  * This function is identical to the one in dso_dlfcn.c, but as it is highly
200  * unlikely that both the "dl" *and* "dlfcn" variants are being compiled at
201  * the same time, there's no great duplicating the code. Figuring out an
202  * elegant way to share one copy of the code would be more difficult and
203  * would not leave the implementations independent.
204  */
205 static char *dl_name_converter(DSO *dso, const char *filename)
206 {
207     char *translated;
208     int len, rsize, transform;
209
210     len = strlen(filename);
211     rsize = len + 1;
212     transform = (strstr(filename, "/") == NULL);
213     {
214         /* We will convert this to "%s.s?" or "lib%s.s?" */
215         rsize += strlen(DSO_EXTENSION); /* The length of ".s?" */
216         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
217             rsize += 3;         /* The length of "lib" */
218     }
219     translated = OPENSSL_malloc(rsize);
220     if (translated == NULL) {
221         DSOerr(DSO_F_DL_NAME_CONVERTER, DSO_R_NAME_TRANSLATION_FAILED);
222         return (NULL);
223     }
224     if (transform) {
225         if ((DSO_flags(dso) & DSO_FLAG_NAME_TRANSLATION_EXT_ONLY) == 0)
226             sprintf(translated, "lib%s%s", filename, DSO_EXTENSION);
227         else
228             sprintf(translated, "%s%s", filename, DSO_EXTENSION);
229     } else
230         sprintf(translated, "%s", filename);
231     return (translated);
232 }
233
234 static void *dl_globallookup(const char *name)
235 {
236     void *ret;
237     shl_t h = NULL;
238
239     return shl_findsym(&h, name, TYPE_UNDEFINED, &ret) ? NULL : ret;
240 }
241 #endif                          /* DSO_DL */