• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_local.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_local.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' */
ENGINE_get_table_flags(void)43 unsigned int ENGINE_get_table_flags(void)
44 {
45     return table_flags;
46 }
47 
ENGINE_set_table_flags(unsigned int flags)48 void ENGINE_set_table_flags(unsigned int flags)
49 {
50     table_flags = flags;
51 }
52 
53 /* Internal functions for the "piles" hash table */
engine_pile_hash(const ENGINE_PILE * c)54 static unsigned long engine_pile_hash(const ENGINE_PILE *c)
55 {
56     return c->nid;
57 }
58 
engine_pile_cmp(const ENGINE_PILE * a,const ENGINE_PILE * b)59 static int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b)
60 {
61     return a->nid - b->nid;
62 }
63 
int_table_check(ENGINE_TABLE ** t,int create)64 static int int_table_check(ENGINE_TABLE **t, int create)
65 {
66     LHASH_OF(ENGINE_PILE) *lh;
67 
68     if (*t)
69         return 1;
70     if (!create)
71         return 0;
72     if ((lh = lh_ENGINE_PILE_new(engine_pile_hash, engine_pile_cmp)) == NULL)
73         return 0;
74     *t = (ENGINE_TABLE *)lh;
75     return 1;
76 }
77 
78 /*
79  * Privately exposed (via eng_local.h) functions for adding and/or removing
80  * ENGINEs from the implementation table
81  */
engine_table_register(ENGINE_TABLE ** table,ENGINE_CLEANUP_CB * cleanup,ENGINE * e,const int * nids,int num_nids,int setdefault)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 {
86     int ret = 0, added = 0;
87     ENGINE_PILE tmplate, *fnd;
88     CRYPTO_THREAD_write_lock(global_engine_lock);
89     if (!(*table))
90         added = 1;
91     if (!int_table_check(table, 1))
92         goto end;
93     if (added)
94         /* The cleanup callback needs to be added */
95         engine_cleanup_add_first(cleanup);
96     while (num_nids--) {
97         tmplate.nid = *nids;
98         fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
99         if (!fnd) {
100             fnd = OPENSSL_malloc(sizeof(*fnd));
101             if (fnd == NULL)
102                 goto end;
103             fnd->uptodate = 1;
104             fnd->nid = *nids;
105             fnd->sk = sk_ENGINE_new_null();
106             if (!fnd->sk) {
107                 OPENSSL_free(fnd);
108                 goto end;
109             }
110             fnd->funct = NULL;
111             (void)lh_ENGINE_PILE_insert(&(*table)->piles, fnd);
112             if (lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate) != fnd) {
113                 sk_ENGINE_free(fnd->sk);
114                 OPENSSL_free(fnd);
115                 goto end;
116             }
117         }
118         /* A registration shouldn't add duplicate entries */
119         (void)sk_ENGINE_delete_ptr(fnd->sk, e);
120         /*
121          * if 'setdefault', this ENGINE goes to the head of the list
122          */
123         if (!sk_ENGINE_push(fnd->sk, e))
124             goto end;
125         /* "touch" this ENGINE_PILE */
126         fnd->uptodate = 0;
127         if (setdefault) {
128             if (!engine_unlocked_init(e)) {
129                 ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER,
130                           ENGINE_R_INIT_FAILED);
131                 goto end;
132             }
133             if (fnd->funct)
134                 engine_unlocked_finish(fnd->funct, 0);
135             fnd->funct = e;
136             fnd->uptodate = 1;
137         }
138         nids++;
139     }
140     ret = 1;
141  end:
142     CRYPTO_THREAD_unlock(global_engine_lock);
143     return ret;
144 }
145 
int_unregister_cb(ENGINE_PILE * pile,ENGINE * e)146 static void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e)
147 {
148     int n;
149     /* Iterate the 'c->sk' stack removing any occurrence of 'e' */
150     while ((n = sk_ENGINE_find(pile->sk, e)) >= 0) {
151         (void)sk_ENGINE_delete(pile->sk, n);
152         pile->uptodate = 0;
153     }
154     if (pile->funct == e) {
155         engine_unlocked_finish(e, 0);
156         pile->funct = NULL;
157     }
158 }
159 
160 IMPLEMENT_LHASH_DOALL_ARG(ENGINE_PILE, ENGINE);
161 
engine_table_unregister(ENGINE_TABLE ** table,ENGINE * e)162 void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e)
163 {
164     CRYPTO_THREAD_write_lock(global_engine_lock);
165     if (int_table_check(table, 0))
166         lh_ENGINE_PILE_doall_ENGINE(&(*table)->piles, int_unregister_cb, e);
167     CRYPTO_THREAD_unlock(global_engine_lock);
168 }
169 
int_cleanup_cb_doall(ENGINE_PILE * p)170 static void int_cleanup_cb_doall(ENGINE_PILE *p)
171 {
172     if (!p)
173         return;
174     sk_ENGINE_free(p->sk);
175     if (p->funct)
176         engine_unlocked_finish(p->funct, 0);
177     OPENSSL_free(p);
178 }
179 
engine_table_cleanup(ENGINE_TABLE ** table)180 void engine_table_cleanup(ENGINE_TABLE **table)
181 {
182     CRYPTO_THREAD_write_lock(global_engine_lock);
183     if (*table) {
184         lh_ENGINE_PILE_doall(&(*table)->piles, int_cleanup_cb_doall);
185         lh_ENGINE_PILE_free(&(*table)->piles);
186         *table = NULL;
187     }
188     CRYPTO_THREAD_unlock(global_engine_lock);
189 }
190 
191 /* return a functional reference for a given 'nid' */
192 #ifndef ENGINE_TABLE_DEBUG
engine_table_select(ENGINE_TABLE ** table,int nid)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 {
199     ENGINE *ret = NULL;
200     ENGINE_PILE tmplate, *fnd = NULL;
201     int initres, loop = 0;
202 
203     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     }
210     ERR_set_mark();
211     CRYPTO_THREAD_write_lock(global_engine_lock);
212     /*
213      * Check again inside the lock otherwise we could race against cleanup
214      * operations. But don't worry about a fprintf(stderr).
215      */
216     if (!int_table_check(table, 0))
217         goto end;
218     tmplate.nid = nid;
219     fnd = lh_ENGINE_PILE_retrieve(&(*table)->piles, &tmplate);
220     if (!fnd)
221         goto end;
222     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         goto end;
229     }
230     if (fnd->uptodate) {
231         ret = fnd->funct;
232         goto end;
233     }
234  trynext:
235     ret = sk_ENGINE_value(fnd->sk, loop++);
236     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     }
243     /* Try to initialise the ENGINE? */
244     if ((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT))
245         initres = engine_unlocked_init(ret);
246     else
247         initres = 0;
248     if (initres) {
249         /* Update 'funct' */
250         if ((fnd->funct != ret) && engine_unlocked_init(ret)) {
251             /* If there was a previous default we release it. */
252             if (fnd->funct)
253                 engine_unlocked_finish(fnd->funct, 0);
254             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     }
266     goto trynext;
267  end:
268     /*
269      * If it failed, it is unlikely to succeed again until some future
270      * registrations have taken place. In all cases, we cache.
271      */
272     if (fnd)
273         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     /*
284      * Whatever happened, any failed init()s are not failures in this
285      * context, so clear our error state.
286      */
287     ERR_pop_to_mark();
288     return ret;
289 }
290 
291 /* Table enumeration */
292 
int_dall(const ENGINE_PILE * pile,ENGINE_PILE_DOALL * dall)293 static void int_dall(const ENGINE_PILE *pile, ENGINE_PILE_DOALL *dall)
294 {
295     dall->cb(pile->nid, pile->sk, pile->funct, dall->arg);
296 }
297 
298 IMPLEMENT_LHASH_DOALL_ARG_CONST(ENGINE_PILE, ENGINE_PILE_DOALL);
299 
engine_table_doall(ENGINE_TABLE * table,engine_table_doall_cb * cb,void * arg)300 void engine_table_doall(ENGINE_TABLE *table, engine_table_doall_cb *cb,
301                         void *arg)
302 {
303     ENGINE_PILE_DOALL dall;
304     dall.cb = cb;
305     dall.arg = arg;
306     if (table)
307         lh_ENGINE_PILE_doall_ENGINE_PILE_DOALL(&table->piles, int_dall, &dall);
308 }
309