In OpenSSL builds, declare STACK for datatypes ...
[openssl.git] / crypto / ex_data.c
1 /*
2  * Copyright 1995-2020 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 DEFINE_STACK_OF(void)
14
15 int do_ex_data_init(OPENSSL_CTX *ctx)
16 {
17     OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
18
19     if (global == NULL)
20         return 0;
21
22     global->ex_data_lock = CRYPTO_THREAD_lock_new();
23     return global->ex_data_lock != NULL;
24 }
25
26 /*
27  * Return the EX_CALLBACKS from the |ex_data| array that corresponds to
28  * a given class.  On success, *holds the lock.*
29  * The |global| parameter is assumed to be non null (checked by the caller).
30  */
31 static EX_CALLBACKS *get_and_lock(OSSL_EX_DATA_GLOBAL *global, int class_index)
32 {
33     EX_CALLBACKS *ip;
34
35     if (class_index < 0 || class_index >= CRYPTO_EX_INDEX__COUNT) {
36         CRYPTOerr(CRYPTO_F_GET_AND_LOCK, ERR_R_PASSED_INVALID_ARGUMENT);
37         return NULL;
38     }
39
40     if (global->ex_data_lock == NULL) {
41         /*
42          * If we get here, someone (who?) cleaned up the lock, so just
43          * treat it as an error.
44          */
45          return NULL;
46     }
47
48     CRYPTO_THREAD_write_lock(global->ex_data_lock);
49     ip = &global->ex_data[class_index];
50     return ip;
51 }
52
53 static void cleanup_cb(EX_CALLBACK *funcs)
54 {
55     OPENSSL_free(funcs);
56 }
57
58 /*
59  * Release all "ex_data" state to prevent memory leaks. This can't be made
60  * thread-safe without overhauling a lot of stuff, and shouldn't really be
61  * called under potential race-conditions anyway (it's for program shutdown
62  * after all).
63  */
64 void crypto_cleanup_all_ex_data_int(OPENSSL_CTX *ctx)
65 {
66     int i;
67     OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
68
69     if (global == NULL)
70         return;
71
72     for (i = 0; i < CRYPTO_EX_INDEX__COUNT; ++i) {
73         EX_CALLBACKS *ip = &global->ex_data[i];
74
75         sk_EX_CALLBACK_pop_free(ip->meth, cleanup_cb);
76         ip->meth = NULL;
77     }
78
79     CRYPTO_THREAD_lock_free(global->ex_data_lock);
80     global->ex_data_lock = NULL;
81 }
82
83
84 /*
85  * Unregister a new index by replacing the callbacks with no-ops.
86  * Any in-use instances are leaked.
87  */
88 static void dummy_new(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
89                      long argl, void *argp)
90 {
91 }
92
93 static void dummy_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
94                        long argl, void *argp)
95 {
96 }
97
98 static int dummy_dup(CRYPTO_EX_DATA *to, const CRYPTO_EX_DATA *from,
99                      void *from_d, int idx,
100                      long argl, void *argp)
101 {
102     return 1;
103 }
104
105 int crypto_free_ex_index_ex(OPENSSL_CTX *ctx, int class_index, int idx)
106 {
107     EX_CALLBACKS *ip;
108     EX_CALLBACK *a;
109     int toret = 0;
110     OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
111
112     if (global == NULL)
113         return 0;
114
115     ip = get_and_lock(global, class_index);
116     if (ip == NULL)
117         return 0;
118
119     if (idx < 0 || idx >= sk_EX_CALLBACK_num(ip->meth))
120         goto err;
121     a = sk_EX_CALLBACK_value(ip->meth, idx);
122     if (a == NULL)
123         goto err;
124     a->new_func = dummy_new;
125     a->dup_func = dummy_dup;
126     a->free_func = dummy_free;
127     toret = 1;
128 err:
129     CRYPTO_THREAD_unlock(global->ex_data_lock);
130     return toret;
131 }
132
133 int CRYPTO_free_ex_index(int class_index, int idx)
134 {
135     return crypto_free_ex_index_ex(NULL, class_index, idx);
136 }
137
138 /*
139  * Register a new index.
140  */
141 int crypto_get_ex_new_index_ex(OPENSSL_CTX *ctx, int class_index, long argl,
142                                void *argp, 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 = openssl_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             CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX_EX, ERR_R_MALLOC_FAILURE);
165             goto err;
166         }
167     }
168
169     a = (EX_CALLBACK *)OPENSSL_malloc(sizeof(*a));
170     if (a == NULL) {
171         CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX_EX, 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         CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX_EX, 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 crypto_get_ex_new_index_ex(NULL, class_index, argl, argp, new_func,
198                                       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 crypto_new_ex_data_ex(OPENSSL_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 = openssl_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         CRYPTOerr(CRYPTO_F_CRYPTO_NEW_EX_DATA_EX, 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 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 = openssl_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         CRYPTOerr(CRYPTO_F_CRYPTO_DUP_EX_DATA, 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 = openssl_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             CRYPTO_THREAD_write_lock(global->ex_data_lock);
373             f = sk_EX_CALLBACK_value(ip->meth, i);
374             CRYPTO_THREAD_unlock(global->ex_data_lock);
375         }
376         if (f != NULL && f->free_func != NULL) {
377             ptr = CRYPTO_get_ex_data(ad, i);
378             f->free_func(obj, ptr, ad, i, f->argl, f->argp);
379         }
380     }
381
382     if (storage != stack)
383         OPENSSL_free(storage);
384  err:
385     sk_void_free(ad->sk);
386     ad->sk = NULL;
387     ad->ctx = NULL;
388 }
389
390 /*
391  * Allocate a given CRYPTO_EX_DATA item using the class specific allocation
392  * function
393  */
394 int CRYPTO_alloc_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad,
395                          int idx)
396 {
397     EX_CALLBACK *f;
398     EX_CALLBACKS *ip;
399     void *curval;
400     OSSL_EX_DATA_GLOBAL *global;
401
402     curval = CRYPTO_get_ex_data(ad, idx);
403     /* Already there, no need to allocate */
404     if (curval != NULL)
405         return 1;
406
407     global = openssl_ctx_get_ex_data_global(ad->ctx);
408     if (global == NULL)
409         return 0;
410
411     ip = get_and_lock(global, class_index);
412     if (ip == NULL)
413         return 0;
414     f = sk_EX_CALLBACK_value(ip->meth, idx);
415     CRYPTO_THREAD_unlock(global->ex_data_lock);
416
417     /*
418      * This should end up calling CRYPTO_set_ex_data(), which allocates
419      * everything necessary to support placing the new data in the right spot.
420      */
421     if (f->new_func == NULL)
422         return 0;
423
424     f->new_func(obj, NULL, ad, idx, f->argl, f->argp);
425
426     return 1;
427 }
428
429 /*
430  * For a given CRYPTO_EX_DATA variable, set the value corresponding to a
431  * particular index in the class used by this variable
432  */
433 int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val)
434 {
435     int i;
436
437     if (ad->sk == NULL) {
438         if ((ad->sk = sk_void_new_null()) == NULL) {
439             CRYPTOerr(CRYPTO_F_CRYPTO_SET_EX_DATA, ERR_R_MALLOC_FAILURE);
440             return 0;
441         }
442     }
443
444     for (i = sk_void_num(ad->sk); i <= idx; ++i) {
445         if (!sk_void_push(ad->sk, NULL)) {
446             CRYPTOerr(CRYPTO_F_CRYPTO_SET_EX_DATA, ERR_R_MALLOC_FAILURE);
447             return 0;
448         }
449     }
450     sk_void_set(ad->sk, idx, val);
451     return 1;
452 }
453
454 /*
455  * For a given CRYPTO_EX_DATA_ variable, get the value corresponding to a
456  * particular index in the class used by this variable
457  */
458 void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx)
459 {
460     if (ad->sk == NULL || idx >= sk_void_num(ad->sk))
461         return NULL;
462     return sk_void_value(ad->sk, idx);
463 }
464
465 OPENSSL_CTX *crypto_ex_data_get_openssl_ctx(const CRYPTO_EX_DATA *ad)
466 {
467     return ad->ctx;
468 }