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