Coverage Report

Created: 2024-06-09 08:57

/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
7.52k
{
15
7.52k
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
16
17
7.52k
    if (global == NULL)
18
0
        return 0;
19
20
7.52k
    global->ex_data_lock = CRYPTO_THREAD_lock_new();
21
7.52k
    return global->ex_data_lock != NULL;
22
7.52k
}
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
389k
{
30
389k
    EX_CALLBACKS *ip;
31
389k
    OSSL_EX_DATA_GLOBAL *global = NULL;
32
33
389k
    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
389k
    global = openssl_ctx_get_ex_data_global(ctx);
39
389k
    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
389k
    CRYPTO_THREAD_write_lock(global->ex_data_lock);
48
389k
    ip = &global->ex_data[class_index];
49
389k
    return ip;
50
389k
}
51
52
static void cleanup_cb(EX_CALLBACK *funcs)
53
3.76k
{
54
3.76k
    OPENSSL_free(funcs);
55
3.76k
}
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
3.76k
{
65
3.76k
    int i;
66
3.76k
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
67
68
3.76k
    if (global == NULL)
69
0
        return;
70
71
67.8k
    for (i = 0; i < CRYPTO_EX_INDEX__COUNT; ++i) {
72
64.0k
        EX_CALLBACKS *ip = &global->ex_data[i];
73
74
64.0k
        sk_EX_CALLBACK_pop_free(ip->meth, cleanup_cb);
75
64.0k
        ip->meth = NULL;
76
64.0k
    }
77
78
3.76k
    CRYPTO_THREAD_lock_free(global->ex_data_lock);
79
3.76k
    global->ex_data_lock = NULL;
80
3.76k
}
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
7.52k
{
106
7.52k
    EX_CALLBACKS *ip;
107
7.52k
    EX_CALLBACK *a;
108
7.52k
    int toret = 0;
109
7.52k
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
110
111
7.52k
    if (global == NULL)
112
0
        return 0;
113
114
7.52k
    ip = get_and_lock(ctx, class_index);
115
7.52k
    if (ip == NULL)
116
0
        return 0;
117
7.52k
    if (idx < 0 || idx >= sk_EX_CALLBACK_num(ip->meth))
118
7.52k
        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
7.52k
err:
127
7.52k
    CRYPTO_THREAD_unlock(global->ex_data_lock);
128
7.52k
    return toret;
129
0
}
130
131
int CRYPTO_free_ex_index(int class_index, int idx)
132
7.52k
{
133
7.52k
    return crypto_free_ex_index_ex(NULL, class_index, idx);
134
7.52k
}
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
7.52k
{
144
7.52k
    int toret = -1;
145
7.52k
    EX_CALLBACK *a;
146
7.52k
    EX_CALLBACKS *ip;
147
7.52k
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
148
149
7.52k
    if (global == NULL)
150
0
        return -1;
151
152
7.52k
    ip = get_and_lock(ctx, class_index);
153
7.52k
    if (ip == NULL)
154
0
        return -1;
155
156
7.52k
    if (ip->meth == NULL) {
157
7.52k
        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
7.52k
        if (ip->meth == NULL
161
7.52k
            || !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
7.52k
    }
166
167
7.52k
    a = (EX_CALLBACK *)OPENSSL_malloc(sizeof(*a));
168
7.52k
    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
7.52k
    a->argl = argl;
173
7.52k
    a->argp = argp;
174
7.52k
    a->new_func = new_func;
175
7.52k
    a->dup_func = dup_func;
176
7.52k
    a->free_func = free_func;
177
178
7.52k
    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
7.52k
    toret = sk_EX_CALLBACK_num(ip->meth) - 1;
184
7.52k
    (void)sk_EX_CALLBACK_set(ip->meth, toret, a);
185
186
7.52k
 err:
187
7.52k
    CRYPTO_THREAD_unlock(global->ex_data_lock);
188
7.52k
    return toret;
189
7.52k
}
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
187k
{
209
187k
    int mx, i;
210
187k
    void *ptr;
211
187k
    EX_CALLBACK **storage = NULL;
212
187k
    EX_CALLBACK *stack[10];
213
187k
    EX_CALLBACKS *ip;
214
187k
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ctx);
215
216
187k
    if (global == NULL)
217
0
        return 0;
218
219
187k
    ip = get_and_lock(ctx, class_index);
220
187k
    if (ip == NULL)
221
0
        return 0;
222
223
187k
    ad->ctx = ctx;
224
187k
    ad->sk = NULL;
225
226
187k
    mx = sk_EX_CALLBACK_num(ip->meth);
227
187k
    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
187k
    CRYPTO_THREAD_unlock(global->ex_data_lock);
237
238
187k
    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
187k
    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
187k
    if (storage != stack)
250
187k
        OPENSSL_free(storage);
251
187k
    return 1;
252
187k
}
253
254
int CRYPTO_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
255
179k
{
256
179k
    return crypto_new_ex_data_ex(NULL, class_index, obj, ad);
257
179k
}
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
179k
{
337
179k
    int mx, i;
338
179k
    EX_CALLBACKS *ip;
339
179k
    void *ptr;
340
179k
    EX_CALLBACK *f;
341
179k
    EX_CALLBACK *stack[10];
342
179k
    EX_CALLBACK **storage = NULL;
343
179k
    OSSL_EX_DATA_GLOBAL *global;
344
345
179k
    if ((ip = get_and_lock(ad->ctx, class_index)) == NULL)
346
0
        goto err;
347
179k
    global = openssl_ctx_get_ex_data_global(ad->ctx);
348
179k
    if (global == NULL)
349
0
        goto err;
350
351
179k
    mx = sk_EX_CALLBACK_num(ip->meth);
352
179k
    if (mx > 0) {
353
3.76k
        if (mx < (int)OSSL_NELEM(stack))
354
3.76k
            storage = stack;
355
0
        else
356
0
            storage = OPENSSL_malloc(sizeof(*storage) * mx);
357
3.76k
        if (storage != NULL)
358
11.3k
            for (i = 0; i < mx; i++)
359
7.53k
                storage[i] = sk_EX_CALLBACK_value(ip->meth, i);
360
3.76k
    }
361
179k
    CRYPTO_THREAD_unlock(global->ex_data_lock);
362
363
186k
    for (i = 0; i < mx; i++) {
364
7.53k
        if (storage != NULL)
365
7.53k
            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
7.53k
        if (f != NULL && f->free_func != NULL) {
372
3.76k
            ptr = CRYPTO_get_ex_data(ad, i);
373
3.76k
            f->free_func(obj, ptr, ad, i, f->argl, f->argp);
374
3.76k
        }
375
7.53k
    }
376
377
179k
    if (storage != stack)
378
175k
        OPENSSL_free(storage);
379
179k
 err:
380
179k
    sk_void_free(ad->sk);
381
179k
    ad->sk = NULL;
382
179k
    ad->ctx = NULL;
383
179k
}
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
7.52k
{
392
7.52k
    EX_CALLBACK *f;
393
7.52k
    EX_CALLBACKS *ip;
394
7.52k
    void *curval;
395
7.52k
    OSSL_EX_DATA_GLOBAL *global = openssl_ctx_get_ex_data_global(ad->ctx);
396
397
7.52k
    if (global == NULL)
398
0
        return 0;
399
400
7.52k
    curval = CRYPTO_get_ex_data(ad, idx);
401
402
    /* Already there, no need to allocate */
403
7.52k
    if (curval != NULL)
404
0
        return 1;
405
406
7.52k
    ip = get_and_lock(ad->ctx, class_index);
407
7.52k
    if (ip == NULL)
408
0
        return 0;
409
7.52k
    f = sk_EX_CALLBACK_value(ip->meth, idx);
410
7.52k
    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
7.52k
    if (f->new_func == NULL)
417
0
        return 0;
418
419
7.52k
    f->new_func(obj, NULL, ad, idx, f->argl, f->argp);
420
421
7.52k
    return 1;
422
7.52k
}
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
7.52k
{
430
7.52k
    int i;
431
432
7.52k
    if (ad->sk == NULL) {
433
7.52k
        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
7.52k
    }
438
439
22.5k
    for (i = sk_void_num(ad->sk); i <= idx; ++i) {
440
15.0k
        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
15.0k
    }
445
7.52k
    sk_void_set(ad->sk, idx, val);
446
7.52k
    return 1;
447
7.52k
}
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
86.4k
{
455
86.4k
    if (ad->sk == NULL || idx >= sk_void_num(ad->sk))
456
7.52k
        return NULL;
457
78.9k
    return sk_void_value(ad->sk, idx);
458
86.4k
}
459
460
OPENSSL_CTX *crypto_ex_data_get_openssl_ctx(const CRYPTO_EX_DATA *ad)
461
7.52k
{
462
7.52k
    return ad->ctx;
463
7.52k
}