1e1051a39Sopenharmony_ci/*
2e1051a39Sopenharmony_ci * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
3e1051a39Sopenharmony_ci *
4e1051a39Sopenharmony_ci * Licensed under the Apache License 2.0 (the "License").  You may not use
5e1051a39Sopenharmony_ci * this file except in compliance with the License.  You can obtain a copy
6e1051a39Sopenharmony_ci * in the file LICENSE in the source distribution or at
7e1051a39Sopenharmony_ci * https://www.openssl.org/source/license.html
8e1051a39Sopenharmony_ci */
9e1051a39Sopenharmony_ci
10e1051a39Sopenharmony_ci#include "internal/cryptlib.h"
11e1051a39Sopenharmony_ci#include <openssl/evp.h>
12e1051a39Sopenharmony_ci#include <openssl/lhash.h>
13e1051a39Sopenharmony_ci#include <openssl/trace.h>
14e1051a39Sopenharmony_ci#include "eng_local.h"
15e1051a39Sopenharmony_ci
16e1051a39Sopenharmony_ci/* The type of the items in the table */
17e1051a39Sopenharmony_cistruct st_engine_pile {
18e1051a39Sopenharmony_ci    /* The 'nid' of this algorithm/mode */
19e1051a39Sopenharmony_ci    int nid;
20e1051a39Sopenharmony_ci    /* ENGINEs that implement this algorithm/mode. */
21e1051a39Sopenharmony_ci    STACK_OF(ENGINE) *sk;
22e1051a39Sopenharmony_ci    /* The default ENGINE to perform this algorithm/mode. */
23e1051a39Sopenharmony_ci    ENGINE *funct;
24e1051a39Sopenharmony_ci    /*
25e1051a39Sopenharmony_ci     * Zero if 'sk' is newer than the cached 'funct', non-zero otherwise
26e1051a39Sopenharmony_ci     */
27e1051a39Sopenharmony_ci    int uptodate;
28e1051a39Sopenharmony_ci};
29e1051a39Sopenharmony_ci
30e1051a39Sopenharmony_ci/* The type exposed in eng_local.h */
31e1051a39Sopenharmony_cistruct st_engine_table {
32e1051a39Sopenharmony_ci    LHASH_OF(ENGINE_PILE) piles;
33e1051a39Sopenharmony_ci};                              /* ENGINE_TABLE */
34e1051a39Sopenharmony_ci
35e1051a39Sopenharmony_citypedef struct st_engine_pile_doall {
36e1051a39Sopenharmony_ci    engine_table_doall_cb *cb;
37e1051a39Sopenharmony_ci    void *arg;
38e1051a39Sopenharmony_ci} ENGINE_PILE_DOALL;
39e1051a39Sopenharmony_ci
40e1051a39Sopenharmony_ci/* Global flags (ENGINE_TABLE_FLAG_***). */
41e1051a39Sopenharmony_cistatic unsigned int table_flags = 0;
42e1051a39Sopenharmony_ci
43e1051a39Sopenharmony_ci/* API function manipulating 'table_flags' */
44e1051a39Sopenharmony_ciunsigned int ENGINE_get_table_flags(void)
45e1051a39Sopenharmony_ci{
46e1051a39Sopenharmony_ci    return table_flags;
47e1051a39Sopenharmony_ci}
48e1051a39Sopenharmony_ci
49e1051a39Sopenharmony_civoid ENGINE_set_table_flags(unsigned int flags)
50e1051a39Sopenharmony_ci{
51e1051a39Sopenharmony_ci    table_flags = flags;
52e1051a39Sopenharmony_ci}
53e1051a39Sopenharmony_ci
54e1051a39Sopenharmony_ci/* Internal functions for the "piles" hash table */
55e1051a39Sopenharmony_cistatic unsigned long engine_pile_hash(const ENGINE_PILE *c)
56e1051a39Sopenharmony_ci{
57e1051a39Sopenharmony_ci    return c->nid;
58e1051a39Sopenharmony_ci}
59e1051a39Sopenharmony_ci
60e1051a39Sopenharmony_cistatic int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b)
61e1051a39Sopenharmony_ci{
62e1051a39Sopenharmony_ci    return a->nid - b->nid;
63e1051a39Sopenharmony_ci}
64e1051a39Sopenharmony_ci
65e1051a39Sopenharmony_cistatic int int_table_check(ENGINE_TABLE **t, int create)
66e1051a39Sopenharmony_ci{
67e1051a39Sopenharmony_ci    LHASH_OF(ENGINE_PILE) *lh;
68e1051a39Sopenharmony_ci
69e1051a39Sopenharmony_ci    if (*t)
70e1051a39Sopenharmony_ci        return 1;
71e1051a39Sopenharmony_ci    if (!create)
72e1051a39Sopenharmony_ci        return 0;
73e1051a39Sopenharmony_ci    if ((lh = lh_ENGINE_PILE_new(engine_pile_hash, engine_pile_cmp)) == NULL)
74e1051a39Sopenharmony_ci        return 0;
75e1051a39Sopenharmony_ci    *t = (ENGINE_TABLE *)lh;
76e1051a39Sopenharmony_ci    return 1;
77e1051a39Sopenharmony_ci}
78e1051a39Sopenharmony_ci
79e1051a39Sopenharmony_ci/*
80e1051a39Sopenharmony_ci * Privately exposed (via eng_local.h) functions for adding and/or removing
81e1051a39Sopenharmony_ci * ENGINEs from the implementation table
82e1051a39Sopenharmony_ci */
83e1051a39Sopenharmony_ciint engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup,
84e1051a39Sopenharmony_ci                          ENGINE *e, const int *nids, int num_nids,
85e1051a39Sopenharmony_ci                          int setdefault)
86e1051a39Sopenharmony_ci{
87e1051a39Sopenharmony_ci    int ret = 0, added = 0;
88e1051a39Sopenharmony_ci    ENGINE_PILE tmplate, *fnd;
89e1051a39Sopenharmony_ci
90e1051a39Sopenharmony_ci    if (!CRYPTO_THREAD_write_lock(global_engine_lock))
91e1051a39Sopenharmony_ci        return 0;
92e1051a39Sopenharmony_ci    if (!(*table))
93e1051a39Sopenharmony_ci        added = 1;
94e1051a39Sopenharmony_ci    if (!int_table_check(table, 1))
95e1051a39Sopenharmony_ci        goto end;
96e1051a39Sopenharmony_ci    if (added)
97e1051a39Sopenharmony_ci        /* The cleanup callback needs to be added */
98e1051a39Sopenharmony_ci        engine_cleanup_add_first(cleanup);
99e1051a39Sopenharmony_ci    while (num_nids--) {
100e1051a39Sopenharmony_ci        tmplate.nid = *nids;
101e1051a39Sopenharmony_ci        fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
102e1051a39Sopenharmony_ci        if (!fnd) {
103e1051a39Sopenharmony_ci            fnd = OPENSSL_malloc(sizeof(*fnd));
104e1051a39Sopenharmony_ci            if (fnd == NULL)
105e1051a39Sopenharmony_ci                goto end;
106e1051a39Sopenharmony_ci            fnd->uptodate = 1;
107e1051a39Sopenharmony_ci            fnd->nid = *nids;
108e1051a39Sopenharmony_ci            fnd->sk = sk_ENGINE_new_null();
109e1051a39Sopenharmony_ci            if (!fnd->sk) {
110e1051a39Sopenharmony_ci                OPENSSL_free(fnd);
111e1051a39Sopenharmony_ci                goto end;
112e1051a39Sopenharmony_ci            }
113e1051a39Sopenharmony_ci            fnd->funct = NULL;
114e1051a39Sopenharmony_ci            (void)lh_ENGINE_PILE_insert(&(*table)->piles, fnd);
115e1051a39Sopenharmony_ci            if (lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate) != fnd) {
116e1051a39Sopenharmony_ci                sk_ENGINE_free(fnd->sk);
117e1051a39Sopenharmony_ci                OPENSSL_free(fnd);
118e1051a39Sopenharmony_ci                goto end;
119e1051a39Sopenharmony_ci            }
120e1051a39Sopenharmony_ci        }
121e1051a39Sopenharmony_ci        /* A registration shouldn't add duplicate entries */
122e1051a39Sopenharmony_ci        (void)sk_ENGINE_delete_ptr(fnd->sk, e);
123e1051a39Sopenharmony_ci        /*
124e1051a39Sopenharmony_ci         * if 'setdefault', this ENGINE goes to the head of the list
125e1051a39Sopenharmony_ci         */
126e1051a39Sopenharmony_ci        if (!sk_ENGINE_push(fnd->sk, e))
127e1051a39Sopenharmony_ci            goto end;
128e1051a39Sopenharmony_ci        /* "touch" this ENGINE_PILE */
129e1051a39Sopenharmony_ci        fnd->uptodate = 0;
130e1051a39Sopenharmony_ci        if (setdefault) {
131e1051a39Sopenharmony_ci            if (!engine_unlocked_init(e)) {
132e1051a39Sopenharmony_ci                ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INIT_FAILED);
133e1051a39Sopenharmony_ci                goto end;
134e1051a39Sopenharmony_ci            }
135e1051a39Sopenharmony_ci            if (fnd->funct)
136e1051a39Sopenharmony_ci                engine_unlocked_finish(fnd->funct, 0);
137e1051a39Sopenharmony_ci            fnd->funct = e;
138e1051a39Sopenharmony_ci            fnd->uptodate = 1;
139e1051a39Sopenharmony_ci        }
140e1051a39Sopenharmony_ci        nids++;
141e1051a39Sopenharmony_ci    }
142e1051a39Sopenharmony_ci    ret = 1;
143e1051a39Sopenharmony_ci end:
144e1051a39Sopenharmony_ci    CRYPTO_THREAD_unlock(global_engine_lock);
145e1051a39Sopenharmony_ci    return ret;
146e1051a39Sopenharmony_ci}
147e1051a39Sopenharmony_ci
148e1051a39Sopenharmony_cistatic void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e)
149e1051a39Sopenharmony_ci{
150e1051a39Sopenharmony_ci    int n;
151e1051a39Sopenharmony_ci    /* Iterate the 'c->sk' stack removing any occurrence of 'e' */
152e1051a39Sopenharmony_ci    while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) {
153e1051a39Sopenharmony_ci        (void)sk_ENGINE_delete(pile->sk, n);
154e1051a39Sopenharmony_ci        pile->uptodate = 0;
155e1051a39Sopenharmony_ci    }
156e1051a39Sopenharmony_ci    if (pile->funct == e) {
157e1051a39Sopenharmony_ci        engine_unlocked_finish(e, 0);
158e1051a39Sopenharmony_ci        pile->funct = NULL;
159e1051a39Sopenharmony_ci    }
160e1051a39Sopenharmony_ci}
161e1051a39Sopenharmony_ci
162e1051a39Sopenharmony_ciIMPLEMENT_LHASH_DOALL_ARG(ENGINE_PILE, ENGINE);
163e1051a39Sopenharmony_ci
164e1051a39Sopenharmony_civoid engine_table_unregister(ENGINE_TABLE **table, ENGINE *e)
165e1051a39Sopenharmony_ci{
166e1051a39Sopenharmony_ci    if (!CRYPTO_THREAD_write_lock(global_engine_lock))
167e1051a39Sopenharmony_ci        /* Can't return a value. :( */
168e1051a39Sopenharmony_ci        return;
169e1051a39Sopenharmony_ci    if (int_table_check(table, 0))
170e1051a39Sopenharmony_ci        lh_ENGINE_PILE_doall_ENGINE(&(*table)->piles, int_unregister_cb, e);
171e1051a39Sopenharmony_ci    CRYPTO_THREAD_unlock(global_engine_lock);
172e1051a39Sopenharmony_ci}
173e1051a39Sopenharmony_ci
174e1051a39Sopenharmony_cistatic void int_cleanup_cb_doall(ENGINE_PILE *p)
175e1051a39Sopenharmony_ci{
176e1051a39Sopenharmony_ci    if (p == NULL)
177e1051a39Sopenharmony_ci        return;
178e1051a39Sopenharmony_ci    sk_ENGINE_free(p->sk);
179e1051a39Sopenharmony_ci    if (p->funct)
180e1051a39Sopenharmony_ci        engine_unlocked_finish(p->funct, 0);
181e1051a39Sopenharmony_ci    OPENSSL_free(p);
182e1051a39Sopenharmony_ci}
183e1051a39Sopenharmony_ci
184e1051a39Sopenharmony_civoid engine_table_cleanup(ENGINE_TABLE **table)
185e1051a39Sopenharmony_ci{
186e1051a39Sopenharmony_ci    if (!CRYPTO_THREAD_write_lock(global_engine_lock))
187e1051a39Sopenharmony_ci        return;
188e1051a39Sopenharmony_ci    if (*table) {
189e1051a39Sopenharmony_ci        lh_ENGINE_PILE_doall(&(*table)->piles, int_cleanup_cb_doall);
190e1051a39Sopenharmony_ci        lh_ENGINE_PILE_free(&(*table)->piles);
191e1051a39Sopenharmony_ci        *table = NULL;
192e1051a39Sopenharmony_ci    }
193e1051a39Sopenharmony_ci    CRYPTO_THREAD_unlock(global_engine_lock);
194e1051a39Sopenharmony_ci}
195e1051a39Sopenharmony_ci
196e1051a39Sopenharmony_ci/* return a functional reference for a given 'nid' */
197e1051a39Sopenharmony_ciENGINE *ossl_engine_table_select(ENGINE_TABLE **table, int nid,
198e1051a39Sopenharmony_ci                                 const char *f, int l)
199e1051a39Sopenharmony_ci{
200e1051a39Sopenharmony_ci    ENGINE *ret = NULL;
201e1051a39Sopenharmony_ci    ENGINE_PILE tmplate, *fnd = NULL;
202e1051a39Sopenharmony_ci    int initres, loop = 0;
203e1051a39Sopenharmony_ci
204e1051a39Sopenharmony_ci    /* Load the config before trying to check if engines are available */
205e1051a39Sopenharmony_ci    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
206e1051a39Sopenharmony_ci
207e1051a39Sopenharmony_ci    if (!(*table)) {
208e1051a39Sopenharmony_ci        OSSL_TRACE3(ENGINE_TABLE,
209e1051a39Sopenharmony_ci                   "%s:%d, nid=%d, nothing registered!\n",
210e1051a39Sopenharmony_ci                   f, l, nid);
211e1051a39Sopenharmony_ci        return NULL;
212e1051a39Sopenharmony_ci    }
213e1051a39Sopenharmony_ci    ERR_set_mark();
214e1051a39Sopenharmony_ci    if (!CRYPTO_THREAD_write_lock(global_engine_lock))
215e1051a39Sopenharmony_ci        goto end;
216e1051a39Sopenharmony_ci    /*
217e1051a39Sopenharmony_ci     * Check again inside the lock otherwise we could race against cleanup
218e1051a39Sopenharmony_ci     * operations. But don't worry about a debug printout
219e1051a39Sopenharmony_ci     */
220e1051a39Sopenharmony_ci    if (!int_table_check(table, 0))
221e1051a39Sopenharmony_ci        goto end;
222e1051a39Sopenharmony_ci    tmplate.nid = nid;
223e1051a39Sopenharmony_ci    fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
224e1051a39Sopenharmony_ci    if (!fnd)
225e1051a39Sopenharmony_ci        goto end;
226e1051a39Sopenharmony_ci    if (fnd->funct && engine_unlocked_init(fnd->funct)) {
227e1051a39Sopenharmony_ci        OSSL_TRACE4(ENGINE_TABLE,
228e1051a39Sopenharmony_ci                   "%s:%d, nid=%d, using ENGINE '%s' cached\n",
229e1051a39Sopenharmony_ci                   f, l, nid, fnd->funct->id);
230e1051a39Sopenharmony_ci        ret = fnd->funct;
231e1051a39Sopenharmony_ci        goto end;
232e1051a39Sopenharmony_ci    }
233e1051a39Sopenharmony_ci    if (fnd->uptodate) {
234e1051a39Sopenharmony_ci        ret = fnd->funct;
235e1051a39Sopenharmony_ci        goto end;
236e1051a39Sopenharmony_ci    }
237e1051a39Sopenharmony_ci trynext:
238e1051a39Sopenharmony_ci    ret = sk_ENGINE_value(fnd->sk, loop++);
239e1051a39Sopenharmony_ci    if (!ret) {
240e1051a39Sopenharmony_ci        OSSL_TRACE3(ENGINE_TABLE,
241e1051a39Sopenharmony_ci                    "%s:%d, nid=%d, "
242e1051a39Sopenharmony_ci                    "no registered implementations would initialise\n",
243e1051a39Sopenharmony_ci                    f, l, nid);
244e1051a39Sopenharmony_ci        goto end;
245e1051a39Sopenharmony_ci    }
246e1051a39Sopenharmony_ci    /* Try to initialise the ENGINE? */
247e1051a39Sopenharmony_ci    if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT))
248e1051a39Sopenharmony_ci        initres = engine_unlocked_init(ret);
249e1051a39Sopenharmony_ci    else
250e1051a39Sopenharmony_ci        initres = 0;
251e1051a39Sopenharmony_ci    if (initres) {
252e1051a39Sopenharmony_ci        /* Update 'funct' */
253e1051a39Sopenharmony_ci        if ((fnd->funct != ret) && engine_unlocked_init(ret)) {
254e1051a39Sopenharmony_ci            /* If there was a previous default we release it. */
255e1051a39Sopenharmony_ci            if (fnd->funct)
256e1051a39Sopenharmony_ci                engine_unlocked_finish(fnd->funct, 0);
257e1051a39Sopenharmony_ci            fnd->funct = ret;
258e1051a39Sopenharmony_ci            OSSL_TRACE4(ENGINE_TABLE,
259e1051a39Sopenharmony_ci                        "%s:%d, nid=%d, setting default to '%s'\n",
260e1051a39Sopenharmony_ci                        f, l, nid, ret->id);
261e1051a39Sopenharmony_ci        }
262e1051a39Sopenharmony_ci        OSSL_TRACE4(ENGINE_TABLE,
263e1051a39Sopenharmony_ci                    "%s:%d, nid=%d, using newly initialised '%s'\n",
264e1051a39Sopenharmony_ci                    f, l, nid, ret->id);
265e1051a39Sopenharmony_ci        goto end;
266e1051a39Sopenharmony_ci    }
267e1051a39Sopenharmony_ci    goto trynext;
268e1051a39Sopenharmony_ci end:
269e1051a39Sopenharmony_ci    /*
270e1051a39Sopenharmony_ci     * If it failed, it is unlikely to succeed again until some future
271e1051a39Sopenharmony_ci     * registrations have taken place. In all cases, we cache.
272e1051a39Sopenharmony_ci     */
273e1051a39Sopenharmony_ci    if (fnd)
274e1051a39Sopenharmony_ci        fnd->uptodate = 1;
275e1051a39Sopenharmony_ci    if (ret)
276e1051a39Sopenharmony_ci        OSSL_TRACE4(ENGINE_TABLE,
277e1051a39Sopenharmony_ci                   "%s:%d, nid=%d, caching ENGINE '%s'\n",
278e1051a39Sopenharmony_ci                   f, l, nid, ret->id);
279e1051a39Sopenharmony_ci    else
280e1051a39Sopenharmony_ci        OSSL_TRACE3(ENGINE_TABLE,
281e1051a39Sopenharmony_ci                    "%s:%d, nid=%d, caching 'no matching ENGINE'\n",
282e1051a39Sopenharmony_ci                    f, l, nid);
283e1051a39Sopenharmony_ci    CRYPTO_THREAD_unlock(global_engine_lock);
284e1051a39Sopenharmony_ci    /*
285e1051a39Sopenharmony_ci     * Whatever happened, any failed init()s are not failures in this
286e1051a39Sopenharmony_ci     * context, so clear our error state.
287e1051a39Sopenharmony_ci     */
288e1051a39Sopenharmony_ci    ERR_pop_to_mark();
289e1051a39Sopenharmony_ci    return ret;
290e1051a39Sopenharmony_ci}
291e1051a39Sopenharmony_ci
292e1051a39Sopenharmony_ci/* Table enumeration */
293e1051a39Sopenharmony_ci
294e1051a39Sopenharmony_cistatic void int_dall(const ENGINE_PILE *pile, ENGINE_PILE_DOALL *dall)
295e1051a39Sopenharmony_ci{
296e1051a39Sopenharmony_ci    dall->cb(pile->nid, pile->sk, pile->funct, dall->arg);
297e1051a39Sopenharmony_ci}
298e1051a39Sopenharmony_ci
299e1051a39Sopenharmony_ciIMPLEMENT_LHASH_DOALL_ARG_CONST(ENGINE_PILE, ENGINE_PILE_DOALL);
300e1051a39Sopenharmony_ci
301e1051a39Sopenharmony_civoid engine_table_doall(ENGINE_TABLE *table, engine_table_doall_cb *cb,
302e1051a39Sopenharmony_ci                        void *arg)
303e1051a39Sopenharmony_ci{
304e1051a39Sopenharmony_ci    ENGINE_PILE_DOALL dall;
305e1051a39Sopenharmony_ci    dall.cb = cb;
306e1051a39Sopenharmony_ci    dall.arg = arg;
307e1051a39Sopenharmony_ci    if (table)
308e1051a39Sopenharmony_ci        lh_ENGINE_PILE_doall_ENGINE_PILE_DOALL(&table->piles, int_dall, &dall);
309e1051a39Sopenharmony_ci}
310