Coverage Report

Created: 2023-05-06 07:01

/src/openssl/crypto/ex_data.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 1995-2018 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
28
{
15
28
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
16
17
28
    if (global == NULL)
18
0
        return 0;
19
20
28
    global->ex_data_lock = CRYPTO_THREAD_lock_new();
21
28
    return global->ex_data_lock != NULL;
22
28
}
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
 */
28
static EX_CALLBACKS *get_and_lock(OPENSSL_CTX *ctx, int class_index)
29
42.8k
{
30
42.8k
    EX_CALLBACKS *ip;
31
42.8k
    OSSL_EX_DATA_GLOBAL *global = NULL;
32
33
42.8k
    if (class_index < 0 || class_index >= CRYPTO_EX_INDEX__COUNT) {
34
0
        CRYPTOerr(CRYPTO_F_GET_AND_LOCK, ERR_R_PASSED_INVALID_ARGUMENT);
35
0
        return NULL;
36
0
    }
37
38
42.8k
    global = openssl_ctx_get_ex_data_global(ctx);
39
42.8k
    if (global == NULL || global->ex_data_lock == NULL) {
40
        /*
41
         * If we get here, someone (who?) cleaned up the lock, so just
42
         * treat it as an error.
43
         */
44
0
         return NULL;
45
0
    }
46
47
42.8k
    CRYPTO_THREAD_write_lock(global->ex_data_lock);
48
42.8k
    ip = &global->ex_data[class_index];
49
42.8k
    return ip;
50
42.8k
}
51
52
static void cleanup_cb(EX_CALLBACK *funcs)
53
16
{
54
16
    OPENSSL_free(funcs);
55
16
}
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 crypto_cleanup_all_ex_data_int(OPENSSL_CTX *ctx)
64
16
{
65
16
    int i;
66
16
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
67
68
16
    if (global == NULL)
69
0
        return;
70
71
288
    for (i = 0; i < CRYPTO_EX_INDEX__COUNT; ++i) {
72
272
        EX_CALLBACKS *ip = &global->ex_data[i];
73
74
272
        sk_EX_CALLBACK_pop_free(ip->meth, cleanup_cb);
75
272
        ip->meth = NULL;
76
272
    }
77
78
16
    CRYPTO_THREAD_lock_free(global->ex_data_lock);
79
16
    global->ex_data_lock = NULL;
80
16
}
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
0
{
90
0
}
91
92
static void dummy_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
93
                       long argl, void *argp)
94
0
{
95
0
}
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
0
{
101
0
    return 1;
102
0
}
103
104
int crypto_free_ex_index_ex(OPENSSL_CTX *ctx, int class_index, int idx)
105
28
{
106
28
    EX_CALLBACKS *ip;
107
28
    EX_CALLBACK *a;
108
28
    int toret = 0;
109
28
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
110
111
28
    if (global == NULL)
112
0
        return 0;
113
114
28
    ip = get_and_lock(ctx, class_index);
115
28
    if (ip == NULL)
116
0
        return 0;
117
28
    if (idx < 0 || idx >= sk_EX_CALLBACK_num(ip->meth))
118
28
        goto err;
119
0
    a = sk_EX_CALLBACK_value(ip->meth, idx);
120
0
    if (a == NULL)
121
0
        goto err;
122
0
    a->new_func = dummy_new;
123
0
    a->dup_func = dummy_dup;
124
0
    a->free_func = dummy_free;
125
0
    toret = 1;
126
28
err:
127
28
    CRYPTO_THREAD_unlock(global->ex_data_lock);
128
28
    return toret;
129
0
}
130
131
int CRYPTO_free_ex_index(int class_index, int idx)
132
28
{
133
28
    return crypto_free_ex_index_ex(NULL, class_index, idx);
134
28
}
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
28
{
144
28
    int toret = -1;
145
28
    EX_CALLBACK *a;
146
28
    EX_CALLBACKS *ip;
147
28
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
148
149
28
    if (global == NULL)
150
0
        return -1;
151
152
28
    ip = get_and_lock(ctx, class_index);
153
28
    if (ip == NULL)
154
0
        return -1;
155
156
28
    if (ip->meth == NULL) {
157
28
        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
28
        if (ip->meth == NULL
161
28
            || !sk_EX_CALLBACK_push(ip->meth, NULL)) {
162
0
            CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX_EX, ERR_R_MALLOC_FAILURE);
163
0
            goto err;
164
0
        }
165
28
    }
166
167
28
    a = (EX_CALLBACK *)OPENSSL_malloc(sizeof(*a));
168
28
    if (a == NULL) {
169
0
        CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX_EX, ERR_R_MALLOC_FAILURE);
170
0
        goto err;
171
0
    }
172
28
    a->argl = argl;
173
28
    a->argp = argp;
174
28
    a->new_func = new_func;
175
28
    a->dup_func = dup_func;
176
28
    a->free_func = free_func;
177
178
28
    if (!sk_EX_CALLBACK_push(ip->meth, NULL)) {
179
0
        CRYPTOerr(CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX_EX, ERR_R_MALLOC_FAILURE);
180
0
        OPENSSL_free(a);
181
0
        goto err;
182
0
    }
183
28
    toret = sk_EX_CALLBACK_num(ip->meth) - 1;
184
28
    (void)sk_EX_CALLBACK_set(ip->meth, toret, a);
185
186
28
 err:
187
28
    CRYPTO_THREAD_unlock(global->ex_data_lock);
188
28
    return toret;
189
28
}
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
0
{
195
0
    return crypto_get_ex_new_index_ex(NULL, class_index, argl, argp, new_func,
196
0
                                      dup_func, free_func);
197
0
}
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
21.4k
{
209
21.4k
    int mx, i;
210
21.4k
    void *ptr;
211
21.4k
    EX_CALLBACK **storage = NULL;
212
21.4k
    EX_CALLBACK *stack[10];
213
21.4k
    EX_CALLBACKS *ip;
214
21.4k
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
215
216
21.4k
    if (global == NULL)
217
0
        return 0;
218
219
21.4k
    ip = get_and_lock(ctx, class_index);
220
21.4k
    if (ip == NULL)
221
0
        return 0;
222
223
21.4k
    ad->ctx = ctx;
224
21.4k
    ad->sk = NULL;
225
226
21.4k
    mx = sk_EX_CALLBACK_num(ip->meth);
227
21.4k
    if (mx > 0) {
228
0
        if (mx < (int)OSSL_NELEM(stack))
229
0
            storage = stack;
230
0
        else
231
0
            storage = OPENSSL_malloc(sizeof(*storage) * mx);
232
0
        if (storage != NULL)
233
0
            for (i = 0; i < mx; i++)
234
0
                storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
235
0
    }
236
21.4k
    CRYPTO_THREAD_unlock(global->ex_data_lock);
237
238
21.4k
    if (mx > 0 && storage == NULL) {
239
0
        CRYPTOerr(CRYPTO_F_CRYPTO_NEW_EX_DATA_EX, ERR_R_MALLOC_FAILURE);
240
0
        return 0;
241
0
    }
242
21.4k
    for (i = 0; i < mx; i++) {
243
0
        if (storage[i] != NULL && storage[i]->new_func != NULL) {
244
0
            ptr = CRYPTO_get_ex_data(ad, i);
245
0
            storage[i]->new_func(obj, ptr, ad, i,
246
0
                                 storage[i]->argl, storage[i]->argp);
247
0
        }
248
0
    }
249
21.4k
    if (storage != stack)
250
21.4k
        OPENSSL_free(storage);
251
21.4k
    return 1;
252
21.4k
}
253
254
int CRYPTO_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
255
21.2k
{
256
21.2k
    return crypto_new_ex_data_ex(NULL, class_index, obj, ad);
257
21.2k
}
258
259
/*
260
 * Duplicate a CRYPTO_EX_DATA variable - including calling dup() callbacks
261
 * for each index in the class used by this variable
262
 */
263
int CRYPTO_dup_ex_data(int class_index, CRYPTO_EX_DATA *to,
264
                       const CRYPTO_EX_DATA *from)
265
0
{
266
0
    int mx, j, i;
267
0
    void *ptr;
268
0
    EX_CALLBACK *stack[10];
269
0
    EX_CALLBACK **storage = NULL;
270
0
    EX_CALLBACKS *ip;
271
0
    int toret = 0;
272
0
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(from->ctx);
273
274
0
    if (global == NULL)
275
0
        return 0;
276
277
0
    to->ctx = from->ctx;
278
0
    if (from->sk == NULL)
279
        /* Nothing to copy over */
280
0
        return 1;
281
0
    if ((ip = get_and_lock(from->ctx, class_index)) == NULL)
282
0
        return 0;
283
284
0
    mx = sk_EX_CALLBACK_num(ip->meth);
285
0
    j = sk_void_num(from->sk);
286
0
    if (j < mx)
287
0
        mx = j;
288
0
    if (mx > 0) {
289
0
        if (mx < (int)OSSL_NELEM(stack))
290
0
            storage = stack;
291
0
        else
292
0
            storage = OPENSSL_malloc(sizeof(*storage) * mx);
293
0
        if (storage != NULL)
294
0
            for (i = 0; i < mx; i++)
295
0
                storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
296
0
    }
297
0
    CRYPTO_THREAD_unlock(global->ex_data_lock);
298
299
0
    if (mx == 0)
300
0
        return 1;
301
0
    if (storage == NULL) {
302
0
        CRYPTOerr(CRYPTO_F_CRYPTO_DUP_EX_DATA, ERR_R_MALLOC_FAILURE);
303
0
        return 0;
304
0
    }
305
    /*
306
     * Make sure the ex_data stack is at least |mx| elements long to avoid
307
     * issues in the for loop that follows; so go get the |mx|'th element
308
     * (if it does not exist CRYPTO_get_ex_data() returns NULL), and assign
309
     * to itself. This is normally a no-op; but ensures the stack is the
310
     * proper size
311
     */
312
0
    if (!CRYPTO_set_ex_data(to, mx - 1, CRYPTO_get_ex_data(to, mx - 1)))
313
0
        goto err;
314
315
0
    for (i = 0; i < mx; i++) {
316
0
        ptr = CRYPTO_get_ex_data(from, i);
317
0
        if (storage[i] != NULL && storage[i]->dup_func != NULL)
318
0
            if (!storage[i]->dup_func(to, from, &ptr, i,
319
0
                                      storage[i]->argl, storage[i]->argp))
320
0
                goto err;
321
0
        CRYPTO_set_ex_data(to, i, ptr);
322
0
    }
323
0
    toret = 1;
324
0
 err:
325
0
    if (storage != stack)
326
0
        OPENSSL_free(storage);
327
0
    return toret;
328
0
}
329
330
331
/*
332
 * Cleanup a CRYPTO_EX_DATA variable - including calling free() callbacks for
333
 * each index in the class used by this variable
334
 */
335
void CRYPTO_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
336
21.3k
{
337
21.3k
    int mx, i;
338
21.3k
    EX_CALLBACKS *ip;
339
21.3k
    void *ptr;
340
21.3k
    EX_CALLBACK *f;
341
21.3k
    EX_CALLBACK *stack[10];
342
21.3k
    EX_CALLBACK **storage = NULL;
343
21.3k
    OSSL_EX_DATA_GLOBAL *global;
344
345
21.3k
    if ((ip = get_and_lock(ad->ctx, class_index)) == NULL)
346
0
        goto err;
347
21.3k
    global = openssl_ctx_get_ex_data_global(ad->ctx);
348
21.3k
    if (global == NULL)
349
0
        goto err;
350
351
21.3k
    mx = sk_EX_CALLBACK_num(ip->meth);
352
21.3k
    if (mx > 0) {
353
16
        if (mx < (int)OSSL_NELEM(stack))
354
16
            storage = stack;
355
0
        else
356
0
            storage = OPENSSL_malloc(sizeof(*storage) * mx);
357
16
        if (storage != NULL)
358
48
            for (i = 0; i < mx; i++)
359
32
                storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
360
16
    }
361
21.3k
    CRYPTO_THREAD_unlock(global->ex_data_lock);
362
363
21.4k
    for (i = 0; i < mx; i++) {
364
32
        if (storage != NULL)
365
32
            f = storage[i];
366
0
        else {
367
0
            CRYPTO_THREAD_write_lock(global->ex_data_lock);
368
0
            f = sk_EX_CALLBACK_value(ip->meth, i);
369
0
            CRYPTO_THREAD_unlock(global->ex_data_lock);
370
0
        }
371
32
        if (f != NULL && f->free_func != NULL) {
372
16
            ptr = CRYPTO_get_ex_data(ad, i);
373
16
            f->free_func(obj, ptr, ad, i, f->argl, f->argp);
374
16
        }
375
32
    }
376
377
21.3k
    if (storage != stack)
378
21.3k
        OPENSSL_free(storage);
379
21.3k
 err:
380
21.3k
    sk_void_free(ad->sk);
381
21.3k
    ad->sk = NULL;
382
21.3k
    ad->ctx = NULL;
383
21.3k
}
384
385
/*
386
 * Allocate a given CRYPTO_EX_DATA item using the class specific allocation
387
 * function
388
 */
389
int CRYPTO_alloc_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad,
390
                         int idx)
391
28
{
392
28
    EX_CALLBACK *f;
393
28
    EX_CALLBACKS *ip;
394
28
    void *curval;
395
28
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ad->ctx);
396
397
28
    if (global == NULL)
398
0
        return 0;
399
400
28
    curval = CRYPTO_get_ex_data(ad, idx);
401
402
    /* Already there, no need to allocate */
403
28
    if (curval != NULL)
404
0
        return 1;
405
406
28
    ip = get_and_lock(ad->ctx, class_index);
407
28
    if (ip == NULL)
408
0
        return 0;
409
28
    f = sk_EX_CALLBACK_value(ip->meth, idx);
410
28
    CRYPTO_THREAD_unlock(global->ex_data_lock);
411
412
    /*
413
     * This should end up calling CRYPTO_set_ex_data(), which allocates
414
     * everything necessary to support placing the new data in the right spot.
415
     */
416
28
    if (f->new_func == NULL)
417
0
        return 0;
418
419
28
    f->new_func(obj, NULL, ad, idx, f->argl, f->argp);
420
421
28
    return 1;
422
28
}
423
424
/*
425
 * For a given CRYPTO_EX_DATA variable, set the value corresponding to a
426
 * particular index in the class used by this variable
427
 */
428
int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val)
429
28
{
430
28
    int i;
431
432
28
    if (ad->sk == NULL) {
433
28
        if ((ad->sk = sk_void_new_null()) == NULL) {
434
0
            CRYPTOerr(CRYPTO_F_CRYPTO_SET_EX_DATA, ERR_R_MALLOC_FAILURE);
435
0
            return 0;
436
0
        }
437
28
    }
438
439
84
    for (i = sk_void_num(ad->sk); i <= idx; ++i) {
440
56
        if (!sk_void_push(ad->sk, NULL)) {
441
0
            CRYPTOerr(CRYPTO_F_CRYPTO_SET_EX_DATA, ERR_R_MALLOC_FAILURE);
442
0
            return 0;
443
0
        }
444
56
    }
445
28
    sk_void_set(ad->sk, idx, val);
446
28
    return 1;
447
28
}
448
449
/*
450
 * For a given CRYPTO_EX_DATA_ variable, get the value corresponding to a
451
 * particular index in the class used by this variable
452
 */
453
void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx)
454
324
{
455
324
    if (ad->sk == NULL || idx >= sk_void_num(ad->sk))
456
28
        return NULL;
457
296
    return sk_void_value(ad->sk, idx);
458
324
}
459
460
OPENSSL_CTX *crypto_ex_data_get_openssl_ctx(const CRYPTO_EX_DATA *ad)
461
28
{
462
28
    return ad->ctx;
463
28
}