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