xref: /openssl/crypto/objects/o_names.c (revision e077455e)
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 "internal/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  */
28 static LHASH_OF(OBJ_NAME) *names_lh = NULL;
29 static int names_type_num = OBJ_NAME_TYPE_NUM;
30 static CRYPTO_RWLOCK *obj_lock = NULL;
31 
32 struct 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 
38 static 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 
46 static unsigned long obj_name_hash(const OBJ_NAME *a);
47 static int obj_name_cmp(const OBJ_NAME *a, const OBJ_NAME *b);
48 
49 static CRYPTO_ONCE init = CRYPTO_ONCE_STATIC_INIT;
DEFINE_RUN_ONCE_STATIC(o_names_init)50 DEFINE_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 
OBJ_NAME_init(void)63 int OBJ_NAME_init(void)
64 {
65     return RUN_ONCE(&init, o_names_init);
66 }
67 
OBJ_NAME_new_index(unsigned long (* hash_func)(const char *),int (* cmp_func)(const char *,const char *),void (* free_func)(const char *,int,const char *))68 int 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             ret = 0;
93             goto out;
94         }
95         name_funcs->hash_func = ossl_lh_strcasehash;
96         name_funcs->cmp_func = OPENSSL_strcasecmp;
97         push = sk_NAME_FUNCS_push(name_funcs_stack, name_funcs);
98 
99         if (!push) {
100             ERR_raise(ERR_LIB_OBJ, ERR_R_CRYPTO_LIB);
101             OPENSSL_free(name_funcs);
102             ret = 0;
103             goto out;
104         }
105     }
106     name_funcs = sk_NAME_FUNCS_value(name_funcs_stack, ret);
107     if (hash_func != NULL)
108         name_funcs->hash_func = hash_func;
109     if (cmp_func != NULL)
110         name_funcs->cmp_func = cmp_func;
111     if (free_func != NULL)
112         name_funcs->free_func = free_func;
113 
114 out:
115     CRYPTO_THREAD_unlock(obj_lock);
116     return ret;
117 }
118 
obj_name_cmp(const OBJ_NAME * a,const OBJ_NAME * b)119 static int obj_name_cmp(const OBJ_NAME *a, const OBJ_NAME *b)
120 {
121     int ret;
122 
123     ret = a->type - b->type;
124     if (ret == 0) {
125         if ((name_funcs_stack != NULL)
126             && (sk_NAME_FUNCS_num(name_funcs_stack) > a->type)) {
127             ret = sk_NAME_FUNCS_value(name_funcs_stack,
128                                       a->type)->cmp_func(a->name, b->name);
129         } else
130             ret = OPENSSL_strcasecmp(a->name, b->name);
131     }
132     return ret;
133 }
134 
obj_name_hash(const OBJ_NAME * a)135 static unsigned long obj_name_hash(const OBJ_NAME *a)
136 {
137     unsigned long ret;
138 
139     if ((name_funcs_stack != NULL)
140         && (sk_NAME_FUNCS_num(name_funcs_stack) > a->type)) {
141         ret =
142             sk_NAME_FUNCS_value(name_funcs_stack,
143                                 a->type)->hash_func(a->name);
144     } else {
145         ret = ossl_lh_strcasehash(a->name);
146     }
147     ret ^= a->type;
148     return ret;
149 }
150 
OBJ_NAME_get(const char * name,int type)151 const char *OBJ_NAME_get(const char *name, int type)
152 {
153     OBJ_NAME on, *ret;
154     int num = 0, alias;
155     const char *value = NULL;
156 
157     if (name == NULL)
158         return NULL;
159     if (!OBJ_NAME_init())
160         return NULL;
161     if (!CRYPTO_THREAD_read_lock(obj_lock))
162         return NULL;
163 
164     alias = type & OBJ_NAME_ALIAS;
165     type &= ~OBJ_NAME_ALIAS;
166 
167     on.name = name;
168     on.type = type;
169 
170     for (;;) {
171         ret = lh_OBJ_NAME_retrieve(names_lh, &on);
172         if (ret == NULL)
173             break;
174         if ((ret->alias) && !alias) {
175             if (++num > 10)
176                 break;
177             on.name = ret->data;
178         } else {
179             value = ret->data;
180             break;
181         }
182     }
183 
184     CRYPTO_THREAD_unlock(obj_lock);
185     return value;
186 }
187 
OBJ_NAME_add(const char * name,int type,const char * data)188 int OBJ_NAME_add(const char *name, int type, const char *data)
189 {
190     OBJ_NAME *onp, *ret;
191     int alias, ok = 0;
192 
193     if (!OBJ_NAME_init())
194         return 0;
195 
196     alias = type & OBJ_NAME_ALIAS;
197     type &= ~OBJ_NAME_ALIAS;
198 
199     onp = OPENSSL_malloc(sizeof(*onp));
200     if (onp == NULL)
201         return 0;
202 
203     onp->name = name;
204     onp->alias = alias;
205     onp->type = type;
206     onp->data = data;
207 
208     if (!CRYPTO_THREAD_write_lock(obj_lock)) {
209         OPENSSL_free(onp);
210         return 0;
211     }
212 
213     ret = lh_OBJ_NAME_insert(names_lh, onp);
214     if (ret != NULL) {
215         /* free things */
216         if ((name_funcs_stack != NULL)
217             && (sk_NAME_FUNCS_num(name_funcs_stack) > ret->type)) {
218             /*
219              * XXX: I'm not sure I understand why the free function should
220              * get three arguments... -- Richard Levitte
221              */
222             sk_NAME_FUNCS_value(name_funcs_stack,
223                                 ret->type)->free_func(ret->name, ret->type,
224                                                       ret->data);
225         }
226         OPENSSL_free(ret);
227     } else {
228         if (lh_OBJ_NAME_error(names_lh)) {
229             /* ERROR */
230             OPENSSL_free(onp);
231             goto unlock;
232         }
233     }
234 
235     ok = 1;
236 
237 unlock:
238     CRYPTO_THREAD_unlock(obj_lock);
239     return ok;
240 }
241 
OBJ_NAME_remove(const char * name,int type)242 int OBJ_NAME_remove(const char *name, int type)
243 {
244     OBJ_NAME on, *ret;
245     int ok = 0;
246 
247     if (!OBJ_NAME_init())
248         return 0;
249 
250     if (!CRYPTO_THREAD_write_lock(obj_lock))
251         return 0;
252 
253     type &= ~OBJ_NAME_ALIAS;
254     on.name = name;
255     on.type = type;
256     ret = lh_OBJ_NAME_delete(names_lh, &on);
257     if (ret != NULL) {
258         /* free things */
259         if ((name_funcs_stack != NULL)
260             && (sk_NAME_FUNCS_num(name_funcs_stack) > ret->type)) {
261             /*
262              * XXX: I'm not sure I understand why the free function should
263              * get three arguments... -- Richard Levitte
264              */
265             sk_NAME_FUNCS_value(name_funcs_stack,
266                                 ret->type)->free_func(ret->name, ret->type,
267                                                       ret->data);
268         }
269         OPENSSL_free(ret);
270         ok = 1;
271     }
272 
273     CRYPTO_THREAD_unlock(obj_lock);
274     return ok;
275 }
276 
277 typedef struct {
278     int type;
279     void (*fn) (const OBJ_NAME *, void *arg);
280     void *arg;
281 } OBJ_DOALL;
282 
do_all_fn(const OBJ_NAME * name,OBJ_DOALL * d)283 static void do_all_fn(const OBJ_NAME *name, OBJ_DOALL *d)
284 {
285     if (name->type == d->type)
286         d->fn(name, d->arg);
287 }
288 
289 IMPLEMENT_LHASH_DOALL_ARG_CONST(OBJ_NAME, OBJ_DOALL);
290 
OBJ_NAME_do_all(int type,void (* fn)(const OBJ_NAME *,void * arg),void * arg)291 void OBJ_NAME_do_all(int type, void (*fn) (const OBJ_NAME *, void *arg),
292                      void *arg)
293 {
294     OBJ_DOALL d;
295 
296     d.type = type;
297     d.fn = fn;
298     d.arg = arg;
299 
300     lh_OBJ_NAME_doall_OBJ_DOALL(names_lh, do_all_fn, &d);
301 }
302 
303 struct doall_sorted {
304     int type;
305     int n;
306     const OBJ_NAME **names;
307 };
308 
do_all_sorted_fn(const OBJ_NAME * name,void * d_)309 static void do_all_sorted_fn(const OBJ_NAME *name, void *d_)
310 {
311     struct doall_sorted *d = d_;
312 
313     if (name->type != d->type)
314         return;
315 
316     d->names[d->n++] = name;
317 }
318 
do_all_sorted_cmp(const void * n1_,const void * n2_)319 static int do_all_sorted_cmp(const void *n1_, const void *n2_)
320 {
321     const OBJ_NAME *const *n1 = n1_;
322     const OBJ_NAME *const *n2 = n2_;
323 
324     return strcmp((*n1)->name, (*n2)->name);
325 }
326 
OBJ_NAME_do_all_sorted(int type,void (* fn)(const OBJ_NAME *,void * arg),void * arg)327 void OBJ_NAME_do_all_sorted(int type,
328                             void (*fn) (const OBJ_NAME *, void *arg),
329                             void *arg)
330 {
331     struct doall_sorted d;
332     int n;
333 
334     d.type = type;
335     d.names =
336         OPENSSL_malloc(sizeof(*d.names) * lh_OBJ_NAME_num_items(names_lh));
337     /* Really should return an error if !d.names...but its a void function! */
338     if (d.names != NULL) {
339         d.n = 0;
340         OBJ_NAME_do_all(type, do_all_sorted_fn, &d);
341 
342         qsort((void *)d.names, d.n, sizeof(*d.names), do_all_sorted_cmp);
343 
344         for (n = 0; n < d.n; ++n)
345             fn(d.names[n], arg);
346 
347         OPENSSL_free((void *)d.names);
348     }
349 }
350 
351 static int free_type;
352 
names_lh_free_doall(OBJ_NAME * onp)353 static void names_lh_free_doall(OBJ_NAME *onp)
354 {
355     if (onp == NULL)
356         return;
357 
358     if (free_type < 0 || free_type == onp->type)
359         OBJ_NAME_remove(onp->name, onp->type);
360 }
361 
name_funcs_free(NAME_FUNCS * ptr)362 static void name_funcs_free(NAME_FUNCS *ptr)
363 {
364     OPENSSL_free(ptr);
365 }
366 
OBJ_NAME_cleanup(int type)367 void OBJ_NAME_cleanup(int type)
368 {
369     unsigned long down_load;
370 
371     if (names_lh == NULL)
372         return;
373 
374     free_type = type;
375     down_load = lh_OBJ_NAME_get_down_load(names_lh);
376     lh_OBJ_NAME_set_down_load(names_lh, 0);
377 
378     lh_OBJ_NAME_doall(names_lh, names_lh_free_doall);
379     if (type < 0) {
380         lh_OBJ_NAME_free(names_lh);
381         sk_NAME_FUNCS_pop_free(name_funcs_stack, name_funcs_free);
382         CRYPTO_THREAD_lock_free(obj_lock);
383         names_lh = NULL;
384         name_funcs_stack = NULL;
385         obj_lock = NULL;
386     } else
387         lh_OBJ_NAME_set_down_load(names_lh, down_load);
388 }
389