• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (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 /*
14  * Each structure type (sometimes called a class), that supports
15  * exdata has a stack of callbacks for each instance.
16  */
17 struct ex_callback_st {
18     long argl;                  /* Arbitrary long */
19     void *argp;                 /* Arbitrary void * */
20     CRYPTO_EX_new *new_func;
21     CRYPTO_EX_free *free_func;
22     CRYPTO_EX_dup *dup_func;
23 };
24 
25 /*
26  * The state for each class.  This could just be a typedef, but
27  * a structure allows future changes.
28  */
29 typedef struct ex_callbacks_st {
30     STACK_OF(EX_CALLBACK) *meth;
31 } EX_CALLBACKS;
32 
33 static EX_CALLBACKS ex_data[CRYPTO_EX_INDEX__COUNT];
34 
35 static CRYPTO_RWLOCK *ex_data_lock = NULL;
36 static CRYPTO_ONCE ex_data_init = CRYPTO_ONCE_STATIC_INIT;
37 
DEFINE_RUN_ONCE_STATIC(do_ex_data_init)38 DEFINE_RUN_ONCE_STATIC(do_ex_data_init)
39 {
40     if (!OPENSSL_init_crypto(0, NULL))
41         return 0;
42     ex_data_lock = CRYPTO_THREAD_lock_new();
43     return ex_data_lock != NULL;
44 }
45 
46 /*
47  * Return the EX_CALLBACKS from the |ex_data| array that corresponds to
48  * a given class.  On success, *holds the lock.*
49  */
get_and_lock(int class_index)50 static EX_CALLBACKS *get_and_lock(int class_index)
51 {
52     EX_CALLBACKS *ip;
53 
54     if (class_index < 0 || class_index >= CRYPTO_EX_INDEX__COUNT) {
55         CRYPTOerr(CRYPTO_F_GET_AND_LOCK, ERR_R_PASSED_INVALID_ARGUMENT);
56         return NULL;
57     }
58 
59     if (!RUN_ONCE(&ex_data_init, do_ex_data_init)) {
60         CRYPTOerr(CRYPTO_F_GET_AND_LOCK, ERR_R_MALLOC_FAILURE);
61         return NULL;
62     }
63 
64     if (ex_data_lock == NULL) {
65         /*
66          * This can happen in normal operation when using CRYPTO_mem_leaks().
67          * The CRYPTO_mem_leaks() function calls OPENSSL_cleanup() which cleans
68          * up the locks. Subsequently the BIO that CRYPTO_mem_leaks() uses gets
69          * freed, which also attempts to free the ex_data. However
70          * CRYPTO_mem_leaks() ensures that the ex_data is freed early (i.e.
71          * before OPENSSL_cleanup() is called), so if we get here we can safely
72          * ignore this operation. We just treat it as an error.
73          */
74          return NULL;
75     }
76 
77     ip = &ex_data[class_index];
78     CRYPTO_THREAD_write_lock(ex_data_lock);
79     return ip;
80 }
81 
cleanup_cb(EX_CALLBACK * funcs)82 static void cleanup_cb(EX_CALLBACK *funcs)
83 {
84     OPENSSL_free(funcs);
85 }
86 
87 /*
88  * Release all "ex_data" state to prevent memory leaks. This can't be made
89  * thread-safe without overhauling a lot of stuff, and shouldn't really be
90  * called under potential race-conditions anyway (it's for program shutdown
91  * after all).
92  */
crypto_cleanup_all_ex_data_int(void)93 void crypto_cleanup_all_ex_data_int(void)
94 {
95     int i;
96 
97     for (i = 0; i < CRYPTO_EX_INDEX__COUNT; ++i) {
98         EX_CALLBACKS *ip = &ex_data[i];
99 
100         sk_EX_CALLBACK_pop_free(ip->meth, cleanup_cb);
101         ip->meth = NULL;
102     }
103 
104     CRYPTO_THREAD_lock_free(ex_data_lock);
105     ex_data_lock = NULL;
106 }
107 
108 
109 /*
110  * Unregister a new index by replacing the callbacks with no-ops.
111  * Any in-use instances are leaked.
112  */
dummy_new(void * parent,void * ptr,CRYPTO_EX_DATA * ad,int idx,long argl,void * argp)113 static void dummy_new(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
114                      long argl, void *argp)
115 {
116 }
117 
dummy_free(void * parent,void * ptr,CRYPTO_EX_DATA * ad,int idx,long argl,void * argp)118 static void dummy_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
119                        long argl, void *argp)
120 {
121 }
122 
dummy_dup(CRYPTO_EX_DATA * to,const CRYPTO_EX_DATA * from,void * from_d,int idx,long argl,void * argp)123 static int dummy_dup(CRYPTO_EX_DATA *to, const CRYPTO_EX_DATA *from,
124                      void *from_d, int idx,
125                      long argl, void *argp)
126 {
127     return 1;
128 }
129 
CRYPTO_free_ex_index(int class_index,int idx)130 int CRYPTO_free_ex_index(int class_index, int idx)
131 {
132     EX_CALLBACKS *ip = get_and_lock(class_index);
133     EX_CALLBACK *a;
134     int toret = 0;
135 
136     if (ip == NULL)
137         return 0;
138     if (idx < 0 || idx >= sk_EX_CALLBACK_num(ip->meth))
139         goto err;
140     a = sk_EX_CALLBACK_value(ip->meth, idx);
141     if (a == NULL)
142         goto err;
143     a->new_func = dummy_new;
144     a->dup_func = dummy_dup;
145     a->free_func = dummy_free;
146     toret = 1;
147 err:
148     CRYPTO_THREAD_unlock(ex_data_lock);
149     return toret;
150 }
151 
152 /*
153  * Register a new index.
154  */
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)155 int CRYPTO_get_ex_new_index(int class_index, long argl, void *argp,
156                             CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
157                             CRYPTO_EX_free *free_func)
158 {
159     int toret = -1;
160     EX_CALLBACK *a;
161     EX_CALLBACKS *ip = get_and_lock(class_index);
162 
163     if (ip == NULL)
164         return -1;
165 
166     if (ip->meth == NULL) {
167         ip->meth = sk_EX_CALLBACK_new_null();
168         /* We push an initial value on the stack because the SSL
169          * "app_data" routines use ex_data index zero.  See RT 3710. */
170         if (ip->meth == NULL
171             || !sk_EX_CALLBACK_push(ip->meth, NULL)) {
172             CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX, ERR_R_MALLOC_FAILURE);
173             goto err;
174         }
175     }
176 
177     a = (EX_CALLBACK *)OPENSSL_malloc(sizeof(*a));
178     if (a == NULL) {
179         CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX, ERR_R_MALLOC_FAILURE);
180         goto err;
181     }
182     a->argl = argl;
183     a->argp = argp;
184     a->new_func = new_func;
185     a->dup_func = dup_func;
186     a->free_func = free_func;
187 
188     if (!sk_EX_CALLBACK_push(ip->meth, NULL)) {
189         CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX, ERR_R_MALLOC_FAILURE);
190         OPENSSL_free(a);
191         goto err;
192     }
193     toret = sk_EX_CALLBACK_num(ip->meth) - 1;
194     (void)sk_EX_CALLBACK_set(ip->meth, toret, a);
195 
196  err:
197     CRYPTO_THREAD_unlock(ex_data_lock);
198     return toret;
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  */
CRYPTO_new_ex_data(int class_index,void * obj,CRYPTO_EX_DATA * ad)208 int CRYPTO_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
209 {
210     int mx, i;
211     void *ptr;
212     EX_CALLBACK **storage = NULL;
213     EX_CALLBACK *stack[10];
214     EX_CALLBACKS *ip = get_and_lock(class_index);
215 
216     if (ip == NULL)
217         return 0;
218 
219     ad->sk = NULL;
220 
221     mx = sk_EX_CALLBACK_num(ip->meth);
222     if (mx > 0) {
223         if (mx < (int)OSSL_NELEM(stack))
224             storage = stack;
225         else
226             storage = OPENSSL_malloc(sizeof(*storage) * mx);
227         if (storage != NULL)
228             for (i = 0; i < mx; i++)
229                 storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
230     }
231     CRYPTO_THREAD_unlock(ex_data_lock);
232 
233     if (mx > 0 && storage == NULL) {
234         CRYPTOerr(CRYPTO_F_CRYPTO_NEW_EX_DATA, ERR_R_MALLOC_FAILURE);
235         return 0;
236     }
237     for (i = 0; i < mx; i++) {
238         if (storage[i] != NULL && storage[i]->new_func != NULL) {
239             ptr = CRYPTO_get_ex_data(ad, i);
240             storage[i]->new_func(obj, ptr, ad, i,
241                                  storage[i]->argl, storage[i]->argp);
242         }
243     }
244     if (storage != stack)
245         OPENSSL_free(storage);
246     return 1;
247 }
248 
249 /*
250  * Duplicate a CRYPTO_EX_DATA variable - including calling dup() callbacks
251  * for each index in the class used by this variable
252  */
CRYPTO_dup_ex_data(int class_index,CRYPTO_EX_DATA * to,const CRYPTO_EX_DATA * from)253 int CRYPTO_dup_ex_data(int class_index, CRYPTO_EX_DATA *to,
254                        const CRYPTO_EX_DATA *from)
255 {
256     int mx, j, i;
257     void *ptr;
258     EX_CALLBACK *stack[10];
259     EX_CALLBACK **storage = NULL;
260     EX_CALLBACKS *ip;
261     int toret = 0;
262 
263     if (from->sk == NULL)
264         /* Nothing to copy over */
265         return 1;
266     if ((ip = get_and_lock(class_index)) == NULL)
267         return 0;
268 
269     mx = sk_EX_CALLBACK_num(ip->meth);
270     j = sk_void_num(from->sk);
271     if (j < mx)
272         mx = j;
273     if (mx > 0) {
274         if (mx < (int)OSSL_NELEM(stack))
275             storage = stack;
276         else
277             storage = OPENSSL_malloc(sizeof(*storage) * mx);
278         if (storage != NULL)
279             for (i = 0; i < mx; i++)
280                 storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
281     }
282     CRYPTO_THREAD_unlock(ex_data_lock);
283 
284     if (mx == 0)
285         return 1;
286     if (storage == NULL) {
287         CRYPTOerr(CRYPTO_F_CRYPTO_DUP_EX_DATA, ERR_R_MALLOC_FAILURE);
288         return 0;
289     }
290     /*
291      * Make sure the ex_data stack is at least |mx| elements long to avoid
292      * issues in the for loop that follows; so go get the |mx|'th element
293      * (if it does not exist CRYPTO_get_ex_data() returns NULL), and assign
294      * to itself. This is normally a no-op; but ensures the stack is the
295      * proper size
296      */
297     if (!CRYPTO_set_ex_data(to, mx - 1, CRYPTO_get_ex_data(to, mx - 1)))
298         goto err;
299 
300     for (i = 0; i < mx; i++) {
301         ptr = CRYPTO_get_ex_data(from, i);
302         if (storage[i] != NULL && storage[i]->dup_func != NULL)
303             if (!storage[i]->dup_func(to, from, &ptr, i,
304                                       storage[i]->argl, storage[i]->argp))
305                 goto err;
306         CRYPTO_set_ex_data(to, i, ptr);
307     }
308     toret = 1;
309  err:
310     if (storage != stack)
311         OPENSSL_free(storage);
312     return toret;
313 }
314 
315 
316 /*
317  * Cleanup a CRYPTO_EX_DATA variable - including calling free() callbacks for
318  * each index in the class used by this variable
319  */
CRYPTO_free_ex_data(int class_index,void * obj,CRYPTO_EX_DATA * ad)320 void CRYPTO_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
321 {
322     int mx, i;
323     EX_CALLBACKS *ip;
324     void *ptr;
325     EX_CALLBACK *f;
326     EX_CALLBACK *stack[10];
327     EX_CALLBACK **storage = NULL;
328 
329     if ((ip = get_and_lock(class_index)) == NULL)
330         goto err;
331 
332     mx = sk_EX_CALLBACK_num(ip->meth);
333     if (mx > 0) {
334         if (mx < (int)OSSL_NELEM(stack))
335             storage = stack;
336         else
337             storage = OPENSSL_malloc(sizeof(*storage) * mx);
338         if (storage != NULL)
339             for (i = 0; i < mx; i++)
340                 storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
341     }
342     CRYPTO_THREAD_unlock(ex_data_lock);
343 
344     for (i = 0; i < mx; i++) {
345         if (storage != NULL)
346             f = storage[i];
347         else {
348             CRYPTO_THREAD_write_lock(ex_data_lock);
349             f = sk_EX_CALLBACK_value(ip->meth, i);
350             CRYPTO_THREAD_unlock(ex_data_lock);
351         }
352         if (f != NULL && f->free_func != NULL) {
353             ptr = CRYPTO_get_ex_data(ad, i);
354             f->free_func(obj, ptr, ad, i, f->argl, f->argp);
355         }
356     }
357 
358     if (storage != stack)
359         OPENSSL_free(storage);
360  err:
361     sk_void_free(ad->sk);
362     ad->sk = NULL;
363 }
364 
365 /*
366  * For a given CRYPTO_EX_DATA variable, set the value corresponding to a
367  * particular index in the class used by this variable
368  */
CRYPTO_set_ex_data(CRYPTO_EX_DATA * ad,int idx,void * val)369 int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val)
370 {
371     int i;
372 
373     if (ad->sk == NULL) {
374         if ((ad->sk = sk_void_new_null()) == NULL) {
375             CRYPTOerr(CRYPTO_F_CRYPTO_SET_EX_DATA, ERR_R_MALLOC_FAILURE);
376             return 0;
377         }
378     }
379 
380     for (i = sk_void_num(ad->sk); i <= idx; ++i) {
381         if (!sk_void_push(ad->sk, NULL)) {
382             CRYPTOerr(CRYPTO_F_CRYPTO_SET_EX_DATA, ERR_R_MALLOC_FAILURE);
383             return 0;
384         }
385     }
386     sk_void_set(ad->sk, idx, val);
387     return 1;
388 }
389 
390 /*
391  * For a given CRYPTO_EX_DATA_ variable, get the value corresponding to a
392  * particular index in the class used by this variable
393  */
CRYPTO_get_ex_data(const CRYPTO_EX_DATA * ad,int idx)394 void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx)
395 {
396     if (ad->sk == NULL || idx >= sk_void_num(ad->sk))
397         return NULL;
398     return sk_void_value(ad->sk, idx);
399 }
400