Coverage Report

Created: 2022-11-30 06:20

/src/openssl/crypto/engine/eng_table.c
Line
Count
Source (jump to first uncovered line)
1
/* ====================================================================
2
 * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
3
 *
4
 * Redistribution and use in source and binary forms, with or without
5
 * modification, are permitted provided that the following conditions
6
 * are met:
7
 *
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 *
11
 * 2. Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in
13
 *    the documentation and/or other materials provided with the
14
 *    distribution.
15
 *
16
 * 3. All advertising materials mentioning features or use of this
17
 *    software must display the following acknowledgment:
18
 *    "This product includes software developed by the OpenSSL Project
19
 *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
20
 *
21
 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
22
 *    endorse or promote products derived from this software without
23
 *    prior written permission. For written permission, please contact
24
 *    licensing@OpenSSL.org.
25
 *
26
 * 5. Products derived from this software may not be called "OpenSSL"
27
 *    nor may "OpenSSL" appear in their names without prior written
28
 *    permission of the OpenSSL Project.
29
 *
30
 * 6. Redistributions of any form whatsoever must retain the following
31
 *    acknowledgment:
32
 *    "This product includes software developed by the OpenSSL Project
33
 *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
34
 *
35
 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
36
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
39
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
41
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
46
 * OF THE POSSIBILITY OF SUCH DAMAGE.
47
 * ====================================================================
48
 *
49
 * This product includes cryptographic software written by Eric Young
50
 * (eay@cryptsoft.com).  This product includes software written by Tim
51
 * Hudson (tjh@cryptsoft.com).
52
 *
53
 */
54
55
#include "cryptlib.h"
56
#include <openssl/evp.h>
57
#include <openssl/lhash.h>
58
#include "eng_int.h"
59
60
/* The type of the items in the table */
61
typedef struct st_engine_pile {
62
    /* The 'nid' of this algorithm/mode */
63
    int nid;
64
    /* ENGINEs that implement this algorithm/mode. */
65
    STACK_OF(ENGINE) *sk;
66
    /* The default ENGINE to perform this algorithm/mode. */
67
    ENGINE *funct;
68
    /*
69
     * Zero if 'sk' is newer than the cached 'funct', non-zero otherwise
70
     */
71
    int uptodate;
72
} ENGINE_PILE;
73
74
DECLARE_LHASH_OF(ENGINE_PILE);
75
76
/* The type exposed in eng_int.h */
77
struct st_engine_table {
78
    LHASH_OF(ENGINE_PILE) piles;
79
};                              /* ENGINE_TABLE */
80
81
typedef struct st_engine_pile_doall {
82
    engine_table_doall_cb *cb;
83
    void *arg;
84
} ENGINE_PILE_DOALL;
85
86
/* Global flags (ENGINE_TABLE_FLAG_***). */
87
static unsigned int table_flags = 0;
88
89
/* API function manipulating 'table_flags' */
90
unsigned int ENGINE_get_table_flags(void)
91
0
{
92
0
    return table_flags;
93
0
}
94
95
void ENGINE_set_table_flags(unsigned int flags)
96
0
{
97
0
    table_flags = flags;
98
0
}
99
100
/* Internal functions for the "piles" hash table */
101
static unsigned long engine_pile_hash(const ENGINE_PILE *c)
102
83.2k
{
103
83.2k
    return c->nid;
104
83.2k
}
105
106
static int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b)
107
550
{
108
550
    return a->nid - b->nid;
109
550
}
110
111
static IMPLEMENT_LHASH_HASH_FN(engine_pile, ENGINE_PILE)
112
static IMPLEMENT_LHASH_COMP_FN(engine_pile, ENGINE_PILE)
113
114
static int int_table_check(ENGINE_TABLE **t, int create)
115
82.9k
{
116
82.9k
    LHASH_OF(ENGINE_PILE) *lh;
117
118
82.9k
    if (*t)
119
82.7k
        return 1;
120
133
    if (!create)
121
0
        return 0;
122
133
    if ((lh = lh_ENGINE_PILE_new()) == NULL)
123
0
        return 0;
124
133
    *t = (ENGINE_TABLE *)lh;
125
133
    return 1;
126
133
}
127
128
/*
129
 * Privately exposed (via eng_int.h) functions for adding and/or removing
130
 * ENGINEs from the implementation table
131
 */
132
int engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup,
133
                          ENGINE *e, const int *nids, int num_nids,
134
                          int setdefault)
135
589
{
136
589
    int ret = 0, added = 0;
137
589
    ENGINE_PILE tmplate, *fnd;
138
589
    CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
139
589
    if (!(*table))
140
133
        added = 1;
141
589
    if (!int_table_check(table, 1))
142
0
        goto end;
143
589
    if (added)
144
        /* The cleanup callback needs to be added */
145
133
        engine_cleanup_add_first(cleanup);
146
1.33k
    while (num_nids--) {
147
741
        tmplate.nid = *nids;
148
741
        fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
149
741
        if (!fnd) {
150
209
            fnd = OPENSSL_malloc(sizeof(ENGINE_PILE));
151
209
            if (!fnd)
152
0
                goto end;
153
209
            fnd->uptodate = 1;
154
209
            fnd->nid = *nids;
155
209
            fnd->sk = sk_ENGINE_new_null();
156
209
            if (!fnd->sk) {
157
0
                OPENSSL_free(fnd);
158
0
                goto end;
159
0
            }
160
209
            fnd->funct = NULL;
161
209
            (void)lh_ENGINE_PILE_insert(&(*table)->piles, fnd);
162
209
        }
163
        /* A registration shouldn't add duplciate entries */
164
741
        (void)sk_ENGINE_delete_ptr(fnd->sk, e);
165
        /*
166
         * if 'setdefault', this ENGINE goes to the head of the list
167
         */
168
741
        if (!sk_ENGINE_push(fnd->sk, e))
169
0
            goto end;
170
        /* "touch" this ENGINE_PILE */
171
741
        fnd->uptodate = 0;
172
741
        if (setdefault) {
173
0
            if (!engine_unlocked_init(e)) {
174
0
                ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER,
175
0
                          ENGINE_R_INIT_FAILED);
176
0
                goto end;
177
0
            }
178
0
            if (fnd->funct)
179
0
                engine_unlocked_finish(fnd->funct, 0);
180
0
            fnd->funct = e;
181
0
            fnd->uptodate = 1;
182
0
        }
183
741
        nids++;
184
741
    }
185
589
    ret = 1;
186
589
 end:
187
589
    CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
188
589
    return ret;
189
589
}
190
191
static void int_unregister_cb_doall_arg(ENGINE_PILE *pile, ENGINE *e)
192
0
{
193
0
    int n;
194
    /* Iterate the 'c->sk' stack removing any occurance of 'e' */
195
0
    while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) {
196
0
        (void)sk_ENGINE_delete(pile->sk, n);
197
0
        pile->uptodate = 0;
198
0
    }
199
0
    if (pile->funct == e) {
200
0
        engine_unlocked_finish(e, 0);
201
0
        pile->funct = NULL;
202
0
    }
203
0
}
204
205
static IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb, ENGINE_PILE, ENGINE)
206
207
void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e)
208
0
{
209
0
    CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
210
0
    if (int_table_check(table, 0))
211
0
        lh_ENGINE_PILE_doall_arg(&(*table)->piles,
212
0
                                 LHASH_DOALL_ARG_FN(int_unregister_cb),
213
0
                                 ENGINE, e);
214
0
    CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
215
0
}
216
217
static void int_cleanup_cb_doall(ENGINE_PILE *p)
218
0
{
219
0
    sk_ENGINE_free(p->sk);
220
0
    if (p->funct)
221
0
        engine_unlocked_finish(p->funct, 0);
222
0
    OPENSSL_free(p);
223
0
}
224
225
static IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb, ENGINE_PILE)
226
227
void engine_table_cleanup(ENGINE_TABLE **table)
228
0
{
229
0
    CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
230
0
    if (*table) {
231
0
        lh_ENGINE_PILE_doall(&(*table)->piles,
232
0
                             LHASH_DOALL_FN(int_cleanup_cb));
233
0
        lh_ENGINE_PILE_free(&(*table)->piles);
234
0
        *table = NULL;
235
0
    }
236
0
    CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
237
0
}
238
239
/* return a functional reference for a given 'nid' */
240
#ifndef ENGINE_TABLE_DEBUG
241
ENGINE *engine_table_select(ENGINE_TABLE **table, int nid)
242
#else
243
ENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f,
244
                                int l)
245
#endif
246
82.3k
{
247
82.3k
    ENGINE *ret = NULL;
248
82.3k
    ENGINE_PILE tmplate, *fnd = NULL;
249
82.3k
    int initres, loop = 0;
250
251
82.3k
    if (!(*table)) {
252
#ifdef ENGINE_TABLE_DEBUG
253
        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing "
254
                "registered!\n", f, l, nid);
255
#endif
256
0
        return NULL;
257
0
    }
258
82.3k
    ERR_set_mark();
259
82.3k
    CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
260
    /*
261
     * Check again inside the lock otherwise we could race against cleanup
262
     * operations. But don't worry about a fprintf(stderr).
263
     */
264
82.3k
    if (!int_table_check(table, 0))
265
0
        goto end;
266
82.3k
    tmplate.nid = nid;
267
82.3k
    fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
268
82.3k
    if (!fnd)
269
82.3k
        goto end;
270
18
    if (fnd->funct && engine_unlocked_init(fnd->funct)) {
271
#ifdef ENGINE_TABLE_DEBUG
272
        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
273
                "ENGINE '%s' cached\n", f, l, nid, fnd->funct->id);
274
#endif
275
0
        ret = fnd->funct;
276
0
        goto end;
277
0
    }
278
18
    if (fnd->uptodate) {
279
0
        ret = fnd->funct;
280
0
        goto end;
281
0
    }
282
90
 trynext:
283
90
    ret = sk_ENGINE_value(fnd->sk, loop++);
284
90
    if (!ret) {
285
#ifdef ENGINE_TABLE_DEBUG
286
        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no "
287
                "registered implementations would initialise\n", f, l, nid);
288
#endif
289
18
        goto end;
290
18
    }
291
    /* Try to initialise the ENGINE? */
292
72
    if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT))
293
72
        initres = engine_unlocked_init(ret);
294
0
    else
295
0
        initres = 0;
296
72
    if (initres) {
297
        /* Update 'funct' */
298
0
        if ((fnd->funct != ret) && engine_unlocked_init(ret)) {
299
            /* If there was a previous default we release it. */
300
0
            if (fnd->funct)
301
0
                engine_unlocked_finish(fnd->funct, 0);
302
0
            fnd->funct = ret;
303
#ifdef ENGINE_TABLE_DEBUG
304
            fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, "
305
                    "setting default to '%s'\n", f, l, nid, ret->id);
306
#endif
307
0
        }
308
#ifdef ENGINE_TABLE_DEBUG
309
        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
310
                "newly initialised '%s'\n", f, l, nid, ret->id);
311
#endif
312
0
        goto end;
313
0
    }
314
72
    goto trynext;
315
82.3k
 end:
316
    /*
317
     * If it failed, it is unlikely to succeed again until some future
318
     * registrations have taken place. In all cases, we cache.
319
     */
320
82.3k
    if (fnd)
321
18
        fnd->uptodate = 1;
322
#ifdef ENGINE_TABLE_DEBUG
323
    if (ret)
324
        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
325
                "ENGINE '%s'\n", f, l, nid, ret->id);
326
    else
327
        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
328
                "'no matching ENGINE'\n", f, l, nid);
329
#endif
330
82.3k
    CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
331
    /*
332
     * Whatever happened, any failed init()s are not failures in this
333
     * context, so clear our error state.
334
     */
335
82.3k
    ERR_pop_to_mark();
336
82.3k
    return ret;
337
72
}
338
339
/* Table enumeration */
340
341
static void int_cb_doall_arg(ENGINE_PILE *pile, ENGINE_PILE_DOALL *dall)
342
0
{
343
0
    dall->cb(pile->nid, pile->sk, pile->funct, dall->arg);
344
0
}
345
346
static IMPLEMENT_LHASH_DOALL_ARG_FN(int_cb, ENGINE_PILE, ENGINE_PILE_DOALL)
347
348
void engine_table_doall(ENGINE_TABLE *table, engine_table_doall_cb *cb,
349
                        void *arg)
350
4.99k
{
351
4.99k
    ENGINE_PILE_DOALL dall;
352
4.99k
    dall.cb = cb;
353
4.99k
    dall.arg = arg;
354
4.99k
    if (table)
355
4.99k
        lh_ENGINE_PILE_doall_arg(&table->piles,
356
4.99k
                                 LHASH_DOALL_ARG_FN(int_cb),
357
4.99k
                                 ENGINE_PILE_DOALL, &dall);
358
4.99k
}