1 /* $OpenBSD: eng_table.c,v 1.9 2017/01/29 17:49:23 beck 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 				ENGINEerror(ENGINE_R_INIT_FAILED);
176 				goto end;
177 			}
178 			if (fnd->funct)
179 				engine_unlocked_finish(fnd->funct, 0);
180 			fnd->funct = e;
181 			fnd->uptodate = 1;
182 		}
183 		nids++;
184 	}
185 	ret = 1;
186 end:
187 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
188 	return ret;
189 }
190 
191 static void
192 int_unregister_cb_doall_arg(ENGINE_PILE *pile, ENGINE *e)
193 {
194 	int n;
195 
196 	/* Iterate the 'c->sk' stack removing any occurance of 'e' */
197 	while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) {
198 		(void)sk_ENGINE_delete(pile->sk, n);
199 		pile->uptodate = 0;
200 	}
201 	if (pile->funct == e) {
202 		engine_unlocked_finish(e, 0);
203 		pile->funct = NULL;
204 	}
205 }
206 static IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb, ENGINE_PILE, ENGINE)
207 
208 void
209 engine_table_unregister(ENGINE_TABLE **table, ENGINE *e)
210 {
211 	CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
212 	if (int_table_check(table, 0))
213 		lh_ENGINE_PILE_doall_arg(&(*table)->piles,
214 		    LHASH_DOALL_ARG_FN(int_unregister_cb), ENGINE, e);
215 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
216 }
217 
218 static void
219 int_cleanup_cb_doall(ENGINE_PILE *p)
220 {
221 	sk_ENGINE_free(p->sk);
222 	if (p->funct)
223 		engine_unlocked_finish(p->funct, 0);
224 	free(p);
225 }
226 static IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb, ENGINE_PILE)
227 
228 void
229 engine_table_cleanup(ENGINE_TABLE **table)
230 {
231 	CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
232 	if (*table) {
233 		lh_ENGINE_PILE_doall(&(*table)->piles,
234 		    LHASH_DOALL_FN(int_cleanup_cb));
235 		lh_ENGINE_PILE_free(&(*table)->piles);
236 		*table = NULL;
237 	}
238 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
239 }
240 
241 /* return a functional reference for a given 'nid' */
242 #ifndef ENGINE_TABLE_DEBUG
243 ENGINE *
244 engine_table_select(ENGINE_TABLE **table, int nid)
245 #else
246 ENGINE *
247 engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f, int l)
248 #endif
249 {
250 	ENGINE *ret = NULL;
251 	ENGINE_PILE tmplate, *fnd = NULL;
252 	int initres, loop = 0;
253 
254 	if (!(*table)) {
255 #ifdef ENGINE_TABLE_DEBUG
256 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing "
257 		    "registered!\n", f, l, nid);
258 #endif
259 		return NULL;
260 	}
261 	ERR_set_mark();
262 	CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
263 	/* Check again inside the lock otherwise we could race against cleanup
264 	 * operations. But don't worry about a fprintf(stderr). */
265 	if (!int_table_check(table, 0))
266 		goto end;
267 	tmplate.nid = nid;
268 	fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
269 	if (!fnd)
270 		goto end;
271 	if (fnd->funct && engine_unlocked_init(fnd->funct)) {
272 #ifdef ENGINE_TABLE_DEBUG
273 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
274 		    "ENGINE '%s' cached\n", f, l, nid, fnd->funct->id);
275 #endif
276 		ret = fnd->funct;
277 		goto end;
278 	}
279 	if (fnd->uptodate) {
280 		ret = fnd->funct;
281 		goto end;
282 	}
283 trynext:
284 	ret = sk_ENGINE_value(fnd->sk, loop++);
285 	if (!ret) {
286 #ifdef ENGINE_TABLE_DEBUG
287 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no "
288 		    "registered implementations would initialise\n", f, l, nid);
289 #endif
290 		goto end;
291 	}
292 	/* Try to initialise the ENGINE? */
293 	if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT))
294 		initres = engine_unlocked_init(ret);
295 	else
296 		initres = 0;
297 	if (initres) {
298 		/* Update 'funct' */
299 		if ((fnd->funct != ret) && engine_unlocked_init(ret)) {
300 			/* If there was a previous default we release it. */
301 			if (fnd->funct)
302 				engine_unlocked_finish(fnd->funct, 0);
303 			fnd->funct = ret;
304 #ifdef ENGINE_TABLE_DEBUG
305 			fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, "
306 			    "setting default to '%s'\n", f, l, nid, ret->id);
307 #endif
308 		}
309 #ifdef ENGINE_TABLE_DEBUG
310 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
311 		    "newly initialised '%s'\n", f, l, nid, ret->id);
312 #endif
313 		goto end;
314 	}
315 	goto trynext;
316 end:
317 	/* If it failed, it is unlikely to succeed again until some future
318 	 * registrations have taken place. In all cases, we cache. */
319 	if (fnd)
320 		fnd->uptodate = 1;
321 #ifdef ENGINE_TABLE_DEBUG
322 	if (ret)
323 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
324 		    "ENGINE '%s'\n", f, l, nid, ret->id);
325 	else
326 		fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
327 		    "'no matching ENGINE'\n", f, l, nid);
328 #endif
329 	CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
330 	/* Whatever happened, any failed init()s are not failures in this
331 	 * context, so clear our error state. */
332 	ERR_pop_to_mark();
333 	return ret;
334 }
335 
336 /* Table enumeration */
337 
338 static void
339 int_cb_doall_arg(ENGINE_PILE *pile, ENGINE_PILE_DOALL *dall)
340 {
341 	dall->cb(pile->nid, pile->sk, pile->funct, dall->arg);
342 }
343 static IMPLEMENT_LHASH_DOALL_ARG_FN(int_cb, ENGINE_PILE, ENGINE_PILE_DOALL)
344 
345 void
346 engine_table_doall(ENGINE_TABLE *table, engine_table_doall_cb *cb, void *arg)
347 {
348 	ENGINE_PILE_DOALL dall;
349 
350 	dall.cb = cb;
351 	dall.arg = arg;
352 	lh_ENGINE_PILE_doall_arg(&table->piles, LHASH_DOALL_ARG_FN(int_cb),
353 	    ENGINE_PILE_DOALL, &dall);
354 }
355