eng_table.c revision 194206
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 9109998Smarkm * 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 */ 61109998Smarkmtypedef struct st_engine_pile 62109998Smarkm { 63160814Ssimon /* The 'nid' of this algorithm/mode */ 64109998Smarkm int nid; 65160814Ssimon /* ENGINEs that implement this algorithm/mode. */ 66109998Smarkm STACK_OF(ENGINE) *sk; 67109998Smarkm /* The default ENGINE to perform this algorithm/mode. */ 68109998Smarkm ENGINE *funct; 69160814Ssimon /* Zero if 'sk' is newer than the cached 'funct', non-zero otherwise */ 70109998Smarkm int uptodate; 71109998Smarkm } ENGINE_PILE; 72109998Smarkm 73160814Ssimon/* The type exposed in eng_int.h */ 74109998Smarkmstruct st_engine_table 75109998Smarkm { 76109998Smarkm LHASH piles; 77109998Smarkm }; /* 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) 84109998Smarkm { 85109998Smarkm return table_flags; 86109998Smarkm } 87109998Smarkmvoid ENGINE_set_table_flags(unsigned int flags) 88109998Smarkm { 89109998Smarkm table_flags = flags; 90109998Smarkm } 91109998Smarkm 92109998Smarkm/* Internal functions for the "piles" hash table */ 93109998Smarkmstatic unsigned long engine_pile_hash(const ENGINE_PILE *c) 94109998Smarkm { 95109998Smarkm return c->nid; 96109998Smarkm } 97109998Smarkmstatic int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b) 98109998Smarkm { 99109998Smarkm return a->nid - b->nid; 100109998Smarkm } 101109998Smarkmstatic IMPLEMENT_LHASH_HASH_FN(engine_pile_hash, const ENGINE_PILE *) 102109998Smarkmstatic IMPLEMENT_LHASH_COMP_FN(engine_pile_cmp, const ENGINE_PILE *) 103109998Smarkmstatic int int_table_check(ENGINE_TABLE **t, int create) 104109998Smarkm { 105109998Smarkm LHASH *lh; 106160814Ssimon if(*t) return 1; 107160814Ssimon if(!create) return 0; 108109998Smarkm if((lh = lh_new(LHASH_HASH_FN(engine_pile_hash), 109109998Smarkm LHASH_COMP_FN(engine_pile_cmp))) == NULL) 110109998Smarkm return 0; 111109998Smarkm *t = (ENGINE_TABLE *)lh; 112109998Smarkm return 1; 113109998Smarkm } 114109998Smarkm 115109998Smarkm/* Privately exposed (via eng_int.h) functions for adding and/or removing 116109998Smarkm * ENGINEs from the implementation table */ 117109998Smarkmint engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup, 118109998Smarkm ENGINE *e, const int *nids, int num_nids, int setdefault) 119109998Smarkm { 120109998Smarkm int ret = 0, added = 0; 121109998Smarkm ENGINE_PILE tmplate, *fnd; 122109998Smarkm CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 123109998Smarkm if(!(*table)) 124109998Smarkm added = 1; 125109998Smarkm if(!int_table_check(table, 1)) 126109998Smarkm goto end; 127109998Smarkm if(added) 128109998Smarkm /* The cleanup callback needs to be added */ 129109998Smarkm engine_cleanup_add_first(cleanup); 130109998Smarkm while(num_nids--) 131109998Smarkm { 132109998Smarkm tmplate.nid = *nids; 133109998Smarkm fnd = lh_retrieve(&(*table)->piles, &tmplate); 134109998Smarkm if(!fnd) 135109998Smarkm { 136109998Smarkm fnd = OPENSSL_malloc(sizeof(ENGINE_PILE)); 137160814Ssimon if(!fnd) goto end; 138194206Ssimon fnd->uptodate = 1; 139109998Smarkm fnd->nid = *nids; 140109998Smarkm fnd->sk = sk_ENGINE_new_null(); 141109998Smarkm if(!fnd->sk) 142109998Smarkm { 143109998Smarkm OPENSSL_free(fnd); 144109998Smarkm goto end; 145109998Smarkm } 146160814Ssimon fnd->funct = NULL; 147109998Smarkm lh_insert(&(*table)->piles, fnd); 148109998Smarkm } 149109998Smarkm /* A registration shouldn't add duplciate entries */ 150194206Ssimon (void)sk_ENGINE_delete_ptr(fnd->sk, e); 151109998Smarkm /* if 'setdefault', this ENGINE goes to the head of the list */ 152109998Smarkm if(!sk_ENGINE_push(fnd->sk, e)) 153109998Smarkm goto end; 154109998Smarkm /* "touch" this ENGINE_PILE */ 155194206Ssimon fnd->uptodate = 0; 156109998Smarkm if(setdefault) 157109998Smarkm { 158109998Smarkm if(!engine_unlocked_init(e)) 159109998Smarkm { 160109998Smarkm ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER, 161109998Smarkm ENGINE_R_INIT_FAILED); 162109998Smarkm goto end; 163109998Smarkm } 164109998Smarkm if(fnd->funct) 165109998Smarkm engine_unlocked_finish(fnd->funct, 0); 166109998Smarkm fnd->funct = e; 167194206Ssimon fnd->uptodate = 1; 168109998Smarkm } 169109998Smarkm nids++; 170109998Smarkm } 171109998Smarkm ret = 1; 172109998Smarkmend: 173109998Smarkm CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 174109998Smarkm return ret; 175109998Smarkm } 176109998Smarkmstatic void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e) 177109998Smarkm { 178109998Smarkm int n; 179109998Smarkm /* Iterate the 'c->sk' stack removing any occurance of 'e' */ 180109998Smarkm while((n = sk_ENGINE_find(pile->sk, e)) >= 0) 181109998Smarkm { 182194206Ssimon (void)sk_ENGINE_delete(pile->sk, n); 183194206Ssimon pile->uptodate = 0; 184109998Smarkm } 185109998Smarkm if(pile->funct == e) 186109998Smarkm { 187109998Smarkm engine_unlocked_finish(e, 0); 188109998Smarkm pile->funct = NULL; 189109998Smarkm } 190109998Smarkm } 191109998Smarkmstatic IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb,ENGINE_PILE *,ENGINE *) 192109998Smarkmvoid engine_table_unregister(ENGINE_TABLE **table, ENGINE *e) 193109998Smarkm { 194109998Smarkm CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 195109998Smarkm if(int_table_check(table, 0)) 196109998Smarkm lh_doall_arg(&(*table)->piles, 197109998Smarkm LHASH_DOALL_ARG_FN(int_unregister_cb), e); 198109998Smarkm CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 199109998Smarkm } 200109998Smarkm 201109998Smarkmstatic void int_cleanup_cb(ENGINE_PILE *p) 202109998Smarkm { 203109998Smarkm sk_ENGINE_free(p->sk); 204109998Smarkm if(p->funct) 205109998Smarkm engine_unlocked_finish(p->funct, 0); 206109998Smarkm OPENSSL_free(p); 207109998Smarkm } 208109998Smarkmstatic IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb,ENGINE_PILE *) 209109998Smarkmvoid engine_table_cleanup(ENGINE_TABLE **table) 210109998Smarkm { 211109998Smarkm CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 212109998Smarkm if(*table) 213109998Smarkm { 214109998Smarkm lh_doall(&(*table)->piles, LHASH_DOALL_FN(int_cleanup_cb)); 215109998Smarkm lh_free(&(*table)->piles); 216109998Smarkm *table = NULL; 217109998Smarkm } 218109998Smarkm CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 219109998Smarkm } 220109998Smarkm 221160814Ssimon/* return a functional reference for a given 'nid' */ 222109998Smarkm#ifndef ENGINE_TABLE_DEBUG 223109998SmarkmENGINE *engine_table_select(ENGINE_TABLE **table, int nid) 224109998Smarkm#else 225109998SmarkmENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f, int l) 226109998Smarkm#endif 227109998Smarkm { 228109998Smarkm ENGINE *ret = NULL; 229109998Smarkm ENGINE_PILE tmplate, *fnd=NULL; 230109998Smarkm int initres, loop = 0; 231109998Smarkm 232109998Smarkm if(!(*table)) 233109998Smarkm { 234109998Smarkm#ifdef ENGINE_TABLE_DEBUG 235160814Ssimon fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing " 236160814Ssimon "registered!\n", f, l, nid); 237109998Smarkm#endif 238109998Smarkm return NULL; 239109998Smarkm } 240109998Smarkm CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 241109998Smarkm /* Check again inside the lock otherwise we could race against cleanup 242109998Smarkm * operations. But don't worry about a fprintf(stderr). */ 243160814Ssimon if(!int_table_check(table, 0)) goto end; 244109998Smarkm tmplate.nid = nid; 245109998Smarkm fnd = lh_retrieve(&(*table)->piles, &tmplate); 246160814Ssimon if(!fnd) goto end; 247109998Smarkm if(fnd->funct && engine_unlocked_init(fnd->funct)) 248109998Smarkm { 249109998Smarkm#ifdef ENGINE_TABLE_DEBUG 250109998Smarkm fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " 251109998Smarkm "ENGINE '%s' cached\n", f, l, nid, fnd->funct->id); 252109998Smarkm#endif 253109998Smarkm ret = fnd->funct; 254109998Smarkm goto end; 255109998Smarkm } 256109998Smarkm if(fnd->uptodate) 257109998Smarkm { 258109998Smarkm ret = fnd->funct; 259109998Smarkm goto end; 260109998Smarkm } 261109998Smarkmtrynext: 262109998Smarkm ret = sk_ENGINE_value(fnd->sk, loop++); 263109998Smarkm if(!ret) 264109998Smarkm { 265109998Smarkm#ifdef ENGINE_TABLE_DEBUG 266109998Smarkm fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no " 267109998Smarkm "registered implementations would initialise\n", 268109998Smarkm f, l, nid); 269109998Smarkm#endif 270109998Smarkm goto end; 271109998Smarkm } 272160814Ssimon /* Try to initialise the ENGINE? */ 273109998Smarkm if((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT)) 274109998Smarkm initres = engine_unlocked_init(ret); 275109998Smarkm else 276109998Smarkm initres = 0; 277109998Smarkm if(initres) 278109998Smarkm { 279160814Ssimon /* Update 'funct' */ 280109998Smarkm if((fnd->funct != ret) && engine_unlocked_init(ret)) 281109998Smarkm { 282109998Smarkm /* If there was a previous default we release it. */ 283109998Smarkm if(fnd->funct) 284109998Smarkm engine_unlocked_finish(fnd->funct, 0); 285109998Smarkm fnd->funct = ret; 286109998Smarkm#ifdef ENGINE_TABLE_DEBUG 287109998Smarkm fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, " 288109998Smarkm "setting default to '%s'\n", f, l, nid, ret->id); 289109998Smarkm#endif 290109998Smarkm } 291109998Smarkm#ifdef ENGINE_TABLE_DEBUG 292109998Smarkm fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " 293109998Smarkm "newly initialised '%s'\n", f, l, nid, ret->id); 294109998Smarkm#endif 295109998Smarkm goto end; 296109998Smarkm } 297109998Smarkm goto trynext; 298109998Smarkmend: 299160814Ssimon /* If it failed, it is unlikely to succeed again until some future 300160814Ssimon * registrations have taken place. In all cases, we cache. */ 301160814Ssimon if(fnd) fnd->uptodate = 1; 302109998Smarkm#ifdef ENGINE_TABLE_DEBUG 303109998Smarkm if(ret) 304109998Smarkm fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " 305109998Smarkm "ENGINE '%s'\n", f, l, nid, ret->id); 306109998Smarkm else 307109998Smarkm fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " 308109998Smarkm "'no matching ENGINE'\n", f, l, nid); 309109998Smarkm#endif 310109998Smarkm CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 311109998Smarkm /* Whatever happened, any failed init()s are not failures in this 312109998Smarkm * context, so clear our error state. */ 313109998Smarkm ERR_clear_error(); 314109998Smarkm return ret; 315109998Smarkm } 316