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