/src/openssl31/crypto/engine/eng_table.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | { |
46 | 0 | return table_flags; |
47 | 0 | } |
48 | | |
49 | | void ENGINE_set_table_flags(unsigned int flags) |
50 | 0 | { |
51 | 0 | table_flags = flags; |
52 | 0 | } |
53 | | |
54 | | /* Internal functions for the "piles" hash table */ |
55 | | static unsigned long engine_pile_hash(const ENGINE_PILE *c) |
56 | 0 | { |
57 | 0 | return c->nid; |
58 | 0 | } |
59 | | |
60 | | static int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b) |
61 | 0 | { |
62 | 0 | return a->nid - b->nid; |
63 | 0 | } |
64 | | |
65 | | static int int_table_check(ENGINE_TABLE **t, int create) |
66 | 0 | { |
67 | 0 | LHASH_OF(ENGINE_PILE) *lh; |
68 | |
|
69 | 0 | if (*t) |
70 | 0 | return 1; |
71 | 0 | if (!create) |
72 | 0 | return 0; |
73 | 0 | if ((lh = lh_ENGINE_PILE_new(engine_pile_hash, engine_pile_cmp)) == NULL) |
74 | 0 | return 0; |
75 | 0 | *t = (ENGINE_TABLE *)lh; |
76 | 0 | return 1; |
77 | 0 | } |
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 | 0 | { |
87 | 0 | int ret = 0, added = 0; |
88 | 0 | ENGINE_PILE tmplate, *fnd; |
89 | |
|
90 | 0 | if (!CRYPTO_THREAD_write_lock(global_engine_lock)) |
91 | 0 | return 0; |
92 | 0 | if (!(*table)) |
93 | 0 | added = 1; |
94 | 0 | if (!int_table_check(table, 1)) |
95 | 0 | goto end; |
96 | | /* The cleanup callback needs to be added */ |
97 | 0 | if (added && !engine_cleanup_add_first(cleanup)) { |
98 | 0 | lh_ENGINE_PILE_free(&(*table)->piles); |
99 | 0 | *table = NULL; |
100 | 0 | goto end; |
101 | 0 | } |
102 | 0 | while (num_nids--) { |
103 | 0 | tmplate.nid = *nids; |
104 | 0 | fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate); |
105 | 0 | if (!fnd) { |
106 | 0 | fnd = OPENSSL_malloc(sizeof(*fnd)); |
107 | 0 | if (fnd == NULL) |
108 | 0 | goto end; |
109 | 0 | fnd->uptodate = 1; |
110 | 0 | fnd->nid = *nids; |
111 | 0 | fnd->sk = sk_ENGINE_new_null(); |
112 | 0 | if (!fnd->sk) { |
113 | 0 | OPENSSL_free(fnd); |
114 | 0 | goto end; |
115 | 0 | } |
116 | 0 | fnd->funct = NULL; |
117 | 0 | (void)lh_ENGINE_PILE_insert(&(*table)->piles, fnd); |
118 | 0 | if (lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate) != fnd) { |
119 | 0 | sk_ENGINE_free(fnd->sk); |
120 | 0 | OPENSSL_free(fnd); |
121 | 0 | goto end; |
122 | 0 | } |
123 | 0 | } |
124 | | /* A registration shouldn't add duplicate entries */ |
125 | 0 | (void)sk_ENGINE_delete_ptr(fnd->sk, e); |
126 | | /* |
127 | | * if 'setdefault', this ENGINE goes to the head of the list |
128 | | */ |
129 | 0 | if (!sk_ENGINE_push(fnd->sk, e)) |
130 | 0 | goto end; |
131 | | /* "touch" this ENGINE_PILE */ |
132 | 0 | fnd->uptodate = 0; |
133 | 0 | if (setdefault) { |
134 | 0 | if (!engine_unlocked_init(e)) { |
135 | 0 | ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INIT_FAILED); |
136 | 0 | goto end; |
137 | 0 | } |
138 | 0 | if (fnd->funct) |
139 | 0 | engine_unlocked_finish(fnd->funct, 0); |
140 | 0 | fnd->funct = e; |
141 | 0 | fnd->uptodate = 1; |
142 | 0 | } |
143 | 0 | nids++; |
144 | 0 | } |
145 | 0 | ret = 1; |
146 | 0 | end: |
147 | 0 | CRYPTO_THREAD_unlock(global_engine_lock); |
148 | 0 | return ret; |
149 | 0 | } |
150 | | |
151 | | static void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e) |
152 | 0 | { |
153 | 0 | int n; |
154 | | /* Iterate the 'c->sk' stack removing any occurrence of 'e' */ |
155 | 0 | while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) { |
156 | 0 | (void)sk_ENGINE_delete(pile->sk, n); |
157 | 0 | pile->uptodate = 0; |
158 | 0 | } |
159 | 0 | if (pile->funct == e) { |
160 | 0 | engine_unlocked_finish(e, 0); |
161 | 0 | pile->funct = NULL; |
162 | 0 | } |
163 | 0 | } |
164 | | |
165 | | IMPLEMENT_LHASH_DOALL_ARG(ENGINE_PILE, ENGINE); |
166 | | |
167 | | void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e) |
168 | 0 | { |
169 | 0 | if (!CRYPTO_THREAD_write_lock(global_engine_lock)) |
170 | | /* Can't return a value. :( */ |
171 | 0 | return; |
172 | 0 | if (int_table_check(table, 0)) |
173 | 0 | lh_ENGINE_PILE_doall_ENGINE(&(*table)->piles, int_unregister_cb, e); |
174 | 0 | CRYPTO_THREAD_unlock(global_engine_lock); |
175 | 0 | } |
176 | | |
177 | | static void int_cleanup_cb_doall(ENGINE_PILE *p) |
178 | 0 | { |
179 | 0 | if (p == NULL) |
180 | 0 | return; |
181 | 0 | sk_ENGINE_free(p->sk); |
182 | 0 | if (p->funct) |
183 | 0 | engine_unlocked_finish(p->funct, 0); |
184 | 0 | OPENSSL_free(p); |
185 | 0 | } |
186 | | |
187 | | void engine_table_cleanup(ENGINE_TABLE **table) |
188 | 0 | { |
189 | 0 | if (!CRYPTO_THREAD_write_lock(global_engine_lock)) |
190 | 0 | return; |
191 | 0 | if (*table) { |
192 | 0 | lh_ENGINE_PILE_doall(&(*table)->piles, int_cleanup_cb_doall); |
193 | 0 | lh_ENGINE_PILE_free(&(*table)->piles); |
194 | 0 | *table = NULL; |
195 | 0 | } |
196 | 0 | CRYPTO_THREAD_unlock(global_engine_lock); |
197 | 0 | } |
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 | 421M | { |
203 | 421M | ENGINE *ret = NULL; |
204 | 421M | ENGINE_PILE tmplate, *fnd = NULL; |
205 | 421M | int initres, loop = 0; |
206 | | |
207 | 421M | #ifndef OPENSSL_NO_AUTOLOAD_CONFIG |
208 | | /* Load the config before trying to check if engines are available */ |
209 | 421M | OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); |
210 | 421M | #endif |
211 | | |
212 | 421M | if (!(*table)) { |
213 | 421M | OSSL_TRACE3(ENGINE_TABLE, |
214 | 421M | "%s:%d, nid=%d, nothing registered!\n", |
215 | 421M | f, l, nid); |
216 | 421M | return NULL; |
217 | 421M | } |
218 | | |
219 | 0 | if (!CRYPTO_THREAD_write_lock(global_engine_lock)) |
220 | 0 | return NULL; |
221 | | |
222 | 0 | 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 | 0 | if (!int_table_check(table, 0)) |
228 | 0 | goto end; |
229 | 0 | tmplate.nid = nid; |
230 | 0 | fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate); |
231 | 0 | if (!fnd) |
232 | 0 | goto end; |
233 | 0 | if (fnd->funct && engine_unlocked_init(fnd->funct)) { |
234 | 0 | OSSL_TRACE4(ENGINE_TABLE, |
235 | 0 | "%s:%d, nid=%d, using ENGINE '%s' cached\n", |
236 | 0 | f, l, nid, fnd->funct->id); |
237 | 0 | ret = fnd->funct; |
238 | 0 | goto end; |
239 | 0 | } |
240 | 0 | if (fnd->uptodate) { |
241 | 0 | ret = fnd->funct; |
242 | 0 | goto end; |
243 | 0 | } |
244 | 0 | trynext: |
245 | 0 | ret = sk_ENGINE_value(fnd->sk, loop++); |
246 | 0 | if (!ret) { |
247 | 0 | OSSL_TRACE3(ENGINE_TABLE, |
248 | 0 | "%s:%d, nid=%d, " |
249 | 0 | "no registered implementations would initialise\n", |
250 | 0 | f, l, nid); |
251 | 0 | goto end; |
252 | 0 | } |
253 | | /* Try to initialise the ENGINE? */ |
254 | 0 | if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT)) |
255 | 0 | initres = engine_unlocked_init(ret); |
256 | 0 | else |
257 | 0 | initres = 0; |
258 | 0 | if (initres) { |
259 | | /* Update 'funct' */ |
260 | 0 | if ((fnd->funct != ret) && engine_unlocked_init(ret)) { |
261 | | /* If there was a previous default we release it. */ |
262 | 0 | if (fnd->funct) |
263 | 0 | engine_unlocked_finish(fnd->funct, 0); |
264 | 0 | fnd->funct = ret; |
265 | 0 | OSSL_TRACE4(ENGINE_TABLE, |
266 | 0 | "%s:%d, nid=%d, setting default to '%s'\n", |
267 | 0 | f, l, nid, ret->id); |
268 | 0 | } |
269 | 0 | OSSL_TRACE4(ENGINE_TABLE, |
270 | 0 | "%s:%d, nid=%d, using newly initialised '%s'\n", |
271 | 0 | f, l, nid, ret->id); |
272 | 0 | goto end; |
273 | 0 | } |
274 | 0 | goto trynext; |
275 | 0 | 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 | 0 | if (fnd) |
281 | 0 | fnd->uptodate = 1; |
282 | 0 | if (ret) |
283 | 0 | OSSL_TRACE4(ENGINE_TABLE, |
284 | 0 | "%s:%d, nid=%d, caching ENGINE '%s'\n", |
285 | 0 | f, l, nid, ret->id); |
286 | 0 | else |
287 | 0 | OSSL_TRACE3(ENGINE_TABLE, |
288 | 0 | "%s:%d, nid=%d, caching 'no matching ENGINE'\n", |
289 | 0 | f, l, nid); |
290 | 0 | 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 | 0 | ERR_pop_to_mark(); |
296 | 0 | return ret; |
297 | 0 | } |
298 | | |
299 | | /* Table enumeration */ |
300 | | |
301 | | static void int_dall(const ENGINE_PILE *pile, ENGINE_PILE_DOALL *dall) |
302 | 0 | { |
303 | 0 | dall->cb(pile->nid, pile->sk, pile->funct, dall->arg); |
304 | 0 | } |
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 | 3.58M | { |
311 | 3.58M | ENGINE_PILE_DOALL dall; |
312 | 3.58M | dall.cb = cb; |
313 | 3.58M | dall.arg = arg; |
314 | 3.58M | if (table) |
315 | 0 | lh_ENGINE_PILE_doall_ENGINE_PILE_DOALL(&table->piles, int_dall, &dall); |
316 | 3.58M | } |