1 /* 2 * Copyright 2001-2024 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include "internal/cryptlib.h" 11 #include <openssl/evp.h> 12 #include <openssl/lhash.h> 13 #include <openssl/trace.h> 14 #include "eng_local.h" 15 16 /* The type of the items in the table */ 17 struct st_engine_pile { 18 /* The 'nid' of this algorithm/mode */ 19 int nid; 20 /* ENGINEs that implement this algorithm/mode. */ 21 STACK_OF(ENGINE) *sk; 22 /* The default ENGINE to perform this algorithm/mode. */ 23 ENGINE *funct; 24 /* 25 * Zero if 'sk' is newer than the cached 'funct', non-zero otherwise 26 */ 27 int uptodate; 28 }; 29 30 /* The type exposed in eng_local.h */ 31 struct st_engine_table { 32 LHASH_OF(ENGINE_PILE) piles; 33 }; /* ENGINE_TABLE */ 34 35 typedef struct st_engine_pile_doall { 36 engine_table_doall_cb *cb; 37 void *arg; 38 } ENGINE_PILE_DOALL; 39 40 /* Global flags (ENGINE_TABLE_FLAG_***). */ 41 static unsigned int table_flags = 0; 42 43 /* API function manipulating 'table_flags' */ 44 unsigned int ENGINE_get_table_flags(void) 45 { 46 return table_flags; 47 } 48 49 void ENGINE_set_table_flags(unsigned int flags) 50 { 51 table_flags = flags; 52 } 53 54 /* Internal functions for the "piles" hash table */ 55 static unsigned long engine_pile_hash(const ENGINE_PILE *c) 56 { 57 return c->nid; 58 } 59 60 static int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b) 61 { 62 return a->nid - b->nid; 63 } 64 65 static int int_table_check(ENGINE_TABLE **t, int create) 66 { 67 LHASH_OF(ENGINE_PILE) *lh; 68 69 if (*t) 70 return 1; 71 if (!create) 72 return 0; 73 if ((lh = lh_ENGINE_PILE_new(engine_pile_hash, engine_pile_cmp)) == NULL) 74 return 0; 75 *t = (ENGINE_TABLE *)lh; 76 return 1; 77 } 78 79 /* 80 * Privately exposed (via eng_local.h) functions for adding and/or removing 81 * ENGINEs from the implementation table 82 */ 83 int engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup, 84 ENGINE *e, const int *nids, int num_nids, 85 int setdefault) 86 { 87 int ret = 0, added = 0; 88 ENGINE_PILE tmplate, *fnd; 89 90 if (!CRYPTO_THREAD_write_lock(global_engine_lock)) 91 return 0; 92 if (!(*table)) 93 added = 1; 94 if (!int_table_check(table, 1)) 95 goto end; 96 /* The cleanup callback needs to be added */ 97 if (added && !engine_cleanup_add_first(cleanup)) { 98 lh_ENGINE_PILE_free(&(*table)->piles); 99 *table = NULL; 100 goto end; 101 } 102 while (num_nids--) { 103 tmplate.nid = *nids; 104 fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate); 105 if (!fnd) { 106 fnd = OPENSSL_malloc(sizeof(*fnd)); 107 if (fnd == NULL) 108 goto end; 109 fnd->uptodate = 1; 110 fnd->nid = *nids; 111 fnd->sk = sk_ENGINE_new_null(); 112 if (!fnd->sk) { 113 OPENSSL_free(fnd); 114 goto end; 115 } 116 fnd->funct = NULL; 117 (void)lh_ENGINE_PILE_insert(&(*table)->piles, fnd); 118 if (lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate) != fnd) { 119 sk_ENGINE_free(fnd->sk); 120 OPENSSL_free(fnd); 121 goto end; 122 } 123 } 124 /* A registration shouldn't add duplicate entries */ 125 (void)sk_ENGINE_delete_ptr(fnd->sk, e); 126 /* 127 * if 'setdefault', this ENGINE goes to the head of the list 128 */ 129 if (!sk_ENGINE_push(fnd->sk, e)) 130 goto end; 131 /* "touch" this ENGINE_PILE */ 132 fnd->uptodate = 0; 133 if (setdefault) { 134 if (!engine_unlocked_init(e)) { 135 ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INIT_FAILED); 136 goto end; 137 } 138 if (fnd->funct) 139 engine_unlocked_finish(fnd->funct, 0); 140 fnd->funct = e; 141 fnd->uptodate = 1; 142 } 143 nids++; 144 } 145 ret = 1; 146 end: 147 CRYPTO_THREAD_unlock(global_engine_lock); 148 return ret; 149 } 150 151 static void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e) 152 { 153 int n; 154 /* Iterate the 'c->sk' stack removing any occurrence of 'e' */ 155 while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) { 156 (void)sk_ENGINE_delete(pile->sk, n); 157 pile->uptodate = 0; 158 } 159 if (pile->funct == e) { 160 engine_unlocked_finish(e, 0); 161 pile->funct = NULL; 162 } 163 } 164 165 IMPLEMENT_LHASH_DOALL_ARG(ENGINE_PILE, ENGINE); 166 167 void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e) 168 { 169 if (!CRYPTO_THREAD_write_lock(global_engine_lock)) 170 /* Can't return a value. :( */ 171 return; 172 if (int_table_check(table, 0)) 173 lh_ENGINE_PILE_doall_ENGINE(&(*table)->piles, int_unregister_cb, e); 174 CRYPTO_THREAD_unlock(global_engine_lock); 175 } 176 177 static void int_cleanup_cb_doall(ENGINE_PILE *p) 178 { 179 if (p == NULL) 180 return; 181 sk_ENGINE_free(p->sk); 182 if (p->funct) 183 engine_unlocked_finish(p->funct, 0); 184 OPENSSL_free(p); 185 } 186 187 void engine_table_cleanup(ENGINE_TABLE **table) 188 { 189 if (!CRYPTO_THREAD_write_lock(global_engine_lock)) 190 return; 191 if (*table) { 192 lh_ENGINE_PILE_doall(&(*table)->piles, int_cleanup_cb_doall); 193 lh_ENGINE_PILE_free(&(*table)->piles); 194 *table = NULL; 195 } 196 CRYPTO_THREAD_unlock(global_engine_lock); 197 } 198 199 /* return a functional reference for a given 'nid' */ 200 ENGINE *ossl_engine_table_select(ENGINE_TABLE **table, int nid, 201 const char *f, int l) 202 { 203 ENGINE *ret = NULL; 204 ENGINE_PILE tmplate, *fnd = NULL; 205 int initres, loop = 0; 206 207 #ifndef OPENSSL_NO_AUTOLOAD_CONFIG 208 /* Load the config before trying to check if engines are available */ 209 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); 210 #endif 211 212 if (!(*table)) { 213 OSSL_TRACE3(ENGINE_TABLE, 214 "%s:%d, nid=%d, nothing registered!\n", 215 f, l, nid); 216 return NULL; 217 } 218 219 if (!CRYPTO_THREAD_write_lock(global_engine_lock)) 220 return NULL; 221 222 ERR_set_mark(); 223 /* 224 * Check again inside the lock otherwise we could race against cleanup 225 * operations. But don't worry about a debug printout 226 */ 227 if (!int_table_check(table, 0)) 228 goto end; 229 tmplate.nid = nid; 230 fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate); 231 if (!fnd) 232 goto end; 233 if (fnd->funct && engine_unlocked_init(fnd->funct)) { 234 OSSL_TRACE4(ENGINE_TABLE, 235 "%s:%d, nid=%d, using ENGINE '%s' cached\n", 236 f, l, nid, fnd->funct->id); 237 ret = fnd->funct; 238 goto end; 239 } 240 if (fnd->uptodate) { 241 ret = fnd->funct; 242 goto end; 243 } 244 trynext: 245 ret = sk_ENGINE_value(fnd->sk, loop++); 246 if (!ret) { 247 OSSL_TRACE3(ENGINE_TABLE, 248 "%s:%d, nid=%d, " 249 "no registered implementations would initialise\n", 250 f, l, nid); 251 goto end; 252 } 253 /* Try to initialise the ENGINE? */ 254 if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT)) 255 initres = engine_unlocked_init(ret); 256 else 257 initres = 0; 258 if (initres) { 259 /* Update 'funct' */ 260 if ((fnd->funct != ret) && engine_unlocked_init(ret)) { 261 /* If there was a previous default we release it. */ 262 if (fnd->funct) 263 engine_unlocked_finish(fnd->funct, 0); 264 fnd->funct = ret; 265 OSSL_TRACE4(ENGINE_TABLE, 266 "%s:%d, nid=%d, setting default to '%s'\n", 267 f, l, nid, ret->id); 268 } 269 OSSL_TRACE4(ENGINE_TABLE, 270 "%s:%d, nid=%d, using newly initialised '%s'\n", 271 f, l, nid, ret->id); 272 goto end; 273 } 274 goto trynext; 275 end: 276 /* 277 * If it failed, it is unlikely to succeed again until some future 278 * registrations have taken place. In all cases, we cache. 279 */ 280 if (fnd) 281 fnd->uptodate = 1; 282 if (ret) 283 OSSL_TRACE4(ENGINE_TABLE, 284 "%s:%d, nid=%d, caching ENGINE '%s'\n", 285 f, l, nid, ret->id); 286 else 287 OSSL_TRACE3(ENGINE_TABLE, 288 "%s:%d, nid=%d, caching 'no matching ENGINE'\n", 289 f, l, nid); 290 CRYPTO_THREAD_unlock(global_engine_lock); 291 /* 292 * Whatever happened, any failed init()s are not failures in this 293 * context, so clear our error state. 294 */ 295 ERR_pop_to_mark(); 296 return ret; 297 } 298 299 /* Table enumeration */ 300 301 static void int_dall(const ENGINE_PILE *pile, ENGINE_PILE_DOALL *dall) 302 { 303 dall->cb(pile->nid, pile->sk, pile->funct, dall->arg); 304 } 305 306 IMPLEMENT_LHASH_DOALL_ARG_CONST(ENGINE_PILE, ENGINE_PILE_DOALL); 307 308 void engine_table_doall(ENGINE_TABLE *table, engine_table_doall_cb *cb, 309 void *arg) 310 { 311 ENGINE_PILE_DOALL dall; 312 dall.cb = cb; 313 dall.arg = arg; 314 if (table) 315 lh_ENGINE_PILE_doall_ENGINE_PILE_DOALL(&table->piles, int_dall, &dall); 316 } 317