xref: /openssl/crypto/engine/eng_list.c (revision 7ed6de99)
1 /*
2  * Copyright 2001-2024 The OpenSSL Project Authors. All Rights Reserved.
3  * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved
4  *
5  * Licensed under the Apache License 2.0 (the "License").  You may not use
6  * this file except in compliance with the License.  You can obtain a copy
7  * in the file LICENSE in the source distribution or at
8  * https://www.openssl.org/source/license.html
9  */
10 
11 /* We need to use some engine deprecated APIs */
12 #define OPENSSL_SUPPRESS_DEPRECATED
13 
14 #include "eng_local.h"
15 
16 /*
17  * The linked-list of pointers to engine types. engine_list_head incorporates
18  * an implicit structural reference but engine_list_tail does not - the
19  * latter is a computational optimization and only points to something that
20  * is already pointed to by its predecessor in the list (or engine_list_head
21  * itself). In the same way, the use of the "prev" pointer in each ENGINE is
22  * to save excessive list iteration, it doesn't correspond to an extra
23  * structural reference. Hence, engine_list_head, and each non-null "next"
24  * pointer account for the list itself assuming exactly 1 structural
25  * reference on each list member.
26  */
27 static ENGINE *engine_list_head = NULL;
28 static ENGINE *engine_list_tail = NULL;
29 
30 /*
31  * The linked list of currently loaded dynamic engines.
32  */
33 static ENGINE *engine_dyn_list_head = NULL;
34 static ENGINE *engine_dyn_list_tail = NULL;
35 
36 /*
37  * This cleanup function is only needed internally. If it should be called,
38  * we register it with the "engine_cleanup_int()" stack to be called during
39  * cleanup.
40  */
41 
engine_list_cleanup(void)42 static void engine_list_cleanup(void)
43 {
44     ENGINE *iterator = engine_list_head;
45 
46     while (iterator != NULL) {
47         ENGINE_remove(iterator);
48         iterator = engine_list_head;
49     }
50     return;
51 }
52 
53 /*
54  * These static functions starting with a lower case "engine_" always take
55  * place when global_engine_lock has been locked up.
56  */
engine_list_add(ENGINE * e)57 static int engine_list_add(ENGINE *e)
58 {
59     int conflict = 0;
60     ENGINE *iterator = NULL;
61     int ref;
62 
63     if (e == NULL) {
64         ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
65         return 0;
66     }
67     iterator = engine_list_head;
68     while (iterator && !conflict) {
69         conflict = (strcmp(iterator->id, e->id) == 0);
70         iterator = iterator->next;
71     }
72     if (conflict) {
73         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_CONFLICTING_ENGINE_ID);
74         return 0;
75     }
76 
77     /*
78      * Having the engine in the list assumes a structural reference.
79      */
80     if (!CRYPTO_UP_REF(&e->struct_ref, &ref)) {
81             ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR);
82             return 0;
83     }
84     ENGINE_REF_PRINT(e, 0, 1);
85     if (engine_list_head == NULL) {
86         /* We are adding to an empty list. */
87         if (engine_list_tail != NULL) {
88             CRYPTO_DOWN_REF(&e->struct_ref, &ref);
89             ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR);
90             return 0;
91         }
92         /*
93          * The first time the list allocates, we should register the cleanup.
94          */
95         if (!engine_cleanup_add_last(engine_list_cleanup)) {
96             CRYPTO_DOWN_REF(&e->struct_ref, &ref);
97             ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR);
98             return 0;
99         }
100         engine_list_head = e;
101         e->prev = NULL;
102     } else {
103         /* We are adding to the tail of an existing list. */
104         if ((engine_list_tail == NULL) || (engine_list_tail->next != NULL)) {
105             CRYPTO_DOWN_REF(&e->struct_ref, &ref);
106             ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR);
107             return 0;
108         }
109         engine_list_tail->next = e;
110         e->prev = engine_list_tail;
111     }
112 
113     /* However it came to be, e is the last item in the list. */
114     engine_list_tail = e;
115     e->next = NULL;
116     return 1;
117 }
118 
engine_list_remove(ENGINE * e)119 static int engine_list_remove(ENGINE *e)
120 {
121     ENGINE *iterator;
122 
123     if (e == NULL) {
124         ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
125         return 0;
126     }
127     /* We need to check that e is in our linked list! */
128     iterator = engine_list_head;
129     while (iterator && (iterator != e))
130         iterator = iterator->next;
131     if (iterator == NULL) {
132         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_ENGINE_IS_NOT_IN_LIST);
133         return 0;
134     }
135     /* un-link e from the chain. */
136     if (e->next)
137         e->next->prev = e->prev;
138     if (e->prev)
139         e->prev->next = e->next;
140     /* Correct our head/tail if necessary. */
141     if (engine_list_head == e)
142         engine_list_head = e->next;
143     if (engine_list_tail == e)
144         engine_list_tail = e->prev;
145     engine_free_util(e, 0);
146     return 1;
147 }
148 
149 /* Add engine to dynamic engine list. */
engine_add_dynamic_id(ENGINE * e,ENGINE_DYNAMIC_ID dynamic_id,int not_locked)150 int engine_add_dynamic_id(ENGINE *e, ENGINE_DYNAMIC_ID dynamic_id,
151                           int not_locked)
152 {
153     int result = 0;
154     ENGINE *iterator = NULL;
155 
156     if (e == NULL)
157         return 0;
158 
159     if (e->dynamic_id == NULL && dynamic_id == NULL)
160         return 0;
161 
162     if (not_locked && !CRYPTO_THREAD_write_lock(global_engine_lock))
163         return 0;
164 
165     if (dynamic_id != NULL) {
166         iterator = engine_dyn_list_head;
167         while (iterator != NULL) {
168             if (iterator->dynamic_id == dynamic_id)
169                 goto err;
170             iterator = iterator->next;
171         }
172         if (e->dynamic_id != NULL)
173             goto err;
174         e->dynamic_id = dynamic_id;
175     }
176 
177     if (engine_dyn_list_head == NULL) {
178         /* We are adding to an empty list. */
179         if (engine_dyn_list_tail != NULL)
180             goto err;
181         engine_dyn_list_head = e;
182         e->prev_dyn = NULL;
183     } else {
184         /* We are adding to the tail of an existing list. */
185         if (engine_dyn_list_tail == NULL
186             || engine_dyn_list_tail->next_dyn != NULL)
187             goto err;
188         engine_dyn_list_tail->next_dyn = e;
189         e->prev_dyn = engine_dyn_list_tail;
190     }
191 
192     engine_dyn_list_tail = e;
193     e->next_dyn = NULL;
194     result = 1;
195 
196  err:
197     if (not_locked)
198         CRYPTO_THREAD_unlock(global_engine_lock);
199     return result;
200 }
201 
202 /* Remove engine from dynamic engine list. */
engine_remove_dynamic_id(ENGINE * e,int not_locked)203 void engine_remove_dynamic_id(ENGINE *e, int not_locked)
204 {
205     if (e == NULL || e->dynamic_id == NULL)
206         return;
207 
208     if (not_locked && !CRYPTO_THREAD_write_lock(global_engine_lock))
209         return;
210 
211     e->dynamic_id = NULL;
212 
213     /* un-link e from the chain. */
214     if (e->next_dyn != NULL)
215         e->next_dyn->prev_dyn = e->prev_dyn;
216     if (e->prev_dyn != NULL)
217         e->prev_dyn->next_dyn = e->next_dyn;
218     /* Correct our head/tail if necessary. */
219     if (engine_dyn_list_head == e)
220         engine_dyn_list_head = e->next_dyn;
221     if (engine_dyn_list_tail == e)
222         engine_dyn_list_tail = e->prev_dyn;
223 
224     if (not_locked)
225         CRYPTO_THREAD_unlock(global_engine_lock);
226 }
227 
228 /* Get the first/last "ENGINE" type available. */
ENGINE_get_first(void)229 ENGINE *ENGINE_get_first(void)
230 {
231     ENGINE *ret;
232 
233     if (!RUN_ONCE(&engine_lock_init, do_engine_lock_init)) {
234         /* Maybe this should be raised in do_engine_lock_init() */
235         ERR_raise(ERR_LIB_ENGINE, ERR_R_CRYPTO_LIB);
236         return NULL;
237     }
238 
239     if (!CRYPTO_THREAD_write_lock(global_engine_lock))
240         return NULL;
241     ret = engine_list_head;
242     if (ret) {
243         int ref;
244 
245         if (!CRYPTO_UP_REF(&ret->struct_ref, &ref)) {
246             CRYPTO_THREAD_unlock(global_engine_lock);
247             ERR_raise(ERR_LIB_ENGINE, ERR_R_CRYPTO_LIB);
248             return NULL;
249         }
250         ENGINE_REF_PRINT(ret, 0, 1);
251     }
252     CRYPTO_THREAD_unlock(global_engine_lock);
253     return ret;
254 }
255 
ENGINE_get_last(void)256 ENGINE *ENGINE_get_last(void)
257 {
258     ENGINE *ret;
259 
260     if (!RUN_ONCE(&engine_lock_init, do_engine_lock_init)) {
261         /* Maybe this should be raised in do_engine_lock_init() */
262         ERR_raise(ERR_LIB_ENGINE, ERR_R_CRYPTO_LIB);
263         return NULL;
264     }
265 
266     if (!CRYPTO_THREAD_write_lock(global_engine_lock))
267         return NULL;
268     ret = engine_list_tail;
269     if (ret) {
270         int ref;
271 
272         if (!CRYPTO_UP_REF(&ret->struct_ref, &ref)) {
273             CRYPTO_THREAD_unlock(global_engine_lock);
274             ERR_raise(ERR_LIB_ENGINE, ERR_R_CRYPTO_LIB);
275             return NULL;
276         }
277         ENGINE_REF_PRINT(ret, 0, 1);
278     }
279     CRYPTO_THREAD_unlock(global_engine_lock);
280     return ret;
281 }
282 
283 /* Iterate to the next/previous "ENGINE" type (NULL = end of the list). */
ENGINE_get_next(ENGINE * e)284 ENGINE *ENGINE_get_next(ENGINE *e)
285 {
286     ENGINE *ret = NULL;
287     if (e == NULL) {
288         ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
289         return NULL;
290     }
291     if (!CRYPTO_THREAD_write_lock(global_engine_lock))
292         return NULL;
293     ret = e->next;
294     if (ret) {
295         int ref;
296 
297         /* Return a valid structural reference to the next ENGINE */
298         if (!CRYPTO_UP_REF(&ret->struct_ref, &ref)) {
299             CRYPTO_THREAD_unlock(global_engine_lock);
300             ERR_raise(ERR_LIB_ENGINE, ERR_R_CRYPTO_LIB);
301             return NULL;
302         }
303         ENGINE_REF_PRINT(ret, 0, 1);
304     }
305     CRYPTO_THREAD_unlock(global_engine_lock);
306     /* Release the structural reference to the previous ENGINE */
307     ENGINE_free(e);
308     return ret;
309 }
310 
ENGINE_get_prev(ENGINE * e)311 ENGINE *ENGINE_get_prev(ENGINE *e)
312 {
313     ENGINE *ret = NULL;
314     if (e == NULL) {
315         ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
316         return NULL;
317     }
318     if (!CRYPTO_THREAD_write_lock(global_engine_lock))
319         return NULL;
320     ret = e->prev;
321     if (ret) {
322         int ref;
323 
324         /* Return a valid structural reference to the next ENGINE */
325         if (!CRYPTO_UP_REF(&ret->struct_ref, &ref)) {
326             CRYPTO_THREAD_unlock(global_engine_lock);
327             ERR_raise(ERR_LIB_ENGINE, ERR_R_CRYPTO_LIB);
328             return NULL;
329         }
330         ENGINE_REF_PRINT(ret, 0, 1);
331     }
332     CRYPTO_THREAD_unlock(global_engine_lock);
333     /* Release the structural reference to the previous ENGINE */
334     ENGINE_free(e);
335     return ret;
336 }
337 
338 /* Add another "ENGINE" type into the list. */
ENGINE_add(ENGINE * e)339 int ENGINE_add(ENGINE *e)
340 {
341     int to_return = 1;
342     if (e == NULL) {
343         ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
344         return 0;
345     }
346     if ((e->id == NULL) || (e->name == NULL)) {
347         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_ID_OR_NAME_MISSING);
348         return 0;
349     }
350     if (!CRYPTO_THREAD_write_lock(global_engine_lock))
351         return 0;
352     if (!engine_list_add(e)) {
353         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR);
354         to_return = 0;
355     }
356     CRYPTO_THREAD_unlock(global_engine_lock);
357     return to_return;
358 }
359 
360 /* Remove an existing "ENGINE" type from the array. */
ENGINE_remove(ENGINE * e)361 int ENGINE_remove(ENGINE *e)
362 {
363     int to_return = 1;
364     if (e == NULL) {
365         ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
366         return 0;
367     }
368     if (!CRYPTO_THREAD_write_lock(global_engine_lock))
369         return 0;
370     if (!engine_list_remove(e)) {
371         ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INTERNAL_LIST_ERROR);
372         to_return = 0;
373     }
374     CRYPTO_THREAD_unlock(global_engine_lock);
375     return to_return;
376 }
377 
engine_cpy(ENGINE * dest,const ENGINE * src)378 static void engine_cpy(ENGINE *dest, const ENGINE *src)
379 {
380     dest->id = src->id;
381     dest->name = src->name;
382     dest->rsa_meth = src->rsa_meth;
383 #ifndef OPENSSL_NO_DSA
384     dest->dsa_meth = src->dsa_meth;
385 #endif
386 #ifndef OPENSSL_NO_DH
387     dest->dh_meth = src->dh_meth;
388 #endif
389 #ifndef OPENSSL_NO_EC
390     dest->ec_meth = src->ec_meth;
391 #endif
392     dest->rand_meth = src->rand_meth;
393     dest->ciphers = src->ciphers;
394     dest->digests = src->digests;
395     dest->pkey_meths = src->pkey_meths;
396     dest->destroy = src->destroy;
397     dest->init = src->init;
398     dest->finish = src->finish;
399     dest->ctrl = src->ctrl;
400     dest->load_privkey = src->load_privkey;
401     dest->load_pubkey = src->load_pubkey;
402     dest->cmd_defns = src->cmd_defns;
403     dest->flags = src->flags;
404     dest->dynamic_id = src->dynamic_id;
405     engine_add_dynamic_id(dest, NULL, 0);
406 }
407 
ENGINE_by_id(const char * id)408 ENGINE *ENGINE_by_id(const char *id)
409 {
410     ENGINE *iterator;
411     const char *load_dir = NULL;
412     if (id == NULL) {
413         ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
414         return NULL;
415     }
416     ENGINE_load_builtin_engines();
417 
418     if (!RUN_ONCE(&engine_lock_init, do_engine_lock_init)) {
419         /* Maybe this should be raised in do_engine_lock_init() */
420         ERR_raise(ERR_LIB_ENGINE, ERR_R_CRYPTO_LIB);
421         return NULL;
422     }
423 
424     if (!CRYPTO_THREAD_write_lock(global_engine_lock))
425         return NULL;
426     iterator = engine_list_head;
427     while (iterator && (strcmp(id, iterator->id) != 0))
428         iterator = iterator->next;
429     if (iterator != NULL) {
430         /*
431          * We need to return a structural reference. If this is an ENGINE
432          * type that returns copies, make a duplicate - otherwise increment
433          * the existing ENGINE's reference count.
434          */
435         if (iterator->flags & ENGINE_FLAGS_BY_ID_COPY) {
436             ENGINE *cp = ENGINE_new();
437             if (cp == NULL)
438                 iterator = NULL;
439             else {
440                 engine_cpy(cp, iterator);
441                 iterator = cp;
442             }
443         } else {
444             int ref;
445 
446             if (!CRYPTO_UP_REF(&iterator->struct_ref, &ref)) {
447                 CRYPTO_THREAD_unlock(global_engine_lock);
448                 ERR_raise(ERR_LIB_ENGINE, ERR_R_CRYPTO_LIB);
449                 return NULL;
450             }
451             ENGINE_REF_PRINT(iterator, 0, 1);
452         }
453     }
454     CRYPTO_THREAD_unlock(global_engine_lock);
455     if (iterator != NULL)
456         return iterator;
457     /*
458      * Prevent infinite recursion if we're looking for the dynamic engine.
459      */
460     if (strcmp(id, "dynamic")) {
461         if ((load_dir = ossl_safe_getenv("OPENSSL_ENGINES")) == NULL)
462             load_dir = ossl_get_enginesdir();
463         iterator = ENGINE_by_id("dynamic");
464         if (!iterator || !ENGINE_ctrl_cmd_string(iterator, "ID", id, 0) ||
465             !ENGINE_ctrl_cmd_string(iterator, "DIR_LOAD", "2", 0) ||
466             !ENGINE_ctrl_cmd_string(iterator, "DIR_ADD",
467                                     load_dir, 0) ||
468             !ENGINE_ctrl_cmd_string(iterator, "LIST_ADD", "1", 0) ||
469             !ENGINE_ctrl_cmd_string(iterator, "LOAD", NULL, 0))
470             goto notfound;
471         return iterator;
472     }
473  notfound:
474     ENGINE_free(iterator);
475     ERR_raise_data(ERR_LIB_ENGINE, ENGINE_R_NO_SUCH_ENGINE, "id=%s", id);
476     return NULL;
477     /* EEK! Experimental code ends */
478 }
479 
ENGINE_up_ref(ENGINE * e)480 int ENGINE_up_ref(ENGINE *e)
481 {
482     int i;
483     if (e == NULL) {
484         ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
485         return 0;
486     }
487     CRYPTO_UP_REF(&e->struct_ref, &i);
488     return 1;
489 }
490