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