| /* ==================================================================== |
| * 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 <openssl/evp.h> |
| #include <openssl/engine.h> |
| #include "eng_int.h" |
| |
| /* This is the type of item in the 'implementation' table. Each 'nid' hashes to |
| * a (potentially NULL) ENGINE_PILE structure which contains a stack of ENGINE* |
| * pointers. These pointers aren't references, because they're inserted and |
| * removed during ENGINE creation and ENGINE destruction. They point to ENGINEs |
| * that *exist* (ie. have a structural reference count greater than zero) rather |
| * than ENGINEs that are *functional*. Each pointer in those stacks are to |
| * ENGINEs that implements the algorithm corresponding to each 'nid'. */ |
| |
| /* The type of the items in the table */ |
| typedef struct st_engine_pile |
| { |
| /* The 'nid' of the algorithm/mode this ENGINE_PILE structure represents |
| * */ |
| int nid; |
| /* A stack of ENGINE pointers for ENGINEs that support this |
| * algorithm/mode. In the event that 'funct' is NULL, the first entry in |
| * this stack that initialises will be set as 'funct' and assumed as the |
| * default for operations of this type. */ |
| STACK_OF(ENGINE) *sk; |
| /* The default ENGINE to perform this algorithm/mode. */ |
| ENGINE *funct; |
| /* This value optimises engine_table_select(). If it is called it sets |
| * this value to 1. Any changes to this ENGINE_PILE resets it to zero. |
| * As such, no ENGINE_init() thrashing is done unless ENGINEs |
| * continually register (and/or unregister). */ |
| int uptodate; |
| } ENGINE_PILE; |
| |
| /* The type of the hash table of ENGINE_PILE structures such that each are |
| * unique and keyed by the 'nid' value. */ |
| struct st_engine_table |
| { |
| LHASH piles; |
| }; /* ENGINE_TABLE */ |
| |
| /* This value stores global options controlling behaviour of (mostly) the |
| * engine_table_select() function. It's a bitmask of flag values of the form |
| * ENGINE_TABLE_FLAG_*** (as defined in engine.h) and is controlled by the |
| * ENGINE_[get|set]_table_flags() function. */ |
| 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_hash, const ENGINE_PILE *) |
| static IMPLEMENT_LHASH_COMP_FN(engine_pile_cmp, const ENGINE_PILE *) |
| static int int_table_check(ENGINE_TABLE **t, int create) |
| { |
| LHASH *lh; |
| if(*t) |
| return 1; |
| if(!create) |
| return 0; |
| if((lh = lh_new(LHASH_HASH_FN(engine_pile_hash), |
| LHASH_COMP_FN(engine_pile_cmp))) == 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_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; |
| lh_insert(&(*table)->piles, fnd); |
| } |
| /* A registration shouldn't add duplciate entries */ |
| 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; |
| } |
| nids++; |
| } |
| ret = 1; |
| end: |
| CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); |
| return ret; |
| } |
| static void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e) |
| { |
| int n; |
| /* Iterate the 'c->sk' stack removing any occurance of 'e' */ |
| while((n = sk_ENGINE_find(pile->sk, e)) >= 0) |
| { |
| sk_ENGINE_delete(pile->sk, n); |
| /* "touch" this ENGINE_CIPHER */ |
| 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_doall_arg(&(*table)->piles, |
| LHASH_DOALL_ARG_FN(int_unregister_cb), e); |
| CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); |
| } |
| |
| static void int_cleanup_cb(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_doall(&(*table)->piles, LHASH_DOALL_FN(int_cleanup_cb)); |
| lh_free(&(*table)->piles); |
| *table = NULL; |
| } |
| CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); |
| } |
| |
| /* Exposed API function to get a functional reference from the implementation |
| * table (ie. try to get a functional reference from the tabled structural |
| * references) for a given cipher '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 'engine_ciphers' is NULL, then it's absolutely *sure* that no |
| * ENGINEs have registered any implementations! */ |
| if(!(*table)) |
| { |
| #ifdef ENGINE_TABLE_DEBUG |
| fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no " |
| "registered for anything!\n", f, l, nid); |
| #endif |
| return NULL; |
| } |
| 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_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; |
| } |
| #if 0 |
| /* Don't need to get a reference if we hold the lock. If the locking has |
| * to change in future, that would be different ... */ |
| ret->struct_ref++; engine_ref_debug(ret, 0, 1) |
| #endif |
| /* Try and initialise the ENGINE if it's already functional *or* if the |
| * ENGINE_TABLE_FLAG_NOINIT flag is not set. */ |
| if((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT)) |
| initres = engine_unlocked_init(ret); |
| else |
| initres = 0; |
| #if 0 |
| /* Release the structural reference */ |
| ret->struct_ref--; engine_ref_debug(ret, 0, -1); |
| #endif |
| if(initres) |
| { |
| /* If we didn't have a default (functional reference) for this |
| * 'nid' (or we had one but for whatever reason we're now |
| * initialising a different one), use this opportunity to set |
| * '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); |
| /* We got an extra functional reference for the |
| * per-'nid' default */ |
| 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: |
| /* Whatever happened - we should "untouch" our uptodate file seeing as |
| * we have tried our best to find a functional reference for 'nid'. If |
| * it failed, it is unlikely to succeed again until some future |
| * registrations (or unregistrations) have taken place that affect that |
| * 'nid'. */ |
| 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_clear_error(); |
| return ret; |
| } |