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