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 9296465Sdelphij * 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 */ 61296465Sdelphijtypedef struct st_engine_pile { 62296465Sdelphij /* The 'nid' of this algorithm/mode */ 63296465Sdelphij int nid; 64296465Sdelphij /* ENGINEs that implement this algorithm/mode. */ 65296465Sdelphij STACK_OF(ENGINE) *sk; 66296465Sdelphij /* The default ENGINE to perform this algorithm/mode. */ 67296465Sdelphij ENGINE *funct; 68296465Sdelphij /* 69296465Sdelphij * Zero if 'sk' is newer than the cached 'funct', non-zero otherwise 70296465Sdelphij */ 71296465Sdelphij int uptodate; 72296465Sdelphij} ENGINE_PILE; 73109998Smarkm 74160814Ssimon/* The type exposed in eng_int.h */ 75296465Sdelphijstruct st_engine_table { 76296465Sdelphij LHASH piles; 77296465Sdelphij}; /* ENGINE_TABLE */ 78109998Smarkm 79160814Ssimon/* Global flags (ENGINE_TABLE_FLAG_***). */ 80109998Smarkmstatic unsigned int table_flags = 0; 81109998Smarkm 82109998Smarkm/* API function manipulating 'table_flags' */ 83109998Smarkmunsigned int ENGINE_get_table_flags(void) 84296465Sdelphij{ 85296465Sdelphij return table_flags; 86296465Sdelphij} 87296465Sdelphij 88109998Smarkmvoid ENGINE_set_table_flags(unsigned int flags) 89296465Sdelphij{ 90296465Sdelphij table_flags = flags; 91296465Sdelphij} 92109998Smarkm 93109998Smarkm/* Internal functions for the "piles" hash table */ 94109998Smarkmstatic unsigned long engine_pile_hash(const ENGINE_PILE *c) 95296465Sdelphij{ 96296465Sdelphij return c->nid; 97296465Sdelphij} 98296465Sdelphij 99109998Smarkmstatic int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b) 100296465Sdelphij{ 101296465Sdelphij return a->nid - b->nid; 102296465Sdelphij} 103296465Sdelphij 104109998Smarkmstatic IMPLEMENT_LHASH_HASH_FN(engine_pile_hash, const ENGINE_PILE *) 105109998Smarkmstatic IMPLEMENT_LHASH_COMP_FN(engine_pile_cmp, const ENGINE_PILE *) 106109998Smarkmstatic int int_table_check(ENGINE_TABLE **t, int create) 107296465Sdelphij{ 108296465Sdelphij LHASH *lh; 109296465Sdelphij if (*t) 110296465Sdelphij return 1; 111296465Sdelphij if (!create) 112296465Sdelphij return 0; 113296465Sdelphij if ((lh = lh_new(LHASH_HASH_FN(engine_pile_hash), 114296465Sdelphij LHASH_COMP_FN(engine_pile_cmp))) == NULL) 115296465Sdelphij return 0; 116296465Sdelphij *t = (ENGINE_TABLE *)lh; 117296465Sdelphij return 1; 118296465Sdelphij} 119109998Smarkm 120296465Sdelphij/* 121296465Sdelphij * Privately exposed (via eng_int.h) functions for adding and/or removing 122296465Sdelphij * ENGINEs from the implementation table 123296465Sdelphij */ 124109998Smarkmint engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup, 125296465Sdelphij ENGINE *e, const int *nids, int num_nids, 126296465Sdelphij int setdefault) 127296465Sdelphij{ 128296465Sdelphij int ret = 0, added = 0; 129296465Sdelphij ENGINE_PILE tmplate, *fnd; 130296465Sdelphij CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 131296465Sdelphij if (!(*table)) 132296465Sdelphij added = 1; 133296465Sdelphij if (!int_table_check(table, 1)) 134296465Sdelphij goto end; 135296465Sdelphij if (added) 136296465Sdelphij /* The cleanup callback needs to be added */ 137296465Sdelphij engine_cleanup_add_first(cleanup); 138296465Sdelphij while (num_nids--) { 139296465Sdelphij tmplate.nid = *nids; 140296465Sdelphij fnd = lh_retrieve(&(*table)->piles, &tmplate); 141296465Sdelphij if (!fnd) { 142296465Sdelphij fnd = OPENSSL_malloc(sizeof(ENGINE_PILE)); 143296465Sdelphij if (!fnd) 144296465Sdelphij goto end; 145296465Sdelphij fnd->uptodate = 1; 146296465Sdelphij fnd->nid = *nids; 147296465Sdelphij fnd->sk = sk_ENGINE_new_null(); 148296465Sdelphij if (!fnd->sk) { 149296465Sdelphij OPENSSL_free(fnd); 150296465Sdelphij goto end; 151296465Sdelphij } 152296465Sdelphij fnd->funct = NULL; 153296465Sdelphij lh_insert(&(*table)->piles, fnd); 154296465Sdelphij } 155296465Sdelphij /* A registration shouldn't add duplciate entries */ 156296465Sdelphij (void)sk_ENGINE_delete_ptr(fnd->sk, e); 157296465Sdelphij /* 158296465Sdelphij * if 'setdefault', this ENGINE goes to the head of the list 159296465Sdelphij */ 160296465Sdelphij if (!sk_ENGINE_push(fnd->sk, e)) 161296465Sdelphij goto end; 162296465Sdelphij /* "touch" this ENGINE_PILE */ 163296465Sdelphij fnd->uptodate = 0; 164296465Sdelphij if (setdefault) { 165296465Sdelphij if (!engine_unlocked_init(e)) { 166296465Sdelphij ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER, 167296465Sdelphij ENGINE_R_INIT_FAILED); 168296465Sdelphij goto end; 169296465Sdelphij } 170296465Sdelphij if (fnd->funct) 171296465Sdelphij engine_unlocked_finish(fnd->funct, 0); 172296465Sdelphij fnd->funct = e; 173296465Sdelphij fnd->uptodate = 1; 174296465Sdelphij } 175296465Sdelphij nids++; 176296465Sdelphij } 177296465Sdelphij ret = 1; 178296465Sdelphij end: 179296465Sdelphij CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 180296465Sdelphij return ret; 181296465Sdelphij} 182296465Sdelphij 183109998Smarkmstatic void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e) 184296465Sdelphij{ 185296465Sdelphij int n; 186296465Sdelphij /* Iterate the 'c->sk' stack removing any occurance of 'e' */ 187296465Sdelphij while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) { 188296465Sdelphij (void)sk_ENGINE_delete(pile->sk, n); 189296465Sdelphij pile->uptodate = 0; 190296465Sdelphij } 191296465Sdelphij if (pile->funct == e) { 192296465Sdelphij engine_unlocked_finish(e, 0); 193296465Sdelphij pile->funct = NULL; 194296465Sdelphij } 195296465Sdelphij} 196296465Sdelphij 197296465Sdelphijstatic IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb, ENGINE_PILE *, 198296465Sdelphij ENGINE *) 199109998Smarkmvoid engine_table_unregister(ENGINE_TABLE **table, ENGINE *e) 200296465Sdelphij{ 201296465Sdelphij CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 202296465Sdelphij if (int_table_check(table, 0)) 203296465Sdelphij lh_doall_arg(&(*table)->piles, 204296465Sdelphij LHASH_DOALL_ARG_FN(int_unregister_cb), e); 205296465Sdelphij CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 206296465Sdelphij} 207109998Smarkm 208109998Smarkmstatic void int_cleanup_cb(ENGINE_PILE *p) 209296465Sdelphij{ 210296465Sdelphij sk_ENGINE_free(p->sk); 211296465Sdelphij if (p->funct) 212296465Sdelphij engine_unlocked_finish(p->funct, 0); 213296465Sdelphij OPENSSL_free(p); 214296465Sdelphij} 215296465Sdelphij 216296465Sdelphijstatic IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb, ENGINE_PILE *) 217109998Smarkmvoid engine_table_cleanup(ENGINE_TABLE **table) 218296465Sdelphij{ 219296465Sdelphij CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 220296465Sdelphij if (*table) { 221296465Sdelphij lh_doall(&(*table)->piles, LHASH_DOALL_FN(int_cleanup_cb)); 222296465Sdelphij lh_free(&(*table)->piles); 223296465Sdelphij *table = NULL; 224296465Sdelphij } 225296465Sdelphij CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 226296465Sdelphij} 227109998Smarkm 228160814Ssimon/* return a functional reference for a given 'nid' */ 229109998Smarkm#ifndef ENGINE_TABLE_DEBUG 230109998SmarkmENGINE *engine_table_select(ENGINE_TABLE **table, int nid) 231109998Smarkm#else 232296465SdelphijENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f, 233296465Sdelphij int l) 234109998Smarkm#endif 235296465Sdelphij{ 236296465Sdelphij ENGINE *ret = NULL; 237296465Sdelphij ENGINE_PILE tmplate, *fnd = NULL; 238296465Sdelphij int initres, loop = 0; 239109998Smarkm 240296465Sdelphij if (!(*table)) { 241109998Smarkm#ifdef ENGINE_TABLE_DEBUG 242296465Sdelphij fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing " 243296465Sdelphij "registered!\n", f, l, nid); 244109998Smarkm#endif 245296465Sdelphij return NULL; 246296465Sdelphij } 247296465Sdelphij ERR_set_mark(); 248296465Sdelphij CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 249296465Sdelphij /* 250296465Sdelphij * Check again inside the lock otherwise we could race against cleanup 251296465Sdelphij * operations. But don't worry about a fprintf(stderr). 252296465Sdelphij */ 253296465Sdelphij if (!int_table_check(table, 0)) 254296465Sdelphij goto end; 255296465Sdelphij tmplate.nid = nid; 256296465Sdelphij fnd = lh_retrieve(&(*table)->piles, &tmplate); 257296465Sdelphij if (!fnd) 258296465Sdelphij goto end; 259296465Sdelphij if (fnd->funct && engine_unlocked_init(fnd->funct)) { 260109998Smarkm#ifdef ENGINE_TABLE_DEBUG 261296465Sdelphij fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " 262296465Sdelphij "ENGINE '%s' cached\n", f, l, nid, fnd->funct->id); 263109998Smarkm#endif 264296465Sdelphij ret = fnd->funct; 265296465Sdelphij goto end; 266296465Sdelphij } 267296465Sdelphij if (fnd->uptodate) { 268296465Sdelphij ret = fnd->funct; 269296465Sdelphij goto end; 270296465Sdelphij } 271296465Sdelphij trynext: 272296465Sdelphij ret = sk_ENGINE_value(fnd->sk, loop++); 273296465Sdelphij if (!ret) { 274109998Smarkm#ifdef ENGINE_TABLE_DEBUG 275296465Sdelphij fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no " 276296465Sdelphij "registered implementations would initialise\n", f, l, nid); 277109998Smarkm#endif 278296465Sdelphij goto end; 279296465Sdelphij } 280296465Sdelphij /* Try to initialise the ENGINE? */ 281296465Sdelphij if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT)) 282296465Sdelphij initres = engine_unlocked_init(ret); 283296465Sdelphij else 284296465Sdelphij initres = 0; 285296465Sdelphij if (initres) { 286296465Sdelphij /* Update 'funct' */ 287296465Sdelphij if ((fnd->funct != ret) && engine_unlocked_init(ret)) { 288296465Sdelphij /* If there was a previous default we release it. */ 289296465Sdelphij if (fnd->funct) 290296465Sdelphij engine_unlocked_finish(fnd->funct, 0); 291296465Sdelphij fnd->funct = ret; 292109998Smarkm#ifdef ENGINE_TABLE_DEBUG 293296465Sdelphij fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, " 294296465Sdelphij "setting default to '%s'\n", f, l, nid, ret->id); 295109998Smarkm#endif 296296465Sdelphij } 297109998Smarkm#ifdef ENGINE_TABLE_DEBUG 298296465Sdelphij fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " 299296465Sdelphij "newly initialised '%s'\n", f, l, nid, ret->id); 300109998Smarkm#endif 301296465Sdelphij goto end; 302296465Sdelphij } 303296465Sdelphij goto trynext; 304296465Sdelphij end: 305296465Sdelphij /* 306296465Sdelphij * If it failed, it is unlikely to succeed again until some future 307296465Sdelphij * registrations have taken place. In all cases, we cache. 308296465Sdelphij */ 309296465Sdelphij if (fnd) 310296465Sdelphij fnd->uptodate = 1; 311109998Smarkm#ifdef ENGINE_TABLE_DEBUG 312296465Sdelphij if (ret) 313296465Sdelphij fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " 314296465Sdelphij "ENGINE '%s'\n", f, l, nid, ret->id); 315296465Sdelphij else 316296465Sdelphij fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " 317296465Sdelphij "'no matching ENGINE'\n", f, l, nid); 318109998Smarkm#endif 319296465Sdelphij CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 320296465Sdelphij /* 321296465Sdelphij * Whatever happened, any failed init()s are not failures in this 322296465Sdelphij * context, so clear our error state. 323296465Sdelphij */ 324296465Sdelphij ERR_pop_to_mark(); 325296465Sdelphij return ret; 326296465Sdelphij} 327