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