|
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 © Portions copyright (c) 2006 Nokia Corporation. All rights reserved. |
|
56 */ |
|
57 |
|
58 |
|
59 #include "cryptlib.h" |
|
60 #include <openssl/evp.h> |
|
61 #include <openssl/lhash.h> |
|
62 #include "eng_int.h" |
|
63 #if (defined(SYMBIAN) && (defined(__WINSCW__) || defined(__WINS__))) |
|
64 #include "libcrypto_wsd_macros.h" |
|
65 #include "libcrypto_wsd.h" |
|
66 #endif |
|
67 |
|
68 /* The type of the items in the table */ |
|
69 typedef struct st_engine_pile |
|
70 { |
|
71 /* The 'nid' of this algorithm/mode */ |
|
72 int nid; |
|
73 /* ENGINEs that implement this algorithm/mode. */ |
|
74 STACK_OF(ENGINE) *sk; |
|
75 /* The default ENGINE to perform this algorithm/mode. */ |
|
76 ENGINE *funct; |
|
77 /* Zero if 'sk' is newer than the cached 'funct', non-zero otherwise */ |
|
78 int uptodate; |
|
79 } ENGINE_PILE; |
|
80 |
|
81 /* The type exposed in eng_int.h */ |
|
82 struct st_engine_table |
|
83 { |
|
84 LHASH piles; |
|
85 }; /* ENGINE_TABLE */ |
|
86 |
|
87 /* Global flags (ENGINE_TABLE_FLAG_***). */ |
|
88 #ifndef EMULATOR |
|
89 static unsigned int table_flags = 0; |
|
90 #else |
|
91 GET_STATIC_VAR_FROM_TLS(table_flags,eng_table,unsigned int) |
|
92 #define table_flags (*GET_WSD_VAR_NAME(table_flags,eng_table, s)()) |
|
93 #endif |
|
94 |
|
95 /* API function manipulating 'table_flags' */ |
|
96 EXPORT_C unsigned int ENGINE_get_table_flags(void) |
|
97 { |
|
98 return table_flags; |
|
99 } |
|
100 EXPORT_C void ENGINE_set_table_flags(unsigned int flags) |
|
101 { |
|
102 table_flags = flags; |
|
103 } |
|
104 |
|
105 /* Internal functions for the "piles" hash table */ |
|
106 static unsigned long engine_pile_hash(const ENGINE_PILE *c) |
|
107 { |
|
108 return c->nid; |
|
109 } |
|
110 static int 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_hash, const ENGINE_PILE *) |
|
115 static IMPLEMENT_LHASH_COMP_FN(engine_pile_cmp, const ENGINE_PILE *) |
|
116 static int int_table_check(ENGINE_TABLE **t, int create) |
|
117 { |
|
118 LHASH *lh; |
|
119 if(*t) return 1; |
|
120 if(!create) return 0; |
|
121 if((lh = lh_new(LHASH_HASH_FN(engine_pile_hash), |
|
122 LHASH_COMP_FN(engine_pile_cmp))) == NULL) |
|
123 return 0; |
|
124 *t = (ENGINE_TABLE *)lh; |
|
125 return 1; |
|
126 } |
|
127 |
|
128 /* Privately exposed (via eng_int.h) functions for adding and/or removing |
|
129 * ENGINEs from the implementation table */ |
|
130 EXPORT_C int engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup, |
|
131 ENGINE *e, const int *nids, int num_nids, int setdefault) |
|
132 { |
|
133 int ret = 0, added = 0; |
|
134 ENGINE_PILE tmplate, *fnd; |
|
135 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); |
|
136 if(!(*table)) |
|
137 added = 1; |
|
138 if(!int_table_check(table, 1)) |
|
139 goto end; |
|
140 if(added) |
|
141 /* The cleanup callback needs to be added */ |
|
142 engine_cleanup_add_first(cleanup); |
|
143 while(num_nids--) |
|
144 { |
|
145 tmplate.nid = *nids; |
|
146 fnd = lh_retrieve(&(*table)->piles, &tmplate); |
|
147 if(!fnd) |
|
148 { |
|
149 fnd = OPENSSL_malloc(sizeof(ENGINE_PILE)); |
|
150 if(!fnd) goto end; |
|
151 fnd->uptodate = 0; |
|
152 fnd->nid = *nids; |
|
153 fnd->sk = sk_ENGINE_new_null(); |
|
154 if(!fnd->sk) |
|
155 { |
|
156 OPENSSL_free(fnd); |
|
157 goto end; |
|
158 } |
|
159 fnd->funct = NULL; |
|
160 lh_insert(&(*table)->piles, fnd); |
|
161 } |
|
162 /* A registration shouldn't add duplciate entries */ |
|
163 (void)sk_ENGINE_delete_ptr(fnd->sk, e); |
|
164 /* if 'setdefault', this ENGINE goes to the head of the list */ |
|
165 if(!sk_ENGINE_push(fnd->sk, e)) |
|
166 goto end; |
|
167 /* "touch" this ENGINE_PILE */ |
|
168 fnd->uptodate = 1; |
|
169 if(setdefault) |
|
170 { |
|
171 if(!engine_unlocked_init(e)) |
|
172 { |
|
173 ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER, |
|
174 ENGINE_R_INIT_FAILED); |
|
175 goto end; |
|
176 } |
|
177 if(fnd->funct) |
|
178 engine_unlocked_finish(fnd->funct, 0); |
|
179 fnd->funct = e; |
|
180 } |
|
181 nids++; |
|
182 } |
|
183 ret = 1; |
|
184 end: |
|
185 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); |
|
186 return ret; |
|
187 } |
|
188 static void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e) |
|
189 { |
|
190 int n; |
|
191 /* Iterate the 'c->sk' stack removing any occurance of 'e' */ |
|
192 while((n = sk_ENGINE_find(pile->sk, e)) >= 0) |
|
193 { |
|
194 (void)sk_ENGINE_delete(pile->sk, n); |
|
195 /* "touch" this ENGINE_CIPHER */ |
|
196 pile->uptodate = 1; |
|
197 } |
|
198 if(pile->funct == e) |
|
199 { |
|
200 engine_unlocked_finish(e, 0); |
|
201 pile->funct = NULL; |
|
202 } |
|
203 } |
|
204 static IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb,ENGINE_PILE *,ENGINE *) |
|
205 EXPORT_C void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e) |
|
206 { |
|
207 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); |
|
208 if(int_table_check(table, 0)) |
|
209 lh_doall_arg(&(*table)->piles, |
|
210 LHASH_DOALL_ARG_FN(int_unregister_cb), e); |
|
211 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); |
|
212 } |
|
213 |
|
214 static void int_cleanup_cb(ENGINE_PILE *p) |
|
215 { |
|
216 sk_ENGINE_free(p->sk); |
|
217 if(p->funct) |
|
218 engine_unlocked_finish(p->funct, 0); |
|
219 OPENSSL_free(p); |
|
220 } |
|
221 static IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb,ENGINE_PILE *) |
|
222 EXPORT_C void engine_table_cleanup(ENGINE_TABLE **table) |
|
223 { |
|
224 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); |
|
225 if(*table) |
|
226 { |
|
227 lh_doall(&(*table)->piles, LHASH_DOALL_FN(int_cleanup_cb)); |
|
228 lh_free(&(*table)->piles); |
|
229 *table = NULL; |
|
230 } |
|
231 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); |
|
232 } |
|
233 |
|
234 /* return a functional reference for a given 'nid' */ |
|
235 #ifndef ENGINE_TABLE_DEBUG |
|
236 EXPORT_C ENGINE *engine_table_select(ENGINE_TABLE **table, int nid) |
|
237 #else |
|
238 EXPORT_C ENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f, int l) |
|
239 #endif |
|
240 { |
|
241 ENGINE *ret = NULL; |
|
242 ENGINE_PILE tmplate, *fnd=NULL; |
|
243 int initres, loop = 0; |
|
244 |
|
245 if(!(*table)) |
|
246 { |
|
247 #ifdef ENGINE_TABLE_DEBUG |
|
248 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing " |
|
249 "registered!\n", f, l, nid); |
|
250 #endif |
|
251 return NULL; |
|
252 } |
|
253 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); |
|
254 /* Check again inside the lock otherwise we could race against cleanup |
|
255 * operations. But don't worry about a fprintf(stderr). */ |
|
256 if(!int_table_check(table, 0)) goto end; |
|
257 tmplate.nid = nid; |
|
258 fnd = lh_retrieve(&(*table)->piles, &tmplate); |
|
259 if(!fnd) goto end; |
|
260 if(fnd->funct && engine_unlocked_init(fnd->funct)) |
|
261 { |
|
262 #ifdef ENGINE_TABLE_DEBUG |
|
263 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " |
|
264 "ENGINE '%s' cached\n", f, l, nid, fnd->funct->id); |
|
265 #endif |
|
266 ret = fnd->funct; |
|
267 goto end; |
|
268 } |
|
269 if(fnd->uptodate) |
|
270 { |
|
271 ret = fnd->funct; |
|
272 goto end; |
|
273 } |
|
274 trynext: |
|
275 ret = sk_ENGINE_value(fnd->sk, loop++); |
|
276 if(!ret) |
|
277 { |
|
278 #ifdef ENGINE_TABLE_DEBUG |
|
279 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no " |
|
280 "registered implementations would initialise\n", |
|
281 f, l, nid); |
|
282 #endif |
|
283 goto end; |
|
284 } |
|
285 /* Try to initialise the ENGINE? */ |
|
286 if((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT)) |
|
287 initres = engine_unlocked_init(ret); |
|
288 else |
|
289 initres = 0; |
|
290 if(initres) |
|
291 { |
|
292 /* Update 'funct' */ |
|
293 if((fnd->funct != ret) && engine_unlocked_init(ret)) |
|
294 { |
|
295 /* If there was a previous default we release it. */ |
|
296 if(fnd->funct) |
|
297 engine_unlocked_finish(fnd->funct, 0); |
|
298 fnd->funct = ret; |
|
299 #ifdef ENGINE_TABLE_DEBUG |
|
300 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, " |
|
301 "setting default to '%s'\n", f, l, nid, ret->id); |
|
302 #endif |
|
303 } |
|
304 #ifdef ENGINE_TABLE_DEBUG |
|
305 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using " |
|
306 "newly initialised '%s'\n", f, l, nid, ret->id); |
|
307 #endif |
|
308 goto end; |
|
309 } |
|
310 goto trynext; |
|
311 end: |
|
312 /* If it failed, it is unlikely to succeed again until some future |
|
313 * registrations have taken place. In all cases, we cache. */ |
|
314 if(fnd) fnd->uptodate = 1; |
|
315 #ifdef ENGINE_TABLE_DEBUG |
|
316 if(ret) |
|
317 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " |
|
318 "ENGINE '%s'\n", f, l, nid, ret->id); |
|
319 else |
|
320 fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching " |
|
321 "'no matching ENGINE'\n", f, l, nid); |
|
322 #endif |
|
323 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); |
|
324 /* Whatever happened, any failed init()s are not failures in this |
|
325 * context, so clear our error state. */ |
|
326 ERR_clear_error(); |
|
327 return ret; |
|
328 } |