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