| /* ==================================================================== |
| * Copyright (c) 2001 The OpenSSL Project. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. All advertising materials mentioning features or use of this |
| * software must display the following acknowledgment: |
| * "This product includes software developed by the OpenSSL Project |
| * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" |
| * |
| * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to |
| * endorse or promote products derived from this software without |
| * prior written permission. For written permission, please contact |
| * licensing@OpenSSL.org. |
| * |
| * 5. Products derived from this software may not be called "OpenSSL" |
| * nor may "OpenSSL" appear in their names without prior written |
| * permission of the OpenSSL Project. |
| * |
| * 6. Redistributions of any form whatsoever must retain the following |
| * acknowledgment: |
| * "This product includes software developed by the OpenSSL Project |
| * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY |
| * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
| * OF THE POSSIBILITY OF SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This product includes cryptographic software written by Eric Young |
| * (eay@cryptsoft.com). This product includes software written by Tim |
| * Hudson (tjh@cryptsoft.com). |
| * |
| */ |
| |
| #include "cryptlib.h" |
| #include <openssl/evp.h> |
| #include <openssl/lhash.h> |
| #include "eng_int.h" |
| |
| /* The type of the items in the table */ |
| typedef struct st_engine_pile { |
| /* The 'nid' of this algorithm/mode */ |
| int nid; |
| /* ENGINEs that implement this algorithm/mode. */ |
| STACK_OF(ENGINE) *sk; |
| /* The default ENGINE to perform this algorithm/mode. */ |
| ENGINE *funct; |
| /* |
| * Zero if 'sk' is newer than the cached 'funct', non-zero otherwise |
| */ |
| int uptodate; |
| } ENGINE_PILE; |
| |
| DECLARE_LHASH_OF(ENGINE_PILE); |
| |
| /* The type exposed in eng_int.h */ |
| struct st_engine_table { |
| LHASH_OF(ENGINE_PILE) piles; |
| }; /* ENGINE_TABLE */ |
| |
| typedef struct st_engine_pile_doall { |
| engine_table_doall_cb *cb; |
| void *arg; |
| } ENGINE_PILE_DOALL; |
| |
| /* Global flags (ENGINE_TABLE_FLAG_***). */ |
| static unsigned int table_flags = 0; |
| |
| /* API function manipulating 'table_flags' */ |
| unsigned int ENGINE_get_table_flags(void) |
| { |
| return table_flags; |
| } |
| |
| void ENGINE_set_table_flags(unsigned int flags) |
| { |
| table_flags = flags; |
| } |
| |
| /* Internal functions for the "piles" hash table */ |
| static unsigned long engine_pile_hash(const ENGINE_PILE *c) |
| { |
| return c->nid; |
| } |
| |
| static int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b) |
| { |
| return a->nid - b->nid; |
| } |
| |
| static IMPLEMENT_LHASH_HASH_FN(engine_pile, ENGINE_PILE) |
| static IMPLEMENT_LHASH_COMP_FN(engine_pile, ENGINE_PILE) |
| |
| static int int_table_check(ENGINE_TABLE **t, int create) |
| { |
| LHASH_OF(ENGINE_PILE) *lh; |
| |
| if (*t) |
| return 1; |
| if (!create) |
| return 0; |
| if ((lh = lh_ENGINE_PILE_new()) == NULL) |
| return 0; |
| *t = (ENGINE_TABLE *)lh; |
| return 1; |
| } |
| |
| /* |
| * Privately exposed (via eng_int.h) functions for adding and/or removing |
| * ENGINEs from the implementation table |
| */ |
| int engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup, |
| ENGINE *e, const int *nids, int num_nids, |
| int setdefault) |
| { |
| int ret = 0, added = 0; |
| ENGINE_PILE tmplate, *fnd; |
| CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); |
| if (!(*table)) |
| added = 1; |
| if (!int_table_check(table, 1)) |
| goto end; |
| if (added) |
| /* The cleanup callback needs to be added */ |
| engine_cleanup_add_first(cleanup); |
| while (num_nids--) { |
| tmplate.nid = *nids; |
| fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate); |
| if (!fnd) { |
| fnd = OPENSSL_malloc(sizeof(ENGINE_PILE)); |
| if (!fnd) |
| goto end; |
| fnd->uptodate = 1; |
| fnd->nid = *nids; |
| fnd->sk = sk_ENGINE_new_null(); |
| if (!fnd->sk) { |
| OPENSSL_free(fnd); |
| goto end; |
| } |
| fnd->funct = NULL; |
| (void)lh_ENGINE_PILE_insert(&(*table)->piles, fnd); |
| } |
| /* A registration shouldn't add duplciate entries */ |
| (void)sk_ENGINE_delete_ptr(fnd->sk, e); |
| /* |
| * if 'setdefault', this ENGINE goes to the head of the list |
| */ |
| if (!sk_ENGINE_push(fnd->sk, e)) |
| goto end; |
| /* "touch" this ENGINE_PILE */ |
| fnd->uptodate = 0; |
| if (setdefault) { |
| if (!engine_unlocked_init(e)) { |
| ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER, |
| ENGINE_R_INIT_FAILED); |
| goto end; |
| } |
| if (fnd->funct) |
| engine_unlocked_finish(fnd->funct, 0); |
| fnd->funct = e; |
| fnd->uptodate = 1; |
| } |
| nids++; |
| } |
| ret = 1; |
| end: |
| CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); |
| return ret; |
| } |
| |
| static void int_unregister_cb_doall_arg(ENGINE_PILE *pile, ENGINE *e) |
| { |
| int n; |
| /* Iterate the 'c->sk' stack removing any occurrence of 'e' */ |
| while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) { |
| (void)sk_ENGINE_delete(pile->sk, n); |
| pile->uptodate = 0; |
| } |
| if (pile->funct == e) { |
| engine_unlocked_finish(e, 0); |
| pile->funct = NULL; |
| } |
| } |
| |
| static IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb, ENGINE_PILE, ENGINE) |
| |
| void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e) |
| { |
| CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); |
| if (int_table_check(table, 0)) |
| lh_ENGINE_PILE_doall_arg(&(*table)->piles, |
| LHASH_DOALL_ARG_FN(int_unregister_cb), |
| ENGINE, e); |
| CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); |
| } |
| |
| static void int_cleanup_cb_doall(ENGINE_PILE *p) |
| { |
| sk_ENGINE_free(p->sk); |
| if (p->funct) |
| engine_unlocked_finish(p->funct, 0); |
| OPENSSL_free(p); |
| } |
| |
| static IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb, ENGINE_PILE) |
| |
| void engine_table_cleanup(ENGINE_TABLE **table) |
| { |
| CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); |
| if (*table) { |
| lh_ENGINE_PILE_doall(&(*table)->piles, |
| LHASH_DOALL_FN(int_cleanup_cb)); |
| lh_ENGINE_PILE_free(&(*table)->piles); |
| *table = NULL; |
| } |
| CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); |
| } |
| |
| /* return a functional reference for a given 'nid' */ |
| #ifndef ENGINE_TABLE_DEBUG |
| ENGINE *engine_table_select(ENGINE_TABLE **table, int nid) |
| #else |
| ENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f, |
| int l) |
| #endif |
| { |
| ENGINE *ret = NULL; |
| ENGINE_PILE tmplate, *fnd = NULL; |
| int initres, loop = 0; |
| |
| if (!(*table)) { |
| #ifdef ENGINE_TABLE_DEBUG |
| fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing " |
| "registered!\n", f, l, nid); |
| #endif |
| return NULL; |
| } |
| ERR_set_mark(); |
| CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); |
| /* |
| * Check again inside the lock otherwise we could race against cleanup |
| * operations. But don't worry about a fprintf(stderr). |
| */ |
| if (!int_table_check(table, 0)) |
| goto end; |
| tmplate.nid = nid; |
| fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate); |
| if (!fnd) |
| goto end; |
| if (fnd->funct && engine_unlocked_init(fnd->funct)) { |
| #ifdef ENGINE_TABLE_DEBUG |
| fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " |
| "ENGINE '%s' cached\n", f, l, nid, fnd->funct->id); |
| #endif |
| ret = fnd->funct; |
| goto end; |
| } |
| if (fnd->uptodate) { |
| ret = fnd->funct; |
| goto end; |
| } |
| trynext: |
| ret = sk_ENGINE_value(fnd->sk, loop++); |
| if (!ret) { |
| #ifdef ENGINE_TABLE_DEBUG |
| fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no " |
| "registered implementations would initialise\n", f, l, nid); |
| #endif |
| goto end; |
| } |
| /* Try to initialise the ENGINE? */ |
| if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT)) |
| initres = engine_unlocked_init(ret); |
| else |
| initres = 0; |
| if (initres) { |
| /* Update 'funct' */ |
| if ((fnd->funct != ret) && engine_unlocked_init(ret)) { |
| /* If there was a previous default we release it. */ |
| if (fnd->funct) |
| engine_unlocked_finish(fnd->funct, 0); |
| fnd->funct = ret; |
| #ifdef ENGINE_TABLE_DEBUG |
| fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, " |
| "setting default to '%s'\n", f, l, nid, ret->id); |
| #endif |
| } |
| #ifdef ENGINE_TABLE_DEBUG |
| fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " |
| "newly initialised '%s'\n", f, l, nid, ret->id); |
| #endif |
| goto end; |
| } |
| goto trynext; |
| end: |
| /* |
| * If it failed, it is unlikely to succeed again until some future |
| * registrations have taken place. In all cases, we cache. |
| */ |
| if (fnd) |
| fnd->uptodate = 1; |
| #ifdef ENGINE_TABLE_DEBUG |
| if (ret) |
| fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " |
| "ENGINE '%s'\n", f, l, nid, ret->id); |
| else |
| fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " |
| "'no matching ENGINE'\n", f, l, nid); |
| #endif |
| CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); |
| /* |
| * Whatever happened, any failed init()s are not failures in this |
| * context, so clear our error state. |
| */ |
| ERR_pop_to_mark(); |
| return ret; |
| } |
| |
| /* Table enumeration */ |
| |
| static void int_cb_doall_arg(ENGINE_PILE *pile, ENGINE_PILE_DOALL *dall) |
| { |
| dall->cb(pile->nid, pile->sk, pile->funct, dall->arg); |
| } |
| |
| static IMPLEMENT_LHASH_DOALL_ARG_FN(int_cb, ENGINE_PILE, ENGINE_PILE_DOALL) |
| |
| void engine_table_doall(ENGINE_TABLE *table, engine_table_doall_cb *cb, |
| void *arg) |
| { |
| ENGINE_PILE_DOALL dall; |
| dall.cb = cb; |
| dall.arg = arg; |
| if (table) |
| lh_ENGINE_PILE_doall_arg(&table->piles, |
| LHASH_DOALL_ARG_FN(int_cb), |
| ENGINE_PILE_DOALL, &dall); |
| } |