eng_table.c revision 296465
1/* ==================================================================== 2 * Copyright (c) 2001 The OpenSSL Project. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * 3. All advertising materials mentioning features or use of this 17 * software must display the following acknowledgment: 18 * "This product includes software developed by the OpenSSL Project 19 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 20 * 21 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 22 * endorse or promote products derived from this software without 23 * prior written permission. For written permission, please contact 24 * licensing@OpenSSL.org. 25 * 26 * 5. Products derived from this software may not be called "OpenSSL" 27 * nor may "OpenSSL" appear in their names without prior written 28 * permission of the OpenSSL Project. 29 * 30 * 6. Redistributions of any form whatsoever must retain the following 31 * acknowledgment: 32 * "This product includes software developed by the OpenSSL Project 33 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 34 * 35 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 36 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 38 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 41 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 42 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 44 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 45 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 46 * OF THE POSSIBILITY OF SUCH DAMAGE. 47 * ==================================================================== 48 * 49 * This product includes cryptographic software written by Eric Young 50 * (eay@cryptsoft.com). This product includes software written by Tim 51 * Hudson (tjh@cryptsoft.com). 52 * 53 */ 54 55#include "cryptlib.h" 56#include <openssl/evp.h> 57#include <openssl/lhash.h> 58#include "eng_int.h" 59 60/* The type of the items in the table */ 61typedef struct st_engine_pile { 62 /* The 'nid' of this algorithm/mode */ 63 int nid; 64 /* ENGINEs that implement this algorithm/mode. */ 65 STACK_OF(ENGINE) *sk; 66 /* The default ENGINE to perform this algorithm/mode. */ 67 ENGINE *funct; 68 /* 69 * Zero if 'sk' is newer than the cached 'funct', non-zero otherwise 70 */ 71 int uptodate; 72} ENGINE_PILE; 73 74/* The type exposed in eng_int.h */ 75struct st_engine_table { 76 LHASH piles; 77}; /* ENGINE_TABLE */ 78 79/* Global flags (ENGINE_TABLE_FLAG_***). */ 80static unsigned int table_flags = 0; 81 82/* API function manipulating 'table_flags' */ 83unsigned int ENGINE_get_table_flags(void) 84{ 85 return table_flags; 86} 87 88void ENGINE_set_table_flags(unsigned int flags) 89{ 90 table_flags = flags; 91} 92 93/* Internal functions for the "piles" hash table */ 94static unsigned long engine_pile_hash(const ENGINE_PILE *c) 95{ 96 return c->nid; 97} 98 99static int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b) 100{ 101 return a->nid - b->nid; 102} 103 104static IMPLEMENT_LHASH_HASH_FN(engine_pile_hash, const ENGINE_PILE *) 105static IMPLEMENT_LHASH_COMP_FN(engine_pile_cmp, const ENGINE_PILE *) 106static int int_table_check(ENGINE_TABLE **t, int create) 107{ 108 LHASH *lh; 109 if (*t) 110 return 1; 111 if (!create) 112 return 0; 113 if ((lh = lh_new(LHASH_HASH_FN(engine_pile_hash), 114 LHASH_COMP_FN(engine_pile_cmp))) == NULL) 115 return 0; 116 *t = (ENGINE_TABLE *)lh; 117 return 1; 118} 119 120/* 121 * Privately exposed (via eng_int.h) functions for adding and/or removing 122 * ENGINEs from the implementation table 123 */ 124int engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup, 125 ENGINE *e, const int *nids, int num_nids, 126 int setdefault) 127{ 128 int ret = 0, added = 0; 129 ENGINE_PILE tmplate, *fnd; 130 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 131 if (!(*table)) 132 added = 1; 133 if (!int_table_check(table, 1)) 134 goto end; 135 if (added) 136 /* The cleanup callback needs to be added */ 137 engine_cleanup_add_first(cleanup); 138 while (num_nids--) { 139 tmplate.nid = *nids; 140 fnd = lh_retrieve(&(*table)->piles, &tmplate); 141 if (!fnd) { 142 fnd = OPENSSL_malloc(sizeof(ENGINE_PILE)); 143 if (!fnd) 144 goto end; 145 fnd->uptodate = 1; 146 fnd->nid = *nids; 147 fnd->sk = sk_ENGINE_new_null(); 148 if (!fnd->sk) { 149 OPENSSL_free(fnd); 150 goto end; 151 } 152 fnd->funct = NULL; 153 lh_insert(&(*table)->piles, fnd); 154 } 155 /* A registration shouldn't add duplciate entries */ 156 (void)sk_ENGINE_delete_ptr(fnd->sk, e); 157 /* 158 * if 'setdefault', this ENGINE goes to the head of the list 159 */ 160 if (!sk_ENGINE_push(fnd->sk, e)) 161 goto end; 162 /* "touch" this ENGINE_PILE */ 163 fnd->uptodate = 0; 164 if (setdefault) { 165 if (!engine_unlocked_init(e)) { 166 ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER, 167 ENGINE_R_INIT_FAILED); 168 goto end; 169 } 170 if (fnd->funct) 171 engine_unlocked_finish(fnd->funct, 0); 172 fnd->funct = e; 173 fnd->uptodate = 1; 174 } 175 nids++; 176 } 177 ret = 1; 178 end: 179 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 180 return ret; 181} 182 183static void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e) 184{ 185 int n; 186 /* Iterate the 'c->sk' stack removing any occurance of 'e' */ 187 while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) { 188 (void)sk_ENGINE_delete(pile->sk, n); 189 pile->uptodate = 0; 190 } 191 if (pile->funct == e) { 192 engine_unlocked_finish(e, 0); 193 pile->funct = NULL; 194 } 195} 196 197static IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb, ENGINE_PILE *, 198 ENGINE *) 199void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e) 200{ 201 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 202 if (int_table_check(table, 0)) 203 lh_doall_arg(&(*table)->piles, 204 LHASH_DOALL_ARG_FN(int_unregister_cb), e); 205 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 206} 207 208static void int_cleanup_cb(ENGINE_PILE *p) 209{ 210 sk_ENGINE_free(p->sk); 211 if (p->funct) 212 engine_unlocked_finish(p->funct, 0); 213 OPENSSL_free(p); 214} 215 216static IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb, ENGINE_PILE *) 217void engine_table_cleanup(ENGINE_TABLE **table) 218{ 219 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 220 if (*table) { 221 lh_doall(&(*table)->piles, LHASH_DOALL_FN(int_cleanup_cb)); 222 lh_free(&(*table)->piles); 223 *table = NULL; 224 } 225 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 226} 227 228/* return a functional reference for a given 'nid' */ 229#ifndef ENGINE_TABLE_DEBUG 230ENGINE *engine_table_select(ENGINE_TABLE **table, int nid) 231#else 232ENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f, 233 int l) 234#endif 235{ 236 ENGINE *ret = NULL; 237 ENGINE_PILE tmplate, *fnd = NULL; 238 int initres, loop = 0; 239 240 if (!(*table)) { 241#ifdef ENGINE_TABLE_DEBUG 242 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing " 243 "registered!\n", f, l, nid); 244#endif 245 return NULL; 246 } 247 ERR_set_mark(); 248 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 249 /* 250 * Check again inside the lock otherwise we could race against cleanup 251 * operations. But don't worry about a fprintf(stderr). 252 */ 253 if (!int_table_check(table, 0)) 254 goto end; 255 tmplate.nid = nid; 256 fnd = lh_retrieve(&(*table)->piles, &tmplate); 257 if (!fnd) 258 goto end; 259 if (fnd->funct && engine_unlocked_init(fnd->funct)) { 260#ifdef ENGINE_TABLE_DEBUG 261 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " 262 "ENGINE '%s' cached\n", f, l, nid, fnd->funct->id); 263#endif 264 ret = fnd->funct; 265 goto end; 266 } 267 if (fnd->uptodate) { 268 ret = fnd->funct; 269 goto end; 270 } 271 trynext: 272 ret = sk_ENGINE_value(fnd->sk, loop++); 273 if (!ret) { 274#ifdef ENGINE_TABLE_DEBUG 275 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no " 276 "registered implementations would initialise\n", f, l, nid); 277#endif 278 goto end; 279 } 280 /* Try to initialise the ENGINE? */ 281 if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT)) 282 initres = engine_unlocked_init(ret); 283 else 284 initres = 0; 285 if (initres) { 286 /* Update 'funct' */ 287 if ((fnd->funct != ret) && engine_unlocked_init(ret)) { 288 /* If there was a previous default we release it. */ 289 if (fnd->funct) 290 engine_unlocked_finish(fnd->funct, 0); 291 fnd->funct = ret; 292#ifdef ENGINE_TABLE_DEBUG 293 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, " 294 "setting default to '%s'\n", f, l, nid, ret->id); 295#endif 296 } 297#ifdef ENGINE_TABLE_DEBUG 298 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " 299 "newly initialised '%s'\n", f, l, nid, ret->id); 300#endif 301 goto end; 302 } 303 goto trynext; 304 end: 305 /* 306 * If it failed, it is unlikely to succeed again until some future 307 * registrations have taken place. In all cases, we cache. 308 */ 309 if (fnd) 310 fnd->uptodate = 1; 311#ifdef ENGINE_TABLE_DEBUG 312 if (ret) 313 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " 314 "ENGINE '%s'\n", f, l, nid, ret->id); 315 else 316 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " 317 "'no matching ENGINE'\n", f, l, nid); 318#endif 319 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 320 /* 321 * Whatever happened, any failed init()s are not failures in this 322 * context, so clear our error state. 323 */ 324 ERR_pop_to_mark(); 325 return ret; 326} 327