1/*
2 * Copyright 1998-2022 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (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 <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include <openssl/err.h>
15#include <openssl/lhash.h>
16#include <openssl/objects.h>
17#include <openssl/safestack.h>
18#include <openssl/e_os2.h>
19#include "internal/thread_once.h"
20#include "crypto/lhash.h"
21#include "obj_local.h"
22#include "e_os.h"
23
24/*
25 * I use the ex_data stuff to manage the identifiers for the obj_name_types
26 * that applications may define.  I only really use the free function field.
27 */
28static LHASH_OF(OBJ_NAME) *names_lh = NULL;
29static int names_type_num = OBJ_NAME_TYPE_NUM;
30static CRYPTO_RWLOCK *obj_lock = NULL;
31
32struct name_funcs_st {
33    unsigned long (*hash_func) (const char *name);
34    int (*cmp_func) (const char *a, const char *b);
35    void (*free_func) (const char *, int, const char *);
36};
37
38static STACK_OF(NAME_FUNCS) *name_funcs_stack;
39
40/*
41 * The LHASH callbacks now use the raw "void *" prototypes and do
42 * per-variable casting in the functions. This prevents function pointer
43 * casting without the need for macro-generated wrapper functions.
44 */
45
46static unsigned long obj_name_hash(const OBJ_NAME *a);
47static int obj_name_cmp(const OBJ_NAME *a, const OBJ_NAME *b);
48
49static CRYPTO_ONCE init = CRYPTO_ONCE_STATIC_INIT;
50DEFINE_RUN_ONCE_STATIC(o_names_init)
51{
52    names_lh = NULL;
53    obj_lock = CRYPTO_THREAD_lock_new();
54    if (obj_lock != NULL)
55        names_lh = lh_OBJ_NAME_new(obj_name_hash, obj_name_cmp);
56    if (names_lh == NULL) {
57        CRYPTO_THREAD_lock_free(obj_lock);
58        obj_lock = NULL;
59    }
60    return names_lh != NULL && obj_lock != NULL;
61}
62
63int OBJ_NAME_init(void)
64{
65    return RUN_ONCE(&init, o_names_init);
66}
67
68int OBJ_NAME_new_index(unsigned long (*hash_func) (const char *),
69                       int (*cmp_func) (const char *, const char *),
70                       void (*free_func) (const char *, int, const char *))
71{
72    int ret = 0, i, push;
73    NAME_FUNCS *name_funcs;
74
75    if (!OBJ_NAME_init())
76        return 0;
77
78    if (!CRYPTO_THREAD_write_lock(obj_lock))
79        return 0;
80
81    if (name_funcs_stack == NULL)
82        name_funcs_stack = sk_NAME_FUNCS_new_null();
83    if (name_funcs_stack == NULL) {
84        /* ERROR */
85        goto out;
86    }
87    ret = names_type_num;
88    names_type_num++;
89    for (i = sk_NAME_FUNCS_num(name_funcs_stack); i < names_type_num; i++) {
90        name_funcs = OPENSSL_zalloc(sizeof(*name_funcs));
91        if (name_funcs == NULL) {
92            ERR_raise(ERR_LIB_OBJ, ERR_R_MALLOC_FAILURE);
93            ret = 0;
94            goto out;
95        }
96        name_funcs->hash_func = ossl_lh_strcasehash;
97        name_funcs->cmp_func = OPENSSL_strcasecmp;
98        push = sk_NAME_FUNCS_push(name_funcs_stack, name_funcs);
99
100        if (!push) {
101            ERR_raise(ERR_LIB_OBJ, ERR_R_MALLOC_FAILURE);
102            OPENSSL_free(name_funcs);
103            ret = 0;
104            goto out;
105        }
106    }
107    name_funcs = sk_NAME_FUNCS_value(name_funcs_stack, ret);
108    if (hash_func != NULL)
109        name_funcs->hash_func = hash_func;
110    if (cmp_func != NULL)
111        name_funcs->cmp_func = cmp_func;
112    if (free_func != NULL)
113        name_funcs->free_func = free_func;
114
115out:
116    CRYPTO_THREAD_unlock(obj_lock);
117    return ret;
118}
119
120static int obj_name_cmp(const OBJ_NAME *a, const OBJ_NAME *b)
121{
122    int ret;
123
124    ret = a->type - b->type;
125    if (ret == 0) {
126        if ((name_funcs_stack != NULL)
127            && (sk_NAME_FUNCS_num(name_funcs_stack) > a->type)) {
128            ret = sk_NAME_FUNCS_value(name_funcs_stack,
129                                      a->type)->cmp_func(a->name, b->name);
130        } else
131            ret = OPENSSL_strcasecmp(a->name, b->name);
132    }
133    return ret;
134}
135
136static unsigned long obj_name_hash(const OBJ_NAME *a)
137{
138    unsigned long ret;
139
140    if ((name_funcs_stack != NULL)
141        && (sk_NAME_FUNCS_num(name_funcs_stack) > a->type)) {
142        ret =
143            sk_NAME_FUNCS_value(name_funcs_stack,
144                                a->type)->hash_func(a->name);
145    } else {
146        ret = ossl_lh_strcasehash(a->name);
147    }
148    ret ^= a->type;
149    return ret;
150}
151
152const char *OBJ_NAME_get(const char *name, int type)
153{
154    OBJ_NAME on, *ret;
155    int num = 0, alias;
156    const char *value = NULL;
157
158    if (name == NULL)
159        return NULL;
160    if (!OBJ_NAME_init())
161        return NULL;
162    if (!CRYPTO_THREAD_read_lock(obj_lock))
163        return NULL;
164
165    alias = type & OBJ_NAME_ALIAS;
166    type &= ~OBJ_NAME_ALIAS;
167
168    on.name = name;
169    on.type = type;
170
171    for (;;) {
172        ret = lh_OBJ_NAME_retrieve(names_lh, &on);
173        if (ret == NULL)
174            break;
175        if ((ret->alias) && !alias) {
176            if (++num > 10)
177                break;
178            on.name = ret->data;
179        } else {
180            value = ret->data;
181            break;
182        }
183    }
184
185    CRYPTO_THREAD_unlock(obj_lock);
186    return value;
187}
188
189int OBJ_NAME_add(const char *name, int type, const char *data)
190{
191    OBJ_NAME *onp, *ret;
192    int alias, ok = 0;
193
194    if (!OBJ_NAME_init())
195        return 0;
196
197    alias = type & OBJ_NAME_ALIAS;
198    type &= ~OBJ_NAME_ALIAS;
199
200    onp = OPENSSL_malloc(sizeof(*onp));
201    if (onp == NULL)
202        return 0;
203
204    onp->name = name;
205    onp->alias = alias;
206    onp->type = type;
207    onp->data = data;
208
209    if (!CRYPTO_THREAD_write_lock(obj_lock)) {
210        OPENSSL_free(onp);
211        return 0;
212    }
213
214    ret = lh_OBJ_NAME_insert(names_lh, onp);
215    if (ret != NULL) {
216        /* free things */
217        if ((name_funcs_stack != NULL)
218            && (sk_NAME_FUNCS_num(name_funcs_stack) > ret->type)) {
219            /*
220             * XXX: I'm not sure I understand why the free function should
221             * get three arguments... -- Richard Levitte
222             */
223            sk_NAME_FUNCS_value(name_funcs_stack,
224                                ret->type)->free_func(ret->name, ret->type,
225                                                      ret->data);
226        }
227        OPENSSL_free(ret);
228    } else {
229        if (lh_OBJ_NAME_error(names_lh)) {
230            /* ERROR */
231            OPENSSL_free(onp);
232            goto unlock;
233        }
234    }
235
236    ok = 1;
237
238unlock:
239    CRYPTO_THREAD_unlock(obj_lock);
240    return ok;
241}
242
243int OBJ_NAME_remove(const char *name, int type)
244{
245    OBJ_NAME on, *ret;
246    int ok = 0;
247
248    if (!OBJ_NAME_init())
249        return 0;
250
251    if (!CRYPTO_THREAD_write_lock(obj_lock))
252        return 0;
253
254    type &= ~OBJ_NAME_ALIAS;
255    on.name = name;
256    on.type = type;
257    ret = lh_OBJ_NAME_delete(names_lh, &on);
258    if (ret != NULL) {
259        /* free things */
260        if ((name_funcs_stack != NULL)
261            && (sk_NAME_FUNCS_num(name_funcs_stack) > ret->type)) {
262            /*
263             * XXX: I'm not sure I understand why the free function should
264             * get three arguments... -- Richard Levitte
265             */
266            sk_NAME_FUNCS_value(name_funcs_stack,
267                                ret->type)->free_func(ret->name, ret->type,
268                                                      ret->data);
269        }
270        OPENSSL_free(ret);
271        ok = 1;
272    }
273
274    CRYPTO_THREAD_unlock(obj_lock);
275    return ok;
276}
277
278typedef struct {
279    int type;
280    void (*fn) (const OBJ_NAME *, void *arg);
281    void *arg;
282} OBJ_DOALL;
283
284static void do_all_fn(const OBJ_NAME *name, OBJ_DOALL *d)
285{
286    if (name->type == d->type)
287        d->fn(name, d->arg);
288}
289
290IMPLEMENT_LHASH_DOALL_ARG_CONST(OBJ_NAME, OBJ_DOALL);
291
292void OBJ_NAME_do_all(int type, void (*fn) (const OBJ_NAME *, void *arg),
293                     void *arg)
294{
295    OBJ_DOALL d;
296
297    d.type = type;
298    d.fn = fn;
299    d.arg = arg;
300
301    lh_OBJ_NAME_doall_OBJ_DOALL(names_lh, do_all_fn, &d);
302}
303
304struct doall_sorted {
305    int type;
306    int n;
307    const OBJ_NAME **names;
308};
309
310static void do_all_sorted_fn(const OBJ_NAME *name, void *d_)
311{
312    struct doall_sorted *d = d_;
313
314    if (name->type != d->type)
315        return;
316
317    d->names[d->n++] = name;
318}
319
320static int do_all_sorted_cmp(const void *n1_, const void *n2_)
321{
322    const OBJ_NAME *const *n1 = n1_;
323    const OBJ_NAME *const *n2 = n2_;
324
325    return strcmp((*n1)->name, (*n2)->name);
326}
327
328void OBJ_NAME_do_all_sorted(int type,
329                            void (*fn) (const OBJ_NAME *, void *arg),
330                            void *arg)
331{
332    struct doall_sorted d;
333    int n;
334
335    d.type = type;
336    d.names =
337        OPENSSL_malloc(sizeof(*d.names) * lh_OBJ_NAME_num_items(names_lh));
338    /* Really should return an error if !d.names...but its a void function! */
339    if (d.names != NULL) {
340        d.n = 0;
341        OBJ_NAME_do_all(type, do_all_sorted_fn, &d);
342
343        qsort((void *)d.names, d.n, sizeof(*d.names), do_all_sorted_cmp);
344
345        for (n = 0; n < d.n; ++n)
346            fn(d.names[n], arg);
347
348        OPENSSL_free((void *)d.names);
349    }
350}
351
352static int free_type;
353
354static void names_lh_free_doall(OBJ_NAME *onp)
355{
356    if (onp == NULL)
357        return;
358
359    if (free_type < 0 || free_type == onp->type)
360        OBJ_NAME_remove(onp->name, onp->type);
361}
362
363static void name_funcs_free(NAME_FUNCS *ptr)
364{
365    OPENSSL_free(ptr);
366}
367
368void OBJ_NAME_cleanup(int type)
369{
370    unsigned long down_load;
371
372    if (names_lh == NULL)
373        return;
374
375    free_type = type;
376    down_load = lh_OBJ_NAME_get_down_load(names_lh);
377    lh_OBJ_NAME_set_down_load(names_lh, 0);
378
379    lh_OBJ_NAME_doall(names_lh, names_lh_free_doall);
380    if (type < 0) {
381        lh_OBJ_NAME_free(names_lh);
382        sk_NAME_FUNCS_pop_free(name_funcs_stack, name_funcs_free);
383        CRYPTO_THREAD_lock_free(obj_lock);
384        names_lh = NULL;
385        name_funcs_stack = NULL;
386        obj_lock = NULL;
387    } else
388        lh_OBJ_NAME_set_down_load(names_lh, down_load);
389}
390