1109998Smarkm/* ====================================================================
2109998Smarkm * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
3109998Smarkm *
4109998Smarkm * Redistribution and use in source and binary forms, with or without
5109998Smarkm * modification, are permitted provided that the following conditions
6109998Smarkm * are met:
7109998Smarkm *
8109998Smarkm * 1. Redistributions of source code must retain the above copyright
9280304Sjkim *    notice, this list of conditions and the following disclaimer.
10109998Smarkm *
11109998Smarkm * 2. Redistributions in binary form must reproduce the above copyright
12109998Smarkm *    notice, this list of conditions and the following disclaimer in
13109998Smarkm *    the documentation and/or other materials provided with the
14109998Smarkm *    distribution.
15109998Smarkm *
16109998Smarkm * 3. All advertising materials mentioning features or use of this
17109998Smarkm *    software must display the following acknowledgment:
18109998Smarkm *    "This product includes software developed by the OpenSSL Project
19109998Smarkm *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
20109998Smarkm *
21109998Smarkm * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
22109998Smarkm *    endorse or promote products derived from this software without
23109998Smarkm *    prior written permission. For written permission, please contact
24109998Smarkm *    licensing@OpenSSL.org.
25109998Smarkm *
26109998Smarkm * 5. Products derived from this software may not be called "OpenSSL"
27109998Smarkm *    nor may "OpenSSL" appear in their names without prior written
28109998Smarkm *    permission of the OpenSSL Project.
29109998Smarkm *
30109998Smarkm * 6. Redistributions of any form whatsoever must retain the following
31109998Smarkm *    acknowledgment:
32109998Smarkm *    "This product includes software developed by the OpenSSL Project
33109998Smarkm *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
34109998Smarkm *
35109998Smarkm * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
36109998Smarkm * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37109998Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38109998Smarkm * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
39109998Smarkm * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40109998Smarkm * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
41109998Smarkm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42109998Smarkm * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43109998Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44109998Smarkm * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45109998Smarkm * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
46109998Smarkm * OF THE POSSIBILITY OF SUCH DAMAGE.
47109998Smarkm * ====================================================================
48109998Smarkm *
49109998Smarkm * This product includes cryptographic software written by Eric Young
50109998Smarkm * (eay@cryptsoft.com).  This product includes software written by Tim
51109998Smarkm * Hudson (tjh@cryptsoft.com).
52109998Smarkm *
53109998Smarkm */
54109998Smarkm
55160814Ssimon#include "cryptlib.h"
56109998Smarkm#include <openssl/evp.h>
57160814Ssimon#include <openssl/lhash.h>
58109998Smarkm#include "eng_int.h"
59109998Smarkm
60109998Smarkm/* The type of the items in the table */
61280304Sjkimtypedef struct st_engine_pile {
62280304Sjkim    /* The 'nid' of this algorithm/mode */
63280304Sjkim    int nid;
64280304Sjkim    /* ENGINEs that implement this algorithm/mode. */
65280304Sjkim    STACK_OF(ENGINE) *sk;
66280304Sjkim    /* The default ENGINE to perform this algorithm/mode. */
67280304Sjkim    ENGINE *funct;
68280304Sjkim    /*
69280304Sjkim     * Zero if 'sk' is newer than the cached 'funct', non-zero otherwise
70280304Sjkim     */
71280304Sjkim    int uptodate;
72280304Sjkim} ENGINE_PILE;
73109998Smarkm
74238405SjkimDECLARE_LHASH_OF(ENGINE_PILE);
75238405Sjkim
76160814Ssimon/* The type exposed in eng_int.h */
77280304Sjkimstruct st_engine_table {
78280304Sjkim    LHASH_OF(ENGINE_PILE) piles;
79280304Sjkim};                              /* ENGINE_TABLE */
80109998Smarkm
81280304Sjkimtypedef struct st_engine_pile_doall {
82280304Sjkim    engine_table_doall_cb *cb;
83280304Sjkim    void *arg;
84280304Sjkim} ENGINE_PILE_DOALL;
85238405Sjkim
86160814Ssimon/* Global flags (ENGINE_TABLE_FLAG_***). */
87109998Smarkmstatic unsigned int table_flags = 0;
88109998Smarkm
89109998Smarkm/* API function manipulating 'table_flags' */
90109998Smarkmunsigned int ENGINE_get_table_flags(void)
91280304Sjkim{
92280304Sjkim    return table_flags;
93280304Sjkim}
94238405Sjkim
95109998Smarkmvoid ENGINE_set_table_flags(unsigned int flags)
96280304Sjkim{
97280304Sjkim    table_flags = flags;
98280304Sjkim}
99109998Smarkm
100109998Smarkm/* Internal functions for the "piles" hash table */
101109998Smarkmstatic unsigned long engine_pile_hash(const ENGINE_PILE *c)
102280304Sjkim{
103280304Sjkim    return c->nid;
104280304Sjkim}
105238405Sjkim
106109998Smarkmstatic int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b)
107280304Sjkim{
108280304Sjkim    return a->nid - b->nid;
109280304Sjkim}
110280304Sjkim
111238405Sjkimstatic IMPLEMENT_LHASH_HASH_FN(engine_pile, ENGINE_PILE)
112238405Sjkimstatic IMPLEMENT_LHASH_COMP_FN(engine_pile, ENGINE_PILE)
113238405Sjkim
114109998Smarkmstatic int int_table_check(ENGINE_TABLE **t, int create)
115280304Sjkim{
116280304Sjkim    LHASH_OF(ENGINE_PILE) *lh;
117238405Sjkim
118280304Sjkim    if (*t)
119280304Sjkim        return 1;
120280304Sjkim    if (!create)
121280304Sjkim        return 0;
122280304Sjkim    if ((lh = lh_ENGINE_PILE_new()) == NULL)
123280304Sjkim        return 0;
124280304Sjkim    *t = (ENGINE_TABLE *)lh;
125280304Sjkim    return 1;
126280304Sjkim}
127109998Smarkm
128280304Sjkim/*
129280304Sjkim * Privately exposed (via eng_int.h) functions for adding and/or removing
130280304Sjkim * ENGINEs from the implementation table
131280304Sjkim */
132109998Smarkmint engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup,
133280304Sjkim                          ENGINE *e, const int *nids, int num_nids,
134280304Sjkim                          int setdefault)
135280304Sjkim{
136280304Sjkim    int ret = 0, added = 0;
137280304Sjkim    ENGINE_PILE tmplate, *fnd;
138280304Sjkim    CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
139280304Sjkim    if (!(*table))
140280304Sjkim        added = 1;
141280304Sjkim    if (!int_table_check(table, 1))
142280304Sjkim        goto end;
143280304Sjkim    if (added)
144280304Sjkim        /* The cleanup callback needs to be added */
145280304Sjkim        engine_cleanup_add_first(cleanup);
146280304Sjkim    while (num_nids--) {
147280304Sjkim        tmplate.nid = *nids;
148280304Sjkim        fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
149280304Sjkim        if (!fnd) {
150280304Sjkim            fnd = OPENSSL_malloc(sizeof(ENGINE_PILE));
151280304Sjkim            if (!fnd)
152280304Sjkim                goto end;
153280304Sjkim            fnd->uptodate = 1;
154280304Sjkim            fnd->nid = *nids;
155280304Sjkim            fnd->sk = sk_ENGINE_new_null();
156280304Sjkim            if (!fnd->sk) {
157280304Sjkim                OPENSSL_free(fnd);
158280304Sjkim                goto end;
159280304Sjkim            }
160280304Sjkim            fnd->funct = NULL;
161280304Sjkim            (void)lh_ENGINE_PILE_insert(&(*table)->piles, fnd);
162280304Sjkim        }
163280304Sjkim        /* A registration shouldn't add duplciate entries */
164280304Sjkim        (void)sk_ENGINE_delete_ptr(fnd->sk, e);
165280304Sjkim        /*
166280304Sjkim         * if 'setdefault', this ENGINE goes to the head of the list
167280304Sjkim         */
168280304Sjkim        if (!sk_ENGINE_push(fnd->sk, e))
169280304Sjkim            goto end;
170280304Sjkim        /* "touch" this ENGINE_PILE */
171280304Sjkim        fnd->uptodate = 0;
172280304Sjkim        if (setdefault) {
173280304Sjkim            if (!engine_unlocked_init(e)) {
174280304Sjkim                ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER,
175280304Sjkim                          ENGINE_R_INIT_FAILED);
176280304Sjkim                goto end;
177280304Sjkim            }
178280304Sjkim            if (fnd->funct)
179280304Sjkim                engine_unlocked_finish(fnd->funct, 0);
180280304Sjkim            fnd->funct = e;
181280304Sjkim            fnd->uptodate = 1;
182280304Sjkim        }
183280304Sjkim        nids++;
184280304Sjkim    }
185280304Sjkim    ret = 1;
186280304Sjkim end:
187280304Sjkim    CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
188280304Sjkim    return ret;
189280304Sjkim}
190280304Sjkim
191238405Sjkimstatic void int_unregister_cb_doall_arg(ENGINE_PILE *pile, ENGINE *e)
192280304Sjkim{
193280304Sjkim    int n;
194280304Sjkim    /* Iterate the 'c->sk' stack removing any occurance of 'e' */
195280304Sjkim    while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) {
196280304Sjkim        (void)sk_ENGINE_delete(pile->sk, n);
197280304Sjkim        pile->uptodate = 0;
198280304Sjkim    }
199280304Sjkim    if (pile->funct == e) {
200280304Sjkim        engine_unlocked_finish(e, 0);
201280304Sjkim        pile->funct = NULL;
202280304Sjkim    }
203280304Sjkim}
204280304Sjkim
205238405Sjkimstatic IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb, ENGINE_PILE, ENGINE)
206238405Sjkim
207109998Smarkmvoid engine_table_unregister(ENGINE_TABLE **table, ENGINE *e)
208280304Sjkim{
209280304Sjkim    CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
210280304Sjkim    if (int_table_check(table, 0))
211280304Sjkim        lh_ENGINE_PILE_doall_arg(&(*table)->piles,
212280304Sjkim                                 LHASH_DOALL_ARG_FN(int_unregister_cb),
213280304Sjkim                                 ENGINE, e);
214280304Sjkim    CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
215280304Sjkim}
216109998Smarkm
217238405Sjkimstatic void int_cleanup_cb_doall(ENGINE_PILE *p)
218280304Sjkim{
219280304Sjkim    sk_ENGINE_free(p->sk);
220280304Sjkim    if (p->funct)
221280304Sjkim        engine_unlocked_finish(p->funct, 0);
222280304Sjkim    OPENSSL_free(p);
223280304Sjkim}
224280304Sjkim
225238405Sjkimstatic IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb, ENGINE_PILE)
226238405Sjkim
227109998Smarkmvoid engine_table_cleanup(ENGINE_TABLE **table)
228280304Sjkim{
229280304Sjkim    CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
230280304Sjkim    if (*table) {
231280304Sjkim        lh_ENGINE_PILE_doall(&(*table)->piles,
232280304Sjkim                             LHASH_DOALL_FN(int_cleanup_cb));
233280304Sjkim        lh_ENGINE_PILE_free(&(*table)->piles);
234280304Sjkim        *table = NULL;
235280304Sjkim    }
236280304Sjkim    CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
237280304Sjkim}
238109998Smarkm
239160814Ssimon/* return a functional reference for a given 'nid' */
240109998Smarkm#ifndef ENGINE_TABLE_DEBUG
241109998SmarkmENGINE *engine_table_select(ENGINE_TABLE **table, int nid)
242109998Smarkm#else
243280304SjkimENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f,
244280304Sjkim                                int l)
245109998Smarkm#endif
246280304Sjkim{
247280304Sjkim    ENGINE *ret = NULL;
248280304Sjkim    ENGINE_PILE tmplate, *fnd = NULL;
249280304Sjkim    int initres, loop = 0;
250109998Smarkm
251280304Sjkim    if (!(*table)) {
252109998Smarkm#ifdef ENGINE_TABLE_DEBUG
253280304Sjkim        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing "
254280304Sjkim                "registered!\n", f, l, nid);
255109998Smarkm#endif
256280304Sjkim        return NULL;
257280304Sjkim    }
258280304Sjkim    ERR_set_mark();
259280304Sjkim    CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
260280304Sjkim    /*
261280304Sjkim     * Check again inside the lock otherwise we could race against cleanup
262280304Sjkim     * operations. But don't worry about a fprintf(stderr).
263280304Sjkim     */
264280304Sjkim    if (!int_table_check(table, 0))
265280304Sjkim        goto end;
266280304Sjkim    tmplate.nid = nid;
267280304Sjkim    fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
268280304Sjkim    if (!fnd)
269280304Sjkim        goto end;
270280304Sjkim    if (fnd->funct && engine_unlocked_init(fnd->funct)) {
271109998Smarkm#ifdef ENGINE_TABLE_DEBUG
272280304Sjkim        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
273280304Sjkim                "ENGINE '%s' cached\n", f, l, nid, fnd->funct->id);
274109998Smarkm#endif
275280304Sjkim        ret = fnd->funct;
276280304Sjkim        goto end;
277280304Sjkim    }
278280304Sjkim    if (fnd->uptodate) {
279280304Sjkim        ret = fnd->funct;
280280304Sjkim        goto end;
281280304Sjkim    }
282280304Sjkim trynext:
283280304Sjkim    ret = sk_ENGINE_value(fnd->sk, loop++);
284280304Sjkim    if (!ret) {
285109998Smarkm#ifdef ENGINE_TABLE_DEBUG
286280304Sjkim        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no "
287280304Sjkim                "registered implementations would initialise\n", f, l, nid);
288109998Smarkm#endif
289280304Sjkim        goto end;
290280304Sjkim    }
291280304Sjkim    /* Try to initialise the ENGINE? */
292280304Sjkim    if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT))
293280304Sjkim        initres = engine_unlocked_init(ret);
294280304Sjkim    else
295280304Sjkim        initres = 0;
296280304Sjkim    if (initres) {
297280304Sjkim        /* Update 'funct' */
298280304Sjkim        if ((fnd->funct != ret) && engine_unlocked_init(ret)) {
299280304Sjkim            /* If there was a previous default we release it. */
300280304Sjkim            if (fnd->funct)
301280304Sjkim                engine_unlocked_finish(fnd->funct, 0);
302280304Sjkim            fnd->funct = ret;
303109998Smarkm#ifdef ENGINE_TABLE_DEBUG
304280304Sjkim            fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, "
305280304Sjkim                    "setting default to '%s'\n", f, l, nid, ret->id);
306109998Smarkm#endif
307280304Sjkim        }
308109998Smarkm#ifdef ENGINE_TABLE_DEBUG
309280304Sjkim        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
310280304Sjkim                "newly initialised '%s'\n", f, l, nid, ret->id);
311109998Smarkm#endif
312280304Sjkim        goto end;
313280304Sjkim    }
314280304Sjkim    goto trynext;
315280304Sjkim end:
316280304Sjkim    /*
317280304Sjkim     * If it failed, it is unlikely to succeed again until some future
318280304Sjkim     * registrations have taken place. In all cases, we cache.
319280304Sjkim     */
320280304Sjkim    if (fnd)
321280304Sjkim        fnd->uptodate = 1;
322109998Smarkm#ifdef ENGINE_TABLE_DEBUG
323280304Sjkim    if (ret)
324280304Sjkim        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
325280304Sjkim                "ENGINE '%s'\n", f, l, nid, ret->id);
326280304Sjkim    else
327280304Sjkim        fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
328280304Sjkim                "'no matching ENGINE'\n", f, l, nid);
329109998Smarkm#endif
330280304Sjkim    CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
331280304Sjkim    /*
332280304Sjkim     * Whatever happened, any failed init()s are not failures in this
333280304Sjkim     * context, so clear our error state.
334280304Sjkim     */
335280304Sjkim    ERR_pop_to_mark();
336280304Sjkim    return ret;
337280304Sjkim}
338238405Sjkim
339238405Sjkim/* Table enumeration */
340238405Sjkim
341238405Sjkimstatic void int_cb_doall_arg(ENGINE_PILE *pile, ENGINE_PILE_DOALL *dall)
342280304Sjkim{
343280304Sjkim    dall->cb(pile->nid, pile->sk, pile->funct, dall->arg);
344280304Sjkim}
345238405Sjkim
346280304Sjkimstatic IMPLEMENT_LHASH_DOALL_ARG_FN(int_cb, ENGINE_PILE, ENGINE_PILE_DOALL)
347280304Sjkim
348238405Sjkimvoid engine_table_doall(ENGINE_TABLE *table, engine_table_doall_cb *cb,
349280304Sjkim                        void *arg)
350280304Sjkim{
351280304Sjkim    ENGINE_PILE_DOALL dall;
352280304Sjkim    dall.cb = cb;
353280304Sjkim    dall.arg = arg;
354284285Sjkim    if (table)
355284285Sjkim        lh_ENGINE_PILE_doall_arg(&table->piles,
356284285Sjkim                                 LHASH_DOALL_ARG_FN(int_cb),
357284285Sjkim                                 ENGINE_PILE_DOALL, &dall);
358280304Sjkim}
359