1/*
2 * Copyright 2000-2017 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_local.h"
11#include "internal/refcount.h"
12
13static DSO_METHOD *default_DSO_meth = NULL;
14
15static DSO *DSO_new_method(DSO_METHOD *meth)
16{
17    DSO *ret;
18
19    if (default_DSO_meth == NULL) {
20        /*
21         * We default to DSO_METH_openssl() which in turn defaults to
22         * stealing the "best available" method. Will fallback to
23         * DSO_METH_null() in the worst case.
24         */
25        default_DSO_meth = DSO_METHOD_openssl();
26    }
27    ret = OPENSSL_zalloc(sizeof(*ret));
28    if (ret == NULL) {
29        DSOerr(DSO_F_DSO_NEW_METHOD, ERR_R_MALLOC_FAILURE);
30        return NULL;
31    }
32    ret->meth_data = sk_void_new_null();
33    if (ret->meth_data == NULL) {
34        /* sk_new doesn't generate any errors so we do */
35        DSOerr(DSO_F_DSO_NEW_METHOD, ERR_R_MALLOC_FAILURE);
36        OPENSSL_free(ret);
37        return NULL;
38    }
39    ret->meth = default_DSO_meth;
40    ret->references = 1;
41    ret->lock = CRYPTO_THREAD_lock_new();
42    if (ret->lock == NULL) {
43        DSOerr(DSO_F_DSO_NEW_METHOD, ERR_R_MALLOC_FAILURE);
44        sk_void_free(ret->meth_data);
45        OPENSSL_free(ret);
46        return NULL;
47    }
48
49    if ((ret->meth->init != NULL) && !ret->meth->init(ret)) {
50        DSO_free(ret);
51        ret = NULL;
52    }
53
54    return ret;
55}
56
57DSO *DSO_new(void)
58{
59    return DSO_new_method(NULL);
60}
61
62int DSO_free(DSO *dso)
63{
64    int i;
65
66    if (dso == NULL)
67        return 1;
68
69    if (CRYPTO_DOWN_REF(&dso->references, &i, dso->lock) <= 0)
70        return 0;
71
72    REF_PRINT_COUNT("DSO", dso);
73    if (i > 0)
74        return 1;
75    REF_ASSERT_ISNT(i < 0);
76
77    if ((dso->flags & DSO_FLAG_NO_UNLOAD_ON_FREE) == 0) {
78        if ((dso->meth->dso_unload != NULL) && !dso->meth->dso_unload(dso)) {
79            DSOerr(DSO_F_DSO_FREE, DSO_R_UNLOAD_FAILED);
80            return 0;
81        }
82    }
83
84    if ((dso->meth->finish != NULL) && !dso->meth->finish(dso)) {
85        DSOerr(DSO_F_DSO_FREE, DSO_R_FINISH_FAILED);
86        return 0;
87    }
88
89    sk_void_free(dso->meth_data);
90    OPENSSL_free(dso->filename);
91    OPENSSL_free(dso->loaded_filename);
92    CRYPTO_THREAD_lock_free(dso->lock);
93    OPENSSL_free(dso);
94    return 1;
95}
96
97int DSO_flags(DSO *dso)
98{
99    return ((dso == NULL) ? 0 : dso->flags);
100}
101
102int DSO_up_ref(DSO *dso)
103{
104    int i;
105
106    if (dso == NULL) {
107        DSOerr(DSO_F_DSO_UP_REF, ERR_R_PASSED_NULL_PARAMETER);
108        return 0;
109    }
110
111    if (CRYPTO_UP_REF(&dso->references, &i, dso->lock) <= 0)
112        return 0;
113
114    REF_PRINT_COUNT("DSO", r);
115    REF_ASSERT_ISNT(i < 2);
116    return ((i > 1) ? 1 : 0);
117}
118
119DSO *DSO_load(DSO *dso, const char *filename, DSO_METHOD *meth, int flags)
120{
121    DSO *ret;
122    int allocated = 0;
123
124    if (dso == NULL) {
125        ret = DSO_new_method(meth);
126        if (ret == NULL) {
127            DSOerr(DSO_F_DSO_LOAD, ERR_R_MALLOC_FAILURE);
128            goto err;
129        }
130        allocated = 1;
131        /* Pass the provided flags to the new DSO object */
132        if (DSO_ctrl(ret, DSO_CTRL_SET_FLAGS, flags, NULL) < 0) {
133            DSOerr(DSO_F_DSO_LOAD, DSO_R_CTRL_FAILED);
134            goto err;
135        }
136    } else
137        ret = dso;
138    /* Don't load if we're currently already loaded */
139    if (ret->filename != NULL) {
140        DSOerr(DSO_F_DSO_LOAD, DSO_R_DSO_ALREADY_LOADED);
141        goto err;
142    }
143    /*
144     * filename can only be NULL if we were passed a dso that already has one
145     * set.
146     */
147    if (filename != NULL)
148        if (!DSO_set_filename(ret, filename)) {
149            DSOerr(DSO_F_DSO_LOAD, DSO_R_SET_FILENAME_FAILED);
150            goto err;
151        }
152    filename = ret->filename;
153    if (filename == NULL) {
154        DSOerr(DSO_F_DSO_LOAD, DSO_R_NO_FILENAME);
155        goto err;
156    }
157    if (ret->meth->dso_load == NULL) {
158        DSOerr(DSO_F_DSO_LOAD, DSO_R_UNSUPPORTED);
159        goto err;
160    }
161    if (!ret->meth->dso_load(ret)) {
162        DSOerr(DSO_F_DSO_LOAD, DSO_R_LOAD_FAILED);
163        goto err;
164    }
165    /* Load succeeded */
166    return ret;
167 err:
168    if (allocated)
169        DSO_free(ret);
170    return NULL;
171}
172
173DSO_FUNC_TYPE DSO_bind_func(DSO *dso, const char *symname)
174{
175    DSO_FUNC_TYPE ret = NULL;
176
177    if ((dso == NULL) || (symname == NULL)) {
178        DSOerr(DSO_F_DSO_BIND_FUNC, ERR_R_PASSED_NULL_PARAMETER);
179        return NULL;
180    }
181    if (dso->meth->dso_bind_func == NULL) {
182        DSOerr(DSO_F_DSO_BIND_FUNC, DSO_R_UNSUPPORTED);
183        return NULL;
184    }
185    if ((ret = dso->meth->dso_bind_func(dso, symname)) == NULL) {
186        DSOerr(DSO_F_DSO_BIND_FUNC, DSO_R_SYM_FAILURE);
187        return NULL;
188    }
189    /* Success */
190    return ret;
191}
192
193/*
194 * I don't really like these *_ctrl functions very much to be perfectly
195 * honest. For one thing, I think I have to return a negative value for any
196 * error because possible DSO_ctrl() commands may return values such as
197 * "size"s that can legitimately be zero (making the standard
198 * "if (DSO_cmd(...))" form that works almost everywhere else fail at odd
199 * times. I'd prefer "output" values to be passed by reference and the return
200 * value as success/failure like usual ... but we conform when we must... :-)
201 */
202long DSO_ctrl(DSO *dso, int cmd, long larg, void *parg)
203{
204    if (dso == NULL) {
205        DSOerr(DSO_F_DSO_CTRL, ERR_R_PASSED_NULL_PARAMETER);
206        return -1;
207    }
208    /*
209     * We should intercept certain generic commands and only pass control to
210     * the method-specific ctrl() function if it's something we don't handle.
211     */
212    switch (cmd) {
213    case DSO_CTRL_GET_FLAGS:
214        return dso->flags;
215    case DSO_CTRL_SET_FLAGS:
216        dso->flags = (int)larg;
217        return 0;
218    case DSO_CTRL_OR_FLAGS:
219        dso->flags |= (int)larg;
220        return 0;
221    default:
222        break;
223    }
224    if ((dso->meth == NULL) || (dso->meth->dso_ctrl == NULL)) {
225        DSOerr(DSO_F_DSO_CTRL, DSO_R_UNSUPPORTED);
226        return -1;
227    }
228    return dso->meth->dso_ctrl(dso, cmd, larg, parg);
229}
230
231const char *DSO_get_filename(DSO *dso)
232{
233    if (dso == NULL) {
234        DSOerr(DSO_F_DSO_GET_FILENAME, ERR_R_PASSED_NULL_PARAMETER);
235        return NULL;
236    }
237    return dso->filename;
238}
239
240int DSO_set_filename(DSO *dso, const char *filename)
241{
242    char *copied;
243
244    if ((dso == NULL) || (filename == NULL)) {
245        DSOerr(DSO_F_DSO_SET_FILENAME, ERR_R_PASSED_NULL_PARAMETER);
246        return 0;
247    }
248    if (dso->loaded_filename) {
249        DSOerr(DSO_F_DSO_SET_FILENAME, DSO_R_DSO_ALREADY_LOADED);
250        return 0;
251    }
252    /* We'll duplicate filename */
253    copied = OPENSSL_strdup(filename);
254    if (copied == NULL) {
255        DSOerr(DSO_F_DSO_SET_FILENAME, ERR_R_MALLOC_FAILURE);
256        return 0;
257    }
258    OPENSSL_free(dso->filename);
259    dso->filename = copied;
260    return 1;
261}
262
263char *DSO_merge(DSO *dso, const char *filespec1, const char *filespec2)
264{
265    char *result = NULL;
266
267    if (dso == NULL || filespec1 == NULL) {
268        DSOerr(DSO_F_DSO_MERGE, ERR_R_PASSED_NULL_PARAMETER);
269        return NULL;
270    }
271    if ((dso->flags & DSO_FLAG_NO_NAME_TRANSLATION) == 0) {
272        if (dso->merger != NULL)
273            result = dso->merger(dso, filespec1, filespec2);
274        else if (dso->meth->dso_merger != NULL)
275            result = dso->meth->dso_merger(dso, filespec1, filespec2);
276    }
277    return result;
278}
279
280char *DSO_convert_filename(DSO *dso, const char *filename)
281{
282    char *result = NULL;
283
284    if (dso == NULL) {
285        DSOerr(DSO_F_DSO_CONVERT_FILENAME, ERR_R_PASSED_NULL_PARAMETER);
286        return NULL;
287    }
288    if (filename == NULL)
289        filename = dso->filename;
290    if (filename == NULL) {
291        DSOerr(DSO_F_DSO_CONVERT_FILENAME, DSO_R_NO_FILENAME);
292        return NULL;
293    }
294    if ((dso->flags & DSO_FLAG_NO_NAME_TRANSLATION) == 0) {
295        if (dso->name_converter != NULL)
296            result = dso->name_converter(dso, filename);
297        else if (dso->meth->dso_name_converter != NULL)
298            result = dso->meth->dso_name_converter(dso, filename);
299    }
300    if (result == NULL) {
301        result = OPENSSL_strdup(filename);
302        if (result == NULL) {
303            DSOerr(DSO_F_DSO_CONVERT_FILENAME, ERR_R_MALLOC_FAILURE);
304            return NULL;
305        }
306    }
307    return result;
308}
309
310int DSO_pathbyaddr(void *addr, char *path, int sz)
311{
312    DSO_METHOD *meth = default_DSO_meth;
313    if (meth == NULL)
314        meth = DSO_METHOD_openssl();
315    if (meth->pathbyaddr == NULL) {
316        DSOerr(DSO_F_DSO_PATHBYADDR, DSO_R_UNSUPPORTED);
317        return -1;
318    }
319    return (*meth->pathbyaddr) (addr, path, sz);
320}
321
322DSO *DSO_dsobyaddr(void *addr, int flags)
323{
324    DSO *ret = NULL;
325    char *filename = NULL;
326    int len = DSO_pathbyaddr(addr, NULL, 0);
327
328    if (len < 0)
329        return NULL;
330
331    filename = OPENSSL_malloc(len);
332    if (filename != NULL
333            && DSO_pathbyaddr(addr, filename, len) == len)
334        ret = DSO_load(NULL, filename, NULL, flags);
335
336    OPENSSL_free(filename);
337    return ret;
338}
339
340void *DSO_global_lookup(const char *name)
341{
342    DSO_METHOD *meth = default_DSO_meth;
343    if (meth == NULL)
344        meth = DSO_METHOD_openssl();
345    if (meth->globallookup == NULL) {
346        DSOerr(DSO_F_DSO_GLOBAL_LOOKUP, DSO_R_UNSUPPORTED);
347        return NULL;
348    }
349    return (*meth->globallookup) (name);
350}
351