eng_table.c revision 331638
1/* ==================================================================== 2 * Copyright (c) 2001-2018 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 74DECLARE_LHASH_OF(ENGINE_PILE); 75 76/* The type exposed in eng_int.h */ 77struct st_engine_table { 78 LHASH_OF(ENGINE_PILE) piles; 79}; /* ENGINE_TABLE */ 80 81typedef struct st_engine_pile_doall { 82 engine_table_doall_cb *cb; 83 void *arg; 84} ENGINE_PILE_DOALL; 85 86/* Global flags (ENGINE_TABLE_FLAG_***). */ 87static unsigned int table_flags = 0; 88 89/* API function manipulating 'table_flags' */ 90unsigned int ENGINE_get_table_flags(void) 91{ 92 return table_flags; 93} 94 95void ENGINE_set_table_flags(unsigned int flags) 96{ 97 table_flags = flags; 98} 99 100/* Internal functions for the "piles" hash table */ 101static unsigned long engine_pile_hash(const ENGINE_PILE *c) 102{ 103 return c->nid; 104} 105 106static int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b) 107{ 108 return a->nid - b->nid; 109} 110 111static IMPLEMENT_LHASH_HASH_FN(engine_pile, ENGINE_PILE) 112static IMPLEMENT_LHASH_COMP_FN(engine_pile, ENGINE_PILE) 113 114static int int_table_check(ENGINE_TABLE **t, int create) 115{ 116 LHASH_OF(ENGINE_PILE) *lh; 117 118 if (*t) 119 return 1; 120 if (!create) 121 return 0; 122 if ((lh = lh_ENGINE_PILE_new()) == NULL) 123 return 0; 124 *t = (ENGINE_TABLE *)lh; 125 return 1; 126} 127 128/* 129 * Privately exposed (via eng_int.h) functions for adding and/or removing 130 * ENGINEs from the implementation table 131 */ 132int engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup, 133 ENGINE *e, const int *nids, int num_nids, 134 int setdefault) 135{ 136 int ret = 0, added = 0; 137 ENGINE_PILE tmplate, *fnd; 138 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 139 if (!(*table)) 140 added = 1; 141 if (!int_table_check(table, 1)) 142 goto end; 143 if (added) 144 /* The cleanup callback needs to be added */ 145 engine_cleanup_add_first(cleanup); 146 while (num_nids--) { 147 tmplate.nid = *nids; 148 fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate); 149 if (!fnd) { 150 fnd = OPENSSL_malloc(sizeof(ENGINE_PILE)); 151 if (!fnd) 152 goto end; 153 fnd->uptodate = 1; 154 fnd->nid = *nids; 155 fnd->sk = sk_ENGINE_new_null(); 156 if (!fnd->sk) { 157 OPENSSL_free(fnd); 158 goto end; 159 } 160 fnd->funct = NULL; 161 (void)lh_ENGINE_PILE_insert(&(*table)->piles, fnd); 162 if (lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate) != fnd) { 163 sk_ENGINE_free(fnd->sk); 164 OPENSSL_free(fnd); 165 goto end; 166 } 167 } 168 /* A registration shouldn't add duplciate entries */ 169 (void)sk_ENGINE_delete_ptr(fnd->sk, e); 170 /* 171 * if 'setdefault', this ENGINE goes to the head of the list 172 */ 173 if (!sk_ENGINE_push(fnd->sk, e)) 174 goto end; 175 /* "touch" this ENGINE_PILE */ 176 fnd->uptodate = 0; 177 if (setdefault) { 178 if (!engine_unlocked_init(e)) { 179 ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER, 180 ENGINE_R_INIT_FAILED); 181 goto end; 182 } 183 if (fnd->funct) 184 engine_unlocked_finish(fnd->funct, 0); 185 fnd->funct = e; 186 fnd->uptodate = 1; 187 } 188 nids++; 189 } 190 ret = 1; 191 end: 192 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 193 return ret; 194} 195 196static void int_unregister_cb_doall_arg(ENGINE_PILE *pile, ENGINE *e) 197{ 198 int n; 199 /* Iterate the 'c->sk' stack removing any occurance of 'e' */ 200 while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) { 201 (void)sk_ENGINE_delete(pile->sk, n); 202 pile->uptodate = 0; 203 } 204 if (pile->funct == e) { 205 engine_unlocked_finish(e, 0); 206 pile->funct = NULL; 207 } 208} 209 210static IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb, ENGINE_PILE, ENGINE) 211 212void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e) 213{ 214 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 215 if (int_table_check(table, 0)) 216 lh_ENGINE_PILE_doall_arg(&(*table)->piles, 217 LHASH_DOALL_ARG_FN(int_unregister_cb), 218 ENGINE, e); 219 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 220} 221 222static void int_cleanup_cb_doall(ENGINE_PILE *p) 223{ 224 sk_ENGINE_free(p->sk); 225 if (p->funct) 226 engine_unlocked_finish(p->funct, 0); 227 OPENSSL_free(p); 228} 229 230static IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb, ENGINE_PILE) 231 232void engine_table_cleanup(ENGINE_TABLE **table) 233{ 234 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 235 if (*table) { 236 lh_ENGINE_PILE_doall(&(*table)->piles, 237 LHASH_DOALL_FN(int_cleanup_cb)); 238 lh_ENGINE_PILE_free(&(*table)->piles); 239 *table = NULL; 240 } 241 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 242} 243 244/* return a functional reference for a given 'nid' */ 245#ifndef ENGINE_TABLE_DEBUG 246ENGINE *engine_table_select(ENGINE_TABLE **table, int nid) 247#else 248ENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f, 249 int l) 250#endif 251{ 252 ENGINE *ret = NULL; 253 ENGINE_PILE tmplate, *fnd = NULL; 254 int initres, loop = 0; 255 256 if (!(*table)) { 257#ifdef ENGINE_TABLE_DEBUG 258 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing " 259 "registered!\n", f, l, nid); 260#endif 261 return NULL; 262 } 263 ERR_set_mark(); 264 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 265 /* 266 * Check again inside the lock otherwise we could race against cleanup 267 * operations. But don't worry about a fprintf(stderr). 268 */ 269 if (!int_table_check(table, 0)) 270 goto end; 271 tmplate.nid = nid; 272 fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate); 273 if (!fnd) 274 goto end; 275 if (fnd->funct && engine_unlocked_init(fnd->funct)) { 276#ifdef ENGINE_TABLE_DEBUG 277 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " 278 "ENGINE '%s' cached\n", f, l, nid, fnd->funct->id); 279#endif 280 ret = fnd->funct; 281 goto end; 282 } 283 if (fnd->uptodate) { 284 ret = fnd->funct; 285 goto end; 286 } 287 trynext: 288 ret = sk_ENGINE_value(fnd->sk, loop++); 289 if (!ret) { 290#ifdef ENGINE_TABLE_DEBUG 291 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no " 292 "registered implementations would initialise\n", f, l, nid); 293#endif 294 goto end; 295 } 296 /* Try to initialise the ENGINE? */ 297 if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT)) 298 initres = engine_unlocked_init(ret); 299 else 300 initres = 0; 301 if (initres) { 302 /* Update 'funct' */ 303 if ((fnd->funct != ret) && engine_unlocked_init(ret)) { 304 /* If there was a previous default we release it. */ 305 if (fnd->funct) 306 engine_unlocked_finish(fnd->funct, 0); 307 fnd->funct = ret; 308#ifdef ENGINE_TABLE_DEBUG 309 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, " 310 "setting default to '%s'\n", f, l, nid, ret->id); 311#endif 312 } 313#ifdef ENGINE_TABLE_DEBUG 314 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " 315 "newly initialised '%s'\n", f, l, nid, ret->id); 316#endif 317 goto end; 318 } 319 goto trynext; 320 end: 321 /* 322 * If it failed, it is unlikely to succeed again until some future 323 * registrations have taken place. In all cases, we cache. 324 */ 325 if (fnd) 326 fnd->uptodate = 1; 327#ifdef ENGINE_TABLE_DEBUG 328 if (ret) 329 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " 330 "ENGINE '%s'\n", f, l, nid, ret->id); 331 else 332 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " 333 "'no matching ENGINE'\n", f, l, nid); 334#endif 335 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 336 /* 337 * Whatever happened, any failed init()s are not failures in this 338 * context, so clear our error state. 339 */ 340 ERR_pop_to_mark(); 341 return ret; 342} 343 344/* Table enumeration */ 345 346static void int_cb_doall_arg(ENGINE_PILE *pile, ENGINE_PILE_DOALL *dall) 347{ 348 dall->cb(pile->nid, pile->sk, pile->funct, dall->arg); 349} 350 351static IMPLEMENT_LHASH_DOALL_ARG_FN(int_cb, ENGINE_PILE, ENGINE_PILE_DOALL) 352 353void engine_table_doall(ENGINE_TABLE *table, engine_table_doall_cb *cb, 354 void *arg) 355{ 356 ENGINE_PILE_DOALL dall; 357 dall.cb = cb; 358 dall.arg = arg; 359 if (table) 360 lh_ENGINE_PILE_doall_arg(&table->piles, 361 LHASH_DOALL_ARG_FN(int_cb), 362 ENGINE_PILE_DOALL, &dall); 363} 364