1 /* $OpenBSD: eng_table.c,v 1.7 2014/07/11 08:44:48 jsing Exp $ */
2 /* ====================================================================
3  * Copyright (c) 2001 The OpenSSL Project.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. All advertising materials mentioning features or use of this
18  *    software must display the following acknowledgment:
19  *    "This product includes software developed by the OpenSSL Project
20  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
21  *
22  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
23  *    endorse or promote products derived from this software without
24  *    prior written permission. For written permission, please contact
25  *    licensing@OpenSSL.org.
26  *
27  * 5. Products derived from this software may not be called "OpenSSL"
28  *    nor may "OpenSSL" appear in their names without prior written
29  *    permission of the OpenSSL Project.
30  *
31  * 6. Redistributions of any form whatsoever must retain the following
32  *    acknowledgment:
33  *    "This product includes software developed by the OpenSSL Project
34  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
35  *
36  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
37  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
42  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
43  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
45  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
47  * OF THE POSSIBILITY OF SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This product includes cryptographic software written by Eric Young
51  * (eay@cryptsoft.com).  This product includes software written by Tim
52  * Hudson (tjh@cryptsoft.com).
53  *
54  */
55 
56 #include <openssl/err.h>
57 #include <openssl/evp.h>
58 #include <openssl/lhash.h>
59 
60 #include "eng_int.h"
61 
62 /* The type of the items in the table */
63 typedef struct st_engine_pile {
64 	/* The 'nid' of this algorithm/mode */
65 	int nid;
66 	/* ENGINEs that implement this algorithm/mode. */
67 	STACK_OF(ENGINE) *sk;
68 	/* The default ENGINE to perform this algorithm/mode. */
69 	ENGINE *funct;
70 	/* Zero if 'sk' is newer than the cached 'funct', non-zero otherwise */
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
91 ENGINE_get_table_flags(void)
92 {
93 	return table_flags;
94 }
95 
96 void
97 ENGINE_set_table_flags(unsigned int flags)
98 {
99 	table_flags = flags;
100 }
101 
102 /* Internal functions for the "piles" hash table */
103 static unsigned long
104 engine_pile_hash(const ENGINE_PILE *c)
105 {
106 	return c->nid;
107 }
108 
109 static int
110 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, ENGINE_PILE)
115 static IMPLEMENT_LHASH_COMP_FN(engine_pile, ENGINE_PILE)
116 
117 static int
118 int_table_check(ENGINE_TABLE **t, int create)
119 {
120 	LHASH_OF(ENGINE_PILE) *lh;
121 
122 	if (*t)
123 		return 1;
124 	if (!create)
125 		return 0;
126 	if ((lh = lh_ENGINE_PILE_new()) == NULL)
127 		return 0;
128 	*t = (ENGINE_TABLE *)lh;
129 	return 1;
130 }
131 
132 /* Privately exposed (via eng_int.h) functions for adding and/or removing
133  * ENGINEs from the implementation table */
134 int
135 engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup,
136     ENGINE *e, const int *nids, int num_nids, int setdefault)
137 {
138 	int ret = 0, added = 0;
139 	ENGINE_PILE tmplate, *fnd;
140 
141 	CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
142 	if (!(*table))
143 		added = 1;
144 	if (!int_table_check(table, 1))
145 		goto end;
146 	if (added)
147 		/* The cleanup callback needs to be added */
148 		engine_cleanup_add_first(cleanup);
149 	while (num_nids--) {
150 		tmplate.nid = *nids;
151 		fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
152 		if (!fnd) {
153 			fnd = malloc(sizeof(ENGINE_PILE));
154 			if (!fnd)
155 				goto end;
156 			fnd->uptodate = 1;
157 			fnd->nid = *nids;
158 			fnd->sk = sk_ENGINE_new_null();
159 			if (!fnd->sk) {
160 				free(fnd);
161 				goto end;
162 			}
163 			fnd->funct = NULL;
164 			(void)lh_ENGINE_PILE_insert(&(*table)->piles, fnd);
165 		}
166 		/* A registration shouldn't add duplciate entries */
167 		(void)sk_ENGINE_delete_ptr(fnd->sk, e);
168 		/* if 'setdefault', this ENGINE goes to the head of the list */
169 		if (!sk_ENGINE_push(fnd->sk, e))
170 			goto end;
171 		/* "touch" this ENGINE_PILE */
172 		fnd->uptodate = 0;
173 		if (setdefault) {
174 			if (!engine_unlocked_init(e)) {
175 				ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER,
176 				    ENGINE_R_INIT_FAILED);
177 				goto end;
178 			}
179 			if (fnd->funct)
180 				engine_unlocked_finish(fnd->funct, 0);
181 			fnd->funct = e;
182 			fnd->uptodate = 1;
183 		}
184 		nids++;
185 	}
186 	ret = 1;
187 end:
188 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
189 	return ret;
190 }
191 
192 static void
193 int_unregister_cb_doall_arg(ENGINE_PILE *pile, ENGINE *e)
194 {
195 	int n;
196 
197 	/* Iterate the 'c->sk' stack removing any occurance of 'e' */
198 	while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) {
199 		(void)sk_ENGINE_delete(pile->sk, n);
200 		pile->uptodate = 0;
201 	}
202 	if (pile->funct == e) {
203 		engine_unlocked_finish(e, 0);
204 		pile->funct = NULL;
205 	}
206 }
207 static IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb, ENGINE_PILE, ENGINE)
208 
209 void
210 engine_table_unregister(ENGINE_TABLE **table, ENGINE *e)
211 {
212 	CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
213 	if (int_table_check(table, 0))
214 		lh_ENGINE_PILE_doall_arg(&(*table)->piles,
215 		    LHASH_DOALL_ARG_FN(int_unregister_cb), ENGINE, e);
216 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
217 }
218 
219 static void
220 int_cleanup_cb_doall(ENGINE_PILE *p)
221 {
222 	sk_ENGINE_free(p->sk);
223 	if (p->funct)
224 		engine_unlocked_finish(p->funct, 0);
225 	free(p);
226 }
227 static IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb, ENGINE_PILE)
228 
229 void
230 engine_table_cleanup(ENGINE_TABLE **table)
231 {
232 	CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
233 	if (*table) {
234 		lh_ENGINE_PILE_doall(&(*table)->piles,
235 		    LHASH_DOALL_FN(int_cleanup_cb));
236 		lh_ENGINE_PILE_free(&(*table)->piles);
237 		*table = NULL;
238 	}
239 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
240 }
241 
242 /* return a functional reference for a given 'nid' */
243 #ifndef ENGINE_TABLE_DEBUG
244 ENGINE *
245 engine_table_select(ENGINE_TABLE **table, int nid)
246 #else
247 ENGINE *
248 engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f, int l)
249 #endif
250 {
251 	ENGINE *ret = NULL;
252 	ENGINE_PILE tmplate, *fnd = NULL;
253 	int initres, loop = 0;
254 
255 	if (!(*table)) {
256 #ifdef ENGINE_TABLE_DEBUG
257 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing "
258 		    "registered!\n", f, l, nid);
259 #endif
260 		return NULL;
261 	}
262 	ERR_set_mark();
263 	CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
264 	/* Check again inside the lock otherwise we could race against cleanup
265 	 * operations. But don't worry about a fprintf(stderr). */
266 	if (!int_table_check(table, 0))
267 		goto end;
268 	tmplate.nid = nid;
269 	fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
270 	if (!fnd)
271 		goto end;
272 	if (fnd->funct && engine_unlocked_init(fnd->funct)) {
273 #ifdef ENGINE_TABLE_DEBUG
274 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
275 		    "ENGINE '%s' cached\n", f, l, nid, fnd->funct->id);
276 #endif
277 		ret = fnd->funct;
278 		goto end;
279 	}
280 	if (fnd->uptodate) {
281 		ret = fnd->funct;
282 		goto end;
283 	}
284 trynext:
285 	ret = sk_ENGINE_value(fnd->sk, loop++);
286 	if (!ret) {
287 #ifdef ENGINE_TABLE_DEBUG
288 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no "
289 		    "registered implementations would initialise\n", f, l, nid);
290 #endif
291 		goto end;
292 	}
293 	/* Try to initialise the ENGINE? */
294 	if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT))
295 		initres = engine_unlocked_init(ret);
296 	else
297 		initres = 0;
298 	if (initres) {
299 		/* Update 'funct' */
300 		if ((fnd->funct != ret) && engine_unlocked_init(ret)) {
301 			/* If there was a previous default we release it. */
302 			if (fnd->funct)
303 				engine_unlocked_finish(fnd->funct, 0);
304 			fnd->funct = ret;
305 #ifdef ENGINE_TABLE_DEBUG
306 			fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, "
307 			    "setting default to '%s'\n", f, l, nid, ret->id);
308 #endif
309 		}
310 #ifdef ENGINE_TABLE_DEBUG
311 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
312 		    "newly initialised '%s'\n", f, l, nid, ret->id);
313 #endif
314 		goto end;
315 	}
316 	goto trynext;
317 end:
318 	/* If it failed, it is unlikely to succeed again until some future
319 	 * registrations have taken place. In all cases, we cache. */
320 	if (fnd)
321 		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 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
331 	/* Whatever happened, any failed init()s are not failures in this
332 	 * context, so clear our error state. */
333 	ERR_pop_to_mark();
334 	return ret;
335 }
336 
337 /* Table enumeration */
338 
339 static void
340 int_cb_doall_arg(ENGINE_PILE *pile, ENGINE_PILE_DOALL *dall)
341 {
342 	dall->cb(pile->nid, pile->sk, pile->funct, dall->arg);
343 }
344 static IMPLEMENT_LHASH_DOALL_ARG_FN(int_cb, ENGINE_PILE, ENGINE_PILE_DOALL)
345 
346 void
347 engine_table_doall(ENGINE_TABLE *table, engine_table_doall_cb *cb, void *arg)
348 {
349 	ENGINE_PILE_DOALL dall;
350 
351 	dall.cb = cb;
352 	dall.arg = arg;
353 	lh_ENGINE_PILE_doall_arg(&table->piles, LHASH_DOALL_ARG_FN(int_cb),
354 	    ENGINE_PILE_DOALL, &dall);
355 }
356