xref: /freebsd/crypto/openssl/crypto/ex_data.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
1 /*
2  * Copyright 1995-2023 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 <stdlib.h>
11 #include "crypto/cryptlib.h"
12 #include "internal/thread_once.h"
13 
ossl_do_ex_data_init(OSSL_LIB_CTX * ctx)14 int ossl_do_ex_data_init(OSSL_LIB_CTX *ctx)
15 {
16     OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx);
17 
18     if (global == NULL)
19         return 0;
20 
21     global->ex_data_lock = CRYPTO_THREAD_lock_new();
22     return global->ex_data_lock != NULL;
23 }
24 
25 /*
26  * Return the EX_CALLBACKS from the |ex_data| array that corresponds to
27  * a given class.  On success, *holds the lock.*
28  * The |global| parameter is assumed to be non null (checked by the caller).
29  * If |read| is 1 then a read lock is obtained. Otherwise it is a write lock.
30  */
get_and_lock(OSSL_EX_DATA_GLOBAL * global,int class_index,int read)31 static EX_CALLBACKS *get_and_lock(OSSL_EX_DATA_GLOBAL *global, int class_index,
32     int read)
33 {
34     EX_CALLBACKS *ip;
35 
36     if (class_index < 0 || class_index >= CRYPTO_EX_INDEX__COUNT) {
37         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
38         return NULL;
39     }
40 
41     if (global->ex_data_lock == NULL) {
42         /*
43          * If we get here, someone (who?) cleaned up the lock, so just
44          * treat it as an error.
45          */
46         return NULL;
47     }
48 
49     if (read) {
50         if (!CRYPTO_THREAD_read_lock(global->ex_data_lock))
51             return NULL;
52     } else {
53         if (!CRYPTO_THREAD_write_lock(global->ex_data_lock))
54             return NULL;
55     }
56 
57     ip = &global->ex_data[class_index];
58     return ip;
59 }
60 
cleanup_cb(EX_CALLBACK * funcs)61 static void cleanup_cb(EX_CALLBACK *funcs)
62 {
63     OPENSSL_free(funcs);
64 }
65 
66 /*
67  * Release all "ex_data" state to prevent memory leaks. This can't be made
68  * thread-safe without overhauling a lot of stuff, and shouldn't really be
69  * called under potential race-conditions anyway (it's for program shutdown
70  * after all).
71  */
ossl_crypto_cleanup_all_ex_data_int(OSSL_LIB_CTX * ctx)72 void ossl_crypto_cleanup_all_ex_data_int(OSSL_LIB_CTX *ctx)
73 {
74     int i;
75     OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx);
76 
77     if (global == NULL)
78         return;
79 
80     for (i = 0; i < CRYPTO_EX_INDEX__COUNT; ++i) {
81         EX_CALLBACKS *ip = &global->ex_data[i];
82 
83         sk_EX_CALLBACK_pop_free(ip->meth, cleanup_cb);
84         ip->meth = NULL;
85     }
86 
87     CRYPTO_THREAD_lock_free(global->ex_data_lock);
88     global->ex_data_lock = NULL;
89 }
90 
91 /*
92  * Unregister a new index by replacing the callbacks with no-ops.
93  * Any in-use instances are leaked.
94  */
dummy_new(void * parent,void * ptr,CRYPTO_EX_DATA * ad,int idx,long argl,void * argp)95 static void dummy_new(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
96     long argl, void *argp)
97 {
98 }
99 
dummy_free(void * parent,void * ptr,CRYPTO_EX_DATA * ad,int idx,long argl,void * argp)100 static void dummy_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
101     long argl, void *argp)
102 {
103 }
104 
dummy_dup(CRYPTO_EX_DATA * to,const CRYPTO_EX_DATA * from,void ** from_d,int idx,long argl,void * argp)105 static int dummy_dup(CRYPTO_EX_DATA *to, const CRYPTO_EX_DATA *from,
106     void **from_d, int idx,
107     long argl, void *argp)
108 {
109     return 1;
110 }
111 
ossl_crypto_free_ex_index_ex(OSSL_LIB_CTX * ctx,int class_index,int idx)112 int ossl_crypto_free_ex_index_ex(OSSL_LIB_CTX *ctx, int class_index, int idx)
113 {
114     EX_CALLBACKS *ip;
115     EX_CALLBACK *a;
116     int toret = 0;
117     OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx);
118 
119     if (global == NULL)
120         return 0;
121 
122     ip = get_and_lock(global, class_index, 0);
123     if (ip == NULL)
124         return 0;
125 
126     if (idx < 0 || idx >= sk_EX_CALLBACK_num(ip->meth))
127         goto err;
128     a = sk_EX_CALLBACK_value(ip->meth, idx);
129     if (a == NULL)
130         goto err;
131     a->new_func = dummy_new;
132     a->dup_func = dummy_dup;
133     a->free_func = dummy_free;
134     toret = 1;
135 err:
136     CRYPTO_THREAD_unlock(global->ex_data_lock);
137     return toret;
138 }
139 
CRYPTO_free_ex_index(int class_index,int idx)140 int CRYPTO_free_ex_index(int class_index, int idx)
141 {
142     return ossl_crypto_free_ex_index_ex(NULL, class_index, idx);
143 }
144 
145 /*
146  * Register a new index.
147  */
ossl_crypto_get_ex_new_index_ex(OSSL_LIB_CTX * ctx,int class_index,long argl,void * argp,CRYPTO_EX_new * new_func,CRYPTO_EX_dup * dup_func,CRYPTO_EX_free * free_func,int priority)148 int ossl_crypto_get_ex_new_index_ex(OSSL_LIB_CTX *ctx, int class_index,
149     long argl, void *argp,
150     CRYPTO_EX_new *new_func,
151     CRYPTO_EX_dup *dup_func,
152     CRYPTO_EX_free *free_func,
153     int priority)
154 {
155     int toret = -1;
156     EX_CALLBACK *a;
157     EX_CALLBACKS *ip;
158     OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx);
159 
160     if (global == NULL)
161         return -1;
162 
163     ip = get_and_lock(global, class_index, 0);
164     if (ip == NULL)
165         return -1;
166 
167     if (ip->meth == NULL) {
168         ip->meth = sk_EX_CALLBACK_new_null();
169         /* We push an initial value on the stack because the SSL
170          * "app_data" routines use ex_data index zero.  See RT 3710. */
171         if (ip->meth == NULL
172             || !sk_EX_CALLBACK_push(ip->meth, NULL)) {
173             sk_EX_CALLBACK_free(ip->meth);
174             ip->meth = NULL;
175             ERR_raise(ERR_LIB_CRYPTO, ERR_R_CRYPTO_LIB);
176             goto err;
177         }
178     }
179 
180     a = (EX_CALLBACK *)OPENSSL_malloc(sizeof(*a));
181     if (a == NULL)
182         goto err;
183     a->argl = argl;
184     a->argp = argp;
185     a->new_func = new_func;
186     a->dup_func = dup_func;
187     a->free_func = free_func;
188     a->priority = priority;
189 
190     if (!sk_EX_CALLBACK_push(ip->meth, NULL)) {
191         ERR_raise(ERR_LIB_CRYPTO, ERR_R_CRYPTO_LIB);
192         OPENSSL_free(a);
193         goto err;
194     }
195     toret = sk_EX_CALLBACK_num(ip->meth) - 1;
196     (void)sk_EX_CALLBACK_set(ip->meth, toret, a);
197 
198 err:
199     CRYPTO_THREAD_unlock(global->ex_data_lock);
200     return toret;
201 }
202 
CRYPTO_get_ex_new_index(int class_index,long argl,void * argp,CRYPTO_EX_new * new_func,CRYPTO_EX_dup * dup_func,CRYPTO_EX_free * free_func)203 int CRYPTO_get_ex_new_index(int class_index, long argl, void *argp,
204     CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
205     CRYPTO_EX_free *free_func)
206 {
207     return ossl_crypto_get_ex_new_index_ex(NULL, class_index, argl, argp,
208         new_func, dup_func, free_func, 0);
209 }
210 
211 /*
212  * Initialise a new CRYPTO_EX_DATA for use in a particular class - including
213  * calling new() callbacks for each index in the class used by this variable
214  * Thread-safe by copying a class's array of "EX_CALLBACK" entries
215  * in the lock, then using them outside the lock. Note this only applies
216  * to the global "ex_data" state (ie. class definitions), not 'ad' itself.
217  */
ossl_crypto_new_ex_data_ex(OSSL_LIB_CTX * ctx,int class_index,void * obj,CRYPTO_EX_DATA * ad)218 int ossl_crypto_new_ex_data_ex(OSSL_LIB_CTX *ctx, int class_index, void *obj,
219     CRYPTO_EX_DATA *ad)
220 {
221     int mx, i;
222     void *ptr;
223     EX_CALLBACK **storage = NULL;
224     EX_CALLBACK *stack[10];
225     EX_CALLBACKS *ip;
226     OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx);
227 
228     if (global == NULL)
229         return 0;
230 
231     ip = get_and_lock(global, class_index, 1);
232     if (ip == NULL)
233         return 0;
234 
235     ad->ctx = ctx;
236     ad->sk = NULL;
237     mx = sk_EX_CALLBACK_num(ip->meth);
238     if (mx > 0) {
239         if (mx < (int)OSSL_NELEM(stack))
240             storage = stack;
241         else
242             storage = OPENSSL_malloc(sizeof(*storage) * mx);
243         if (storage != NULL)
244             for (i = 0; i < mx; i++)
245                 storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
246     }
247     CRYPTO_THREAD_unlock(global->ex_data_lock);
248 
249     if (mx > 0 && storage == NULL)
250         return 0;
251     for (i = 0; i < mx; i++) {
252         if (storage[i] != NULL && storage[i]->new_func != NULL) {
253             ptr = CRYPTO_get_ex_data(ad, i);
254             storage[i]->new_func(obj, ptr, ad, i,
255                 storage[i]->argl, storage[i]->argp);
256         }
257     }
258     if (storage != stack)
259         OPENSSL_free(storage);
260     return 1;
261 }
262 
CRYPTO_new_ex_data(int class_index,void * obj,CRYPTO_EX_DATA * ad)263 int CRYPTO_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
264 {
265     return ossl_crypto_new_ex_data_ex(NULL, class_index, obj, ad);
266 }
267 
268 /*
269  * Duplicate a CRYPTO_EX_DATA variable - including calling dup() callbacks
270  * for each index in the class used by this variable
271  */
CRYPTO_dup_ex_data(int class_index,CRYPTO_EX_DATA * to,const CRYPTO_EX_DATA * from)272 int CRYPTO_dup_ex_data(int class_index, CRYPTO_EX_DATA *to,
273     const CRYPTO_EX_DATA *from)
274 {
275     int mx, j, i;
276     void *ptr;
277     EX_CALLBACK *stack[10];
278     EX_CALLBACK **storage = NULL;
279     EX_CALLBACKS *ip;
280     int toret = 0;
281     OSSL_EX_DATA_GLOBAL *global;
282 
283     to->ctx = from->ctx;
284     if (from->sk == NULL)
285         /* Nothing to copy over */
286         return 1;
287 
288     global = ossl_lib_ctx_get_ex_data_global(from->ctx);
289     if (global == NULL)
290         return 0;
291 
292     ip = get_and_lock(global, class_index, 1);
293     if (ip == NULL)
294         return 0;
295 
296     mx = sk_EX_CALLBACK_num(ip->meth);
297     j = sk_void_num(from->sk);
298     if (j < mx)
299         mx = j;
300     if (mx > 0) {
301         if (mx < (int)OSSL_NELEM(stack))
302             storage = stack;
303         else
304             storage = OPENSSL_malloc(sizeof(*storage) * mx);
305         if (storage != NULL)
306             for (i = 0; i < mx; i++)
307                 storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
308     }
309     CRYPTO_THREAD_unlock(global->ex_data_lock);
310 
311     if (mx == 0)
312         return 1;
313     if (storage == NULL)
314         return 0;
315     /*
316      * Make sure the ex_data stack is at least |mx| elements long to avoid
317      * issues in the for loop that follows; so go get the |mx|'th element
318      * (if it does not exist CRYPTO_get_ex_data() returns NULL), and assign
319      * to itself. This is normally a no-op; but ensures the stack is the
320      * proper size
321      */
322     if (!CRYPTO_set_ex_data(to, mx - 1, CRYPTO_get_ex_data(to, mx - 1)))
323         goto err;
324 
325     for (i = 0; i < mx; i++) {
326         ptr = CRYPTO_get_ex_data(from, i);
327         if (storage[i] != NULL && storage[i]->dup_func != NULL)
328             if (!storage[i]->dup_func(to, from, &ptr, i,
329                     storage[i]->argl, storage[i]->argp))
330                 goto err;
331         CRYPTO_set_ex_data(to, i, ptr);
332     }
333     toret = 1;
334 err:
335     if (storage != stack)
336         OPENSSL_free(storage);
337     return toret;
338 }
339 
340 struct ex_callback_entry {
341     const EX_CALLBACK *excb;
342     int index;
343 };
344 
ex_callback_compare(const void * a,const void * b)345 static int ex_callback_compare(const void *a, const void *b)
346 {
347     const struct ex_callback_entry *ap = (const struct ex_callback_entry *)a;
348     const struct ex_callback_entry *bp = (const struct ex_callback_entry *)b;
349 
350     if (ap->excb == bp->excb)
351         return 0;
352 
353     if (ap->excb == NULL)
354         return 1;
355     if (bp->excb == NULL)
356         return -1;
357     if (ap->excb->priority == bp->excb->priority)
358         return 0;
359     return ap->excb->priority > bp->excb->priority ? -1 : 1;
360 }
361 
362 /*
363  * Cleanup a CRYPTO_EX_DATA variable - including calling free() callbacks for
364  * each index in the class used by this variable
365  */
CRYPTO_free_ex_data(int class_index,void * obj,CRYPTO_EX_DATA * ad)366 void CRYPTO_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
367 {
368     int mx, i;
369     EX_CALLBACKS *ip;
370     void *ptr;
371     const EX_CALLBACK *f;
372     struct ex_callback_entry stack[10];
373     struct ex_callback_entry *storage = NULL;
374     OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ad->ctx);
375 
376     if (global == NULL)
377         goto err;
378 
379     ip = get_and_lock(global, class_index, 1);
380     if (ip == NULL)
381         goto err;
382 
383     mx = sk_EX_CALLBACK_num(ip->meth);
384     if (mx > 0) {
385         if (mx < (int)OSSL_NELEM(stack))
386             storage = stack;
387         else
388             storage = OPENSSL_malloc(sizeof(*storage) * mx);
389         if (storage != NULL)
390             for (i = 0; i < mx; i++) {
391                 storage[i].excb = sk_EX_CALLBACK_value(ip->meth, i);
392                 storage[i].index = i;
393             }
394     }
395     CRYPTO_THREAD_unlock(global->ex_data_lock);
396 
397     if (storage != NULL) {
398         /* Sort according to priority. High priority first */
399         qsort(storage, mx, sizeof(*storage), ex_callback_compare);
400         for (i = 0; i < mx; i++) {
401             f = storage[i].excb;
402 
403             if (f != NULL && f->free_func != NULL) {
404                 ptr = CRYPTO_get_ex_data(ad, storage[i].index);
405                 f->free_func(obj, ptr, ad, storage[i].index, f->argl, f->argp);
406             }
407         }
408     }
409 
410     if (storage != stack)
411         OPENSSL_free(storage);
412 err:
413     sk_void_free(ad->sk);
414     ad->sk = NULL;
415     ad->ctx = NULL;
416 }
417 
418 /*
419  * Allocate a given CRYPTO_EX_DATA item using the class specific allocation
420  * function
421  */
CRYPTO_alloc_ex_data(int class_index,void * obj,CRYPTO_EX_DATA * ad,int idx)422 int CRYPTO_alloc_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad,
423     int idx)
424 {
425     void *curval;
426 
427     curval = CRYPTO_get_ex_data(ad, idx);
428     /* Already there, no need to allocate */
429     if (curval != NULL)
430         return 1;
431 
432     return ossl_crypto_alloc_ex_data_intern(class_index, obj, ad, idx);
433 }
434 
ossl_crypto_alloc_ex_data_intern(int class_index,void * obj,CRYPTO_EX_DATA * ad,int idx)435 int ossl_crypto_alloc_ex_data_intern(int class_index, void *obj,
436     CRYPTO_EX_DATA *ad, int idx)
437 {
438     EX_CALLBACK *f;
439     EX_CALLBACKS *ip;
440     OSSL_EX_DATA_GLOBAL *global;
441 
442     global = ossl_lib_ctx_get_ex_data_global(ad->ctx);
443     if (global == NULL)
444         return 0;
445 
446     ip = get_and_lock(global, class_index, 1);
447     if (ip == NULL)
448         return 0;
449     f = sk_EX_CALLBACK_value(ip->meth, idx);
450     CRYPTO_THREAD_unlock(global->ex_data_lock);
451 
452     /*
453      * This should end up calling CRYPTO_set_ex_data(), which allocates
454      * everything necessary to support placing the new data in the right spot.
455      */
456     if (f->new_func == NULL)
457         return 0;
458 
459     f->new_func(obj, NULL, ad, idx, f->argl, f->argp);
460 
461     return 1;
462 }
463 
464 /*
465  * For a given CRYPTO_EX_DATA variable, set the value corresponding to a
466  * particular index in the class used by this variable
467  */
CRYPTO_set_ex_data(CRYPTO_EX_DATA * ad,int idx,void * val)468 int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val)
469 {
470     int i;
471 
472     if (ad->sk == NULL) {
473         if ((ad->sk = sk_void_new_null()) == NULL) {
474             ERR_raise(ERR_LIB_CRYPTO, ERR_R_CRYPTO_LIB);
475             return 0;
476         }
477     }
478 
479     for (i = sk_void_num(ad->sk); i <= idx; ++i) {
480         if (!sk_void_push(ad->sk, NULL)) {
481             ERR_raise(ERR_LIB_CRYPTO, ERR_R_CRYPTO_LIB);
482             return 0;
483         }
484     }
485     if (sk_void_set(ad->sk, idx, val) != val) {
486         /* Probably the index is out of bounds */
487         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
488         return 0;
489     }
490     return 1;
491 }
492 
493 /*
494  * For a given CRYPTO_EX_DATA_ variable, get the value corresponding to a
495  * particular index in the class used by this variable
496  */
CRYPTO_get_ex_data(const CRYPTO_EX_DATA * ad,int idx)497 void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx)
498 {
499     if (ad->sk == NULL || idx >= sk_void_num(ad->sk))
500         return NULL;
501     return sk_void_value(ad->sk, idx);
502 }
503 
ossl_crypto_ex_data_get_ossl_lib_ctx(const CRYPTO_EX_DATA * ad)504 OSSL_LIB_CTX *ossl_crypto_ex_data_get_ossl_lib_ctx(const CRYPTO_EX_DATA *ad)
505 {
506     return ad->ctx;
507 }
508