VirtualBox

source: vbox/trunk/src/libs/openssl-3.1.3/crypto/ex_data.c@ 102427

Last change on this file since 102427 was 101211, checked in by vboxsync, 17 months ago

openssl-3.1.3: Applied and adjusted our OpenSSL changes to 3.1.2. bugref:10527

File size: 14.2 KB
Line 
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
14int 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 */
31static 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
61static 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 */
72void 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/*
93 * Unregister a new index by replacing the callbacks with no-ops.
94 * Any in-use instances are leaked.
95 */
96static void dummy_new(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
97 long argl, void *argp)
98{
99}
100
101static void dummy_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
102 long argl, void *argp)
103{
104}
105
106static int dummy_dup(CRYPTO_EX_DATA *to, const CRYPTO_EX_DATA *from,
107 void **from_d, int idx,
108 long argl, void *argp)
109{
110 return 1;
111}
112
113int ossl_crypto_free_ex_index_ex(OSSL_LIB_CTX *ctx, int class_index, int idx)
114{
115 EX_CALLBACKS *ip;
116 EX_CALLBACK *a;
117 int toret = 0;
118 OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx);
119
120 if (global == NULL)
121 return 0;
122
123 ip = get_and_lock(global, class_index, 0);
124 if (ip == NULL)
125 return 0;
126
127 if (idx < 0 || idx >= sk_EX_CALLBACK_num(ip->meth))
128 goto err;
129 a = sk_EX_CALLBACK_value(ip->meth, idx);
130 if (a == NULL)
131 goto err;
132 a->new_func = dummy_new;
133 a->dup_func = dummy_dup;
134 a->free_func = dummy_free;
135 toret = 1;
136err:
137 CRYPTO_THREAD_unlock(global->ex_data_lock);
138 return toret;
139}
140
141int CRYPTO_free_ex_index(int class_index, int idx)
142{
143 return ossl_crypto_free_ex_index_ex(NULL, class_index, idx);
144}
145
146/*
147 * Register a new index.
148 */
149int ossl_crypto_get_ex_new_index_ex(OSSL_LIB_CTX *ctx, int class_index,
150 long argl, void *argp,
151 CRYPTO_EX_new *new_func,
152 CRYPTO_EX_dup *dup_func,
153 CRYPTO_EX_free *free_func,
154 int priority)
155{
156 int toret = -1;
157 EX_CALLBACK *a;
158 EX_CALLBACKS *ip;
159 OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx);
160
161 if (global == NULL)
162 return -1;
163
164 ip = get_and_lock(global, class_index, 0);
165 if (ip == NULL)
166 return -1;
167
168 if (ip->meth == NULL) {
169 ip->meth = sk_EX_CALLBACK_new_null();
170 /* We push an initial value on the stack because the SSL
171 * "app_data" routines use ex_data index zero. See RT 3710. */
172 if (ip->meth == NULL
173 || !sk_EX_CALLBACK_push(ip->meth, NULL)) {
174 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
175 goto err;
176 }
177 }
178
179 a = (EX_CALLBACK *)OPENSSL_malloc(sizeof(*a));
180 if (a == NULL) {
181 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
182 goto err;
183 }
184 a->argl = argl;
185 a->argp = argp;
186 a->new_func = new_func;
187 a->dup_func = dup_func;
188 a->free_func = free_func;
189 a->priority = priority;
190
191 if (!sk_EX_CALLBACK_push(ip->meth, NULL)) {
192 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
193 OPENSSL_free(a);
194 goto err;
195 }
196 toret = sk_EX_CALLBACK_num(ip->meth) - 1;
197 (void)sk_EX_CALLBACK_set(ip->meth, toret, a);
198
199 err:
200 CRYPTO_THREAD_unlock(global->ex_data_lock);
201 return toret;
202}
203
204int CRYPTO_get_ex_new_index(int class_index, long argl, void *argp,
205 CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
206 CRYPTO_EX_free *free_func)
207{
208 return ossl_crypto_get_ex_new_index_ex(NULL, class_index, argl, argp,
209 new_func, dup_func, free_func, 0);
210}
211
212/*
213 * Initialise a new CRYPTO_EX_DATA for use in a particular class - including
214 * calling new() callbacks for each index in the class used by this variable
215 * Thread-safe by copying a class's array of "EX_CALLBACK" entries
216 * in the lock, then using them outside the lock. Note this only applies
217 * to the global "ex_data" state (ie. class definitions), not 'ad' itself.
218 */
219int ossl_crypto_new_ex_data_ex(OSSL_LIB_CTX *ctx, int class_index, void *obj,
220 CRYPTO_EX_DATA *ad)
221{
222 int mx, i;
223 void *ptr;
224 EX_CALLBACK **storage = NULL;
225 EX_CALLBACK *stack[10];
226 EX_CALLBACKS *ip;
227 OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ctx);
228
229 if (global == NULL)
230 return 0;
231
232 ip = get_and_lock(global, class_index, 1);
233 if (ip == NULL)
234 return 0;
235
236 ad->ctx = ctx;
237 ad->sk = NULL;
238 mx = sk_EX_CALLBACK_num(ip->meth);
239 if (mx > 0) {
240 if (mx < (int)OSSL_NELEM(stack))
241 storage = stack;
242 else
243 storage = OPENSSL_malloc(sizeof(*storage) * mx);
244 if (storage != NULL)
245 for (i = 0; i < mx; i++)
246 storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
247 }
248 CRYPTO_THREAD_unlock(global->ex_data_lock);
249
250 if (mx > 0 && storage == NULL) {
251 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
252 return 0;
253 }
254 for (i = 0; i < mx; i++) {
255 if (storage[i] != NULL && storage[i]->new_func != NULL) {
256 ptr = CRYPTO_get_ex_data(ad, i);
257 storage[i]->new_func(obj, ptr, ad, i,
258 storage[i]->argl, storage[i]->argp);
259 }
260 }
261 if (storage != stack)
262 OPENSSL_free(storage);
263 return 1;
264}
265
266int CRYPTO_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
267{
268 return ossl_crypto_new_ex_data_ex(NULL, class_index, obj, ad);
269}
270
271/*
272 * Duplicate a CRYPTO_EX_DATA variable - including calling dup() callbacks
273 * for each index in the class used by this variable
274 */
275int CRYPTO_dup_ex_data(int class_index, CRYPTO_EX_DATA *to,
276 const CRYPTO_EX_DATA *from)
277{
278 int mx, j, i;
279 void *ptr;
280 EX_CALLBACK *stack[10];
281 EX_CALLBACK **storage = NULL;
282 EX_CALLBACKS *ip;
283 int toret = 0;
284 OSSL_EX_DATA_GLOBAL *global;
285
286 to->ctx = from->ctx;
287 if (from->sk == NULL)
288 /* Nothing to copy over */
289 return 1;
290
291 global = ossl_lib_ctx_get_ex_data_global(from->ctx);
292 if (global == NULL)
293 return 0;
294
295 ip = get_and_lock(global, class_index, 1);
296 if (ip == NULL)
297 return 0;
298
299 mx = sk_EX_CALLBACK_num(ip->meth);
300 j = sk_void_num(from->sk);
301 if (j < mx)
302 mx = j;
303 if (mx > 0) {
304 if (mx < (int)OSSL_NELEM(stack))
305 storage = stack;
306 else
307 storage = OPENSSL_malloc(sizeof(*storage) * mx);
308 if (storage != NULL)
309 for (i = 0; i < mx; i++)
310 storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
311 }
312 CRYPTO_THREAD_unlock(global->ex_data_lock);
313
314 if (mx == 0)
315 return 1;
316 if (storage == NULL) {
317 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
318 return 0;
319 }
320 /*
321 * Make sure the ex_data stack is at least |mx| elements long to avoid
322 * issues in the for loop that follows; so go get the |mx|'th element
323 * (if it does not exist CRYPTO_get_ex_data() returns NULL), and assign
324 * to itself. This is normally a no-op; but ensures the stack is the
325 * proper size
326 */
327 if (!CRYPTO_set_ex_data(to, mx - 1, CRYPTO_get_ex_data(to, mx - 1)))
328 goto err;
329
330 for (i = 0; i < mx; i++) {
331 ptr = CRYPTO_get_ex_data(from, i);
332 if (storage[i] != NULL && storage[i]->dup_func != NULL)
333 if (!storage[i]->dup_func(to, from, &ptr, i,
334 storage[i]->argl, storage[i]->argp))
335 goto err;
336 CRYPTO_set_ex_data(to, i, ptr);
337 }
338 toret = 1;
339 err:
340 if (storage != stack)
341 OPENSSL_free(storage);
342 return toret;
343}
344
345struct ex_callback_entry {
346 const EX_CALLBACK *excb;
347 int index;
348};
349
350static int ex_callback_compare(const void *a, const void *b)
351{
352 const struct ex_callback_entry *ap = (const struct ex_callback_entry *)a;
353 const struct ex_callback_entry *bp = (const struct ex_callback_entry *)b;
354
355 if (ap->excb == bp->excb)
356 return 0;
357
358 if (ap->excb == NULL)
359 return 1;
360 if (bp->excb == NULL)
361 return -1;
362 if (ap->excb->priority == bp->excb->priority)
363 return 0;
364 return ap->excb->priority > bp->excb->priority ? -1 : 1;
365}
366
367/*
368 * Cleanup a CRYPTO_EX_DATA variable - including calling free() callbacks for
369 * each index in the class used by this variable
370 */
371void CRYPTO_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
372{
373 int mx, i;
374 EX_CALLBACKS *ip;
375 void *ptr;
376 const EX_CALLBACK *f;
377 struct ex_callback_entry stack[10];
378 struct ex_callback_entry *storage = NULL;
379 OSSL_EX_DATA_GLOBAL *global = ossl_lib_ctx_get_ex_data_global(ad->ctx);
380
381 if (global == NULL)
382 goto err;
383
384 ip = get_and_lock(global, class_index, 1);
385 if (ip == NULL)
386 goto err;
387
388 mx = sk_EX_CALLBACK_num(ip->meth);
389 if (mx > 0) {
390 if (mx < (int)OSSL_NELEM(stack))
391 storage = stack;
392 else
393 storage = OPENSSL_malloc(sizeof(*storage) * mx);
394 if (storage != NULL)
395 for (i = 0; i < mx; i++) {
396 storage[i].excb = sk_EX_CALLBACK_value(ip->meth, i);
397 storage[i].index = i;
398 }
399 }
400 CRYPTO_THREAD_unlock(global->ex_data_lock);
401
402 if (storage != NULL) {
403 /* Sort according to priority. High priority first */
404 qsort(storage, mx, sizeof(*storage), ex_callback_compare);
405 for (i = 0; i < mx; i++) {
406 f = storage[i].excb;
407
408 if (f != NULL && f->free_func != NULL) {
409 ptr = CRYPTO_get_ex_data(ad, storage[i].index);
410 f->free_func(obj, ptr, ad, storage[i].index, f->argl, f->argp);
411 }
412 }
413 }
414
415 if (storage != stack)
416 OPENSSL_free(storage);
417 err:
418 sk_void_free(ad->sk);
419 ad->sk = NULL;
420 ad->ctx = NULL;
421}
422
423/*
424 * Allocate a given CRYPTO_EX_DATA item using the class specific allocation
425 * function
426 */
427int CRYPTO_alloc_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad,
428 int idx)
429{
430 void *curval;
431
432 curval = CRYPTO_get_ex_data(ad, idx);
433 /* Already there, no need to allocate */
434 if (curval != NULL)
435 return 1;
436
437 return ossl_crypto_alloc_ex_data_intern(class_index, obj, ad, idx);
438}
439
440int ossl_crypto_alloc_ex_data_intern(int class_index, void *obj,
441 CRYPTO_EX_DATA *ad, int idx)
442{
443 EX_CALLBACK *f;
444 EX_CALLBACKS *ip;
445 OSSL_EX_DATA_GLOBAL *global;
446
447 global = ossl_lib_ctx_get_ex_data_global(ad->ctx);
448 if (global == NULL)
449 return 0;
450
451 ip = get_and_lock(global, class_index, 1);
452 if (ip == NULL)
453 return 0;
454 f = sk_EX_CALLBACK_value(ip->meth, idx);
455 CRYPTO_THREAD_unlock(global->ex_data_lock);
456
457 /*
458 * This should end up calling CRYPTO_set_ex_data(), which allocates
459 * everything necessary to support placing the new data in the right spot.
460 */
461 if (f->new_func == NULL)
462 return 0;
463
464 f->new_func(obj, NULL, ad, idx, f->argl, f->argp);
465
466 return 1;
467}
468
469/*
470 * For a given CRYPTO_EX_DATA variable, set the value corresponding to a
471 * particular index in the class used by this variable
472 */
473int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val)
474{
475 int i;
476
477 if (ad->sk == NULL) {
478 if ((ad->sk = sk_void_new_null()) == NULL) {
479 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
480 return 0;
481 }
482 }
483
484 for (i = sk_void_num(ad->sk); i <= idx; ++i) {
485 if (!sk_void_push(ad->sk, NULL)) {
486 ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
487 return 0;
488 }
489 }
490 if (sk_void_set(ad->sk, idx, val) != val) {
491 /* Probably the index is out of bounds */
492 ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
493 return 0;
494 }
495 return 1;
496}
497
498/*
499 * For a given CRYPTO_EX_DATA_ variable, get the value corresponding to a
500 * particular index in the class used by this variable
501 */
502void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx)
503{
504 if (ad->sk == NULL || idx >= sk_void_num(ad->sk))
505 return NULL;
506 return sk_void_value(ad->sk, idx);
507}
508
509OSSL_LIB_CTX *ossl_crypto_ex_data_get_ossl_lib_ctx(const CRYPTO_EX_DATA *ad)
510{
511 return ad->ctx;
512}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette