1 /*
2  * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (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 <openssl/crypto.h>
11 #include <openssl/core_dispatch.h>
12 #include "crypto/cryptlib.h"
13 #include "prov/providercommon.h"
14 #include "internal/thread_once.h"
15 #include "internal/threads_common.h"
16 #include "crypto/context.h"
17 
18 #ifdef FIPS_MODULE
19 #include "prov/provider_ctx.h"
20 
21 /*
22  * Thread aware code may want to be told about thread stop events. We register
23  * to hear about those thread stop events when we see a new thread has started.
24  * We call the ossl_init_thread_start function to do that. In the FIPS provider
25  * we have our own copy of ossl_init_thread_start, which cascades notifications
26  * about threads stopping from libcrypto to all the code in the FIPS provider
27  * that needs to know about it.
28  *
29  * The FIPS provider tells libcrypto about which threads it is interested in
30  * by calling "c_thread_start" which is a function pointer created during
31  * provider initialisation (i.e. OSSL_provider_init).
32  */
33 extern OSSL_FUNC_core_thread_start_fn *c_thread_start;
34 #endif
35 
36 typedef struct thread_event_handler_st THREAD_EVENT_HANDLER;
37 struct thread_event_handler_st {
38 #ifndef FIPS_MODULE
39     const void *index;
40 #endif
41     void *arg;
42     OSSL_thread_stop_handler_fn handfn;
43     THREAD_EVENT_HANDLER *next;
44 };
45 
46 #ifndef FIPS_MODULE
47 DEFINE_SPECIAL_STACK_OF(THREAD_EVENT_HANDLER_PTR, THREAD_EVENT_HANDLER *)
48 
49 typedef struct global_tevent_register_st GLOBAL_TEVENT_REGISTER;
50 struct global_tevent_register_st {
51     STACK_OF(THREAD_EVENT_HANDLER_PTR) *skhands;
52     CRYPTO_RWLOCK *lock;
53 };
54 
55 static GLOBAL_TEVENT_REGISTER *glob_tevent_reg = NULL;
56 
57 static CRYPTO_ONCE tevent_register_runonce = CRYPTO_ONCE_STATIC_INIT;
58 
DEFINE_RUN_ONCE_STATIC(create_global_tevent_register)59 DEFINE_RUN_ONCE_STATIC(create_global_tevent_register)
60 {
61     glob_tevent_reg = OPENSSL_zalloc(sizeof(*glob_tevent_reg));
62     if (glob_tevent_reg == NULL)
63         return 0;
64 
65     glob_tevent_reg->skhands = sk_THREAD_EVENT_HANDLER_PTR_new_null();
66     glob_tevent_reg->lock = CRYPTO_THREAD_lock_new();
67     if (glob_tevent_reg->skhands == NULL || glob_tevent_reg->lock == NULL) {
68         sk_THREAD_EVENT_HANDLER_PTR_free(glob_tevent_reg->skhands);
69         CRYPTO_THREAD_lock_free(glob_tevent_reg->lock);
70         OPENSSL_free(glob_tevent_reg);
71         glob_tevent_reg = NULL;
72         return 0;
73     }
74 
75     return 1;
76 }
77 
get_global_tevent_register(void)78 static GLOBAL_TEVENT_REGISTER *get_global_tevent_register(void)
79 {
80     if (!RUN_ONCE(&tevent_register_runonce, create_global_tevent_register))
81         return NULL;
82     return glob_tevent_reg;
83 }
84 #endif
85 
86 #ifndef FIPS_MODULE
87 /*
88  * Since per-thread-specific-data destructors are not universally
89  * available, i.e. not on Windows, only below CRYPTO_THREAD_LOCAL key
90  * is assumed to have destructor associated. And then an effort is made
91  * to call this single destructor on non-pthread platform[s].
92  *
93  * Initial value is "impossible". It is used as guard value to shortcut
94  * destructor for threads terminating before libcrypto is initialized or
95  * after it's de-initialized. Access to the key doesn't have to be
96  * serialized for the said threads, because they didn't use libcrypto
97  * and it doesn't matter if they pick "impossible" or dereference real
98  * key value and pull NULL past initialization in the first thread that
99  * intends to use libcrypto.
100  */
101 static union {
102     long sane;
103     CRYPTO_THREAD_LOCAL value;
104 } destructor_key = { -1 };
105 
106 static int  init_thread_push_handlers(THREAD_EVENT_HANDLER **hands);
107 static void init_thread_remove_handlers(THREAD_EVENT_HANDLER **handsin);
108 static void init_thread_destructor(void *hands);
109 static int  init_thread_deregister(void *arg, int all);
110 #endif
111 static void init_thread_stop(void *arg, THREAD_EVENT_HANDLER **hands);
112 
get_thread_event_handler(OSSL_LIB_CTX * ctx)113 static THREAD_EVENT_HANDLER ** get_thread_event_handler(OSSL_LIB_CTX *ctx)
114 {
115 #ifdef FIPS_MODULE
116     return CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_TEVENT_KEY, ctx);
117 #else
118     if (destructor_key.sane != -1)
119         return CRYPTO_THREAD_get_local(&destructor_key.value);
120     return NULL;
121 #endif
122 }
123 
set_thread_event_handler(OSSL_LIB_CTX * ctx,THREAD_EVENT_HANDLER ** hands)124 static int set_thread_event_handler(OSSL_LIB_CTX *ctx, THREAD_EVENT_HANDLER **hands)
125 {
126 #ifdef FIPS_MODULE
127     return CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_TEVENT_KEY, ctx, hands);
128 #else
129     if (destructor_key.sane != -1)
130         return CRYPTO_THREAD_set_local(&destructor_key.value, hands);
131     return 0;
132 #endif
133 }
134 
135 static THREAD_EVENT_HANDLER **
manage_thread_local(OSSL_LIB_CTX * ctx,int alloc,int keep)136 manage_thread_local(OSSL_LIB_CTX *ctx, int alloc, int keep)
137 {
138     THREAD_EVENT_HANDLER **hands = get_thread_event_handler(ctx);
139 
140     if (alloc) {
141         if (hands == NULL) {
142 
143             if ((hands = OPENSSL_zalloc(sizeof(*hands))) == NULL)
144                 return NULL;
145 
146             if (!set_thread_event_handler(ctx, hands)) {
147                 OPENSSL_free(hands);
148                 return NULL;
149             }
150 #ifndef FIPS_MODULE
151             if (!init_thread_push_handlers(hands)) {
152                 set_thread_event_handler(ctx, NULL);
153                 OPENSSL_free(hands);
154                 return NULL;
155             }
156 #endif
157         }
158     } else if (!keep) {
159         set_thread_event_handler(ctx, NULL);
160     }
161 
162     return hands;
163 }
164 
clear_thread_local(OSSL_LIB_CTX * ctx)165 static ossl_inline THREAD_EVENT_HANDLER **clear_thread_local(OSSL_LIB_CTX *ctx)
166 {
167     return manage_thread_local(ctx, 0, 0);
168 }
169 
fetch_thread_local(OSSL_LIB_CTX * ctx)170 static ossl_inline ossl_unused THREAD_EVENT_HANDLER **fetch_thread_local(OSSL_LIB_CTX *ctx)
171 {
172     return manage_thread_local(ctx, 0, 1);
173 }
174 
alloc_thread_local(OSSL_LIB_CTX * ctx)175 static ossl_inline THREAD_EVENT_HANDLER **alloc_thread_local(OSSL_LIB_CTX *ctx)
176 {
177     return manage_thread_local(ctx, 1, 0);
178 }
179 
180 #ifndef FIPS_MODULE
181 /*
182  * The thread event handler list is a thread specific linked list
183  * of callback functions which are invoked in list order by the
184  * current thread in case of certain events. (Currently, there is
185  * only one type of event, the 'thread stop' event.)
186  *
187  * We also keep a global reference to that linked list, so that we
188  * can deregister handlers if necessary before all the threads are
189  * stopped.
190  */
init_thread_push_handlers(THREAD_EVENT_HANDLER ** hands)191 static int init_thread_push_handlers(THREAD_EVENT_HANDLER **hands)
192 {
193     int ret;
194     GLOBAL_TEVENT_REGISTER *gtr;
195 
196     gtr = get_global_tevent_register();
197     if (gtr == NULL)
198         return 0;
199 
200     if (!CRYPTO_THREAD_write_lock(gtr->lock))
201         return 0;
202     ret = (sk_THREAD_EVENT_HANDLER_PTR_push(gtr->skhands, hands) != 0);
203     CRYPTO_THREAD_unlock(gtr->lock);
204 
205     return ret;
206 }
207 
init_thread_remove_handlers(THREAD_EVENT_HANDLER ** handsin)208 static void init_thread_remove_handlers(THREAD_EVENT_HANDLER **handsin)
209 {
210     GLOBAL_TEVENT_REGISTER *gtr;
211     int i;
212 
213     gtr = get_global_tevent_register();
214     if (gtr == NULL)
215         return;
216     if (!CRYPTO_THREAD_write_lock(gtr->lock))
217         return;
218     for (i = 0; i < sk_THREAD_EVENT_HANDLER_PTR_num(gtr->skhands); i++) {
219         THREAD_EVENT_HANDLER **hands
220             = sk_THREAD_EVENT_HANDLER_PTR_value(gtr->skhands, i);
221 
222         if (hands == handsin) {
223             sk_THREAD_EVENT_HANDLER_PTR_delete(gtr->skhands, i);
224             CRYPTO_THREAD_unlock(gtr->lock);
225             return;
226         }
227     }
228     CRYPTO_THREAD_unlock(gtr->lock);
229     return;
230 }
231 
init_thread_destructor(void * hands)232 static void init_thread_destructor(void *hands)
233 {
234     init_thread_stop(NULL, (THREAD_EVENT_HANDLER **)hands);
235     init_thread_remove_handlers(hands);
236     OPENSSL_free(hands);
237 }
238 
ossl_init_thread(void)239 int ossl_init_thread(void)
240 {
241     if (!CRYPTO_THREAD_init_local(&destructor_key.value,
242                                   init_thread_destructor))
243         return 0;
244 
245     return 1;
246 }
247 
ossl_cleanup_thread(void)248 void ossl_cleanup_thread(void)
249 {
250     init_thread_deregister(NULL, 1);
251     CRYPTO_THREAD_cleanup_local(&destructor_key.value);
252     destructor_key.sane = -1;
253 }
254 
OPENSSL_thread_stop_ex(OSSL_LIB_CTX * ctx)255 void OPENSSL_thread_stop_ex(OSSL_LIB_CTX *ctx)
256 {
257     ctx = ossl_lib_ctx_get_concrete(ctx);
258     /*
259      * It would be nice if we could figure out a way to do this on all threads
260      * that have used the OSSL_LIB_CTX when the context is freed. This is
261      * currently not possible due to the use of thread local variables.
262      */
263     ossl_ctx_thread_stop(ctx);
264 }
265 
OPENSSL_thread_stop(void)266 void OPENSSL_thread_stop(void)
267 {
268     if (destructor_key.sane != -1) {
269         THREAD_EVENT_HANDLER **hands = clear_thread_local(NULL);
270 
271         init_thread_stop(NULL, hands);
272 
273         init_thread_remove_handlers(hands);
274         OPENSSL_free(hands);
275     }
276 }
277 
ossl_ctx_thread_stop(OSSL_LIB_CTX * ctx)278 void ossl_ctx_thread_stop(OSSL_LIB_CTX *ctx)
279 {
280     if (destructor_key.sane != -1) {
281         THREAD_EVENT_HANDLER **hands = fetch_thread_local(ctx);
282 
283         init_thread_stop(ctx, hands);
284     }
285 }
286 
287 #else
288 
289 static void ossl_arg_thread_stop(void *arg);
290 
291 /* Register the current thread so that we are informed if it gets stopped */
ossl_thread_register_fips(OSSL_LIB_CTX * libctx)292 int ossl_thread_register_fips(OSSL_LIB_CTX *libctx)
293 {
294     return c_thread_start(FIPS_get_core_handle(libctx), ossl_arg_thread_stop,
295                           libctx);
296 }
297 
ossl_thread_event_ctx_new(OSSL_LIB_CTX * libctx)298 int ossl_thread_event_ctx_new(OSSL_LIB_CTX *libctx)
299 {
300     THREAD_EVENT_HANDLER **hands = NULL;
301 
302     hands = OPENSSL_zalloc(sizeof(*hands));
303     if (hands == NULL)
304         goto err;
305 
306     if (!CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_TEVENT_KEY, libctx, hands))
307         goto err;
308 
309     /*
310      * We should ideally call ossl_thread_register_fips() here. This function
311      * is called during the startup of the FIPS provider and we need to ensure
312      * that the main thread is registered to receive thread callbacks in order
313      * to free |hands| that we allocated above. However we are too early in
314      * the FIPS provider initialisation that FIPS_get_core_handle() doesn't work
315      * yet. So we defer this to the main provider OSSL_provider_init_int()
316      * function.
317      */
318 
319     return 1;
320  err:
321     OPENSSL_free(hands);
322     return 0;
323 }
324 
ossl_thread_event_ctx_free(OSSL_LIB_CTX * ctx)325 void ossl_thread_event_ctx_free(OSSL_LIB_CTX *ctx)
326 {
327     CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_TEVENT_KEY, ctx, NULL);
328 }
329 
ossl_arg_thread_stop(void * arg)330 static void ossl_arg_thread_stop(void *arg)
331 {
332     ossl_ctx_thread_stop((OSSL_LIB_CTX *)arg);
333 }
334 
ossl_ctx_thread_stop(OSSL_LIB_CTX * ctx)335 void ossl_ctx_thread_stop(OSSL_LIB_CTX *ctx)
336 {
337     THREAD_EVENT_HANDLER **hands;
338 
339     hands = clear_thread_local(ctx);
340     init_thread_stop(ctx, hands);
341     OPENSSL_free(hands);
342 }
343 #endif /* FIPS_MODULE */
344 
345 
init_thread_stop(void * arg,THREAD_EVENT_HANDLER ** hands)346 static void init_thread_stop(void *arg, THREAD_EVENT_HANDLER **hands)
347 {
348     THREAD_EVENT_HANDLER *curr, *prev = NULL, *tmp;
349 #ifndef FIPS_MODULE
350     GLOBAL_TEVENT_REGISTER *gtr;
351 #endif
352 
353     /* Can't do much about this */
354     if (hands == NULL)
355         return;
356 
357 #ifndef FIPS_MODULE
358     gtr = get_global_tevent_register();
359     if (gtr == NULL)
360         return;
361 
362     if (!CRYPTO_THREAD_write_lock(gtr->lock))
363         return;
364 #endif
365 
366     curr = *hands;
367     while (curr != NULL) {
368         if (arg != NULL && curr->arg != arg) {
369             prev = curr;
370             curr = curr->next;
371             continue;
372         }
373         curr->handfn(curr->arg);
374         if (prev == NULL)
375             *hands = curr->next;
376         else
377             prev->next = curr->next;
378 
379         tmp = curr;
380         curr = curr->next;
381 
382         OPENSSL_free(tmp);
383     }
384 #ifndef FIPS_MODULE
385     CRYPTO_THREAD_unlock(gtr->lock);
386 #endif
387 }
388 
ossl_init_thread_start(const void * index,void * arg,OSSL_thread_stop_handler_fn handfn)389 int ossl_init_thread_start(const void *index, void *arg,
390                            OSSL_thread_stop_handler_fn handfn)
391 {
392     THREAD_EVENT_HANDLER **hands;
393     THREAD_EVENT_HANDLER *hand;
394     OSSL_LIB_CTX *ctx = NULL;
395 #ifdef FIPS_MODULE
396     /*
397      * In FIPS mode the list of THREAD_EVENT_HANDLERs is unique per combination
398      * of OSSL_LIB_CTX and thread. This is because in FIPS mode each
399      * OSSL_LIB_CTX gets informed about thread stop events individually.
400      */
401 
402     ctx = arg;
403 #endif
404 
405     hands = alloc_thread_local(ctx);
406 
407     if (hands == NULL)
408         return 0;
409 
410 #ifdef FIPS_MODULE
411     if (*hands == NULL) {
412         /*
413          * We've not yet registered any handlers for this thread. We need to get
414          * libcrypto to tell us about later thread stop events. c_thread_start
415          * is a callback to libcrypto defined in fipsprov.c
416          */
417         if (!ossl_thread_register_fips(ctx))
418             return 0;
419     }
420 #endif
421 
422     hand = OPENSSL_malloc(sizeof(*hand));
423     if (hand == NULL)
424         return 0;
425 
426     hand->handfn = handfn;
427     hand->arg = arg;
428 #ifndef FIPS_MODULE
429     hand->index = index;
430 #endif
431     hand->next = *hands;
432     *hands = hand;
433 
434     return 1;
435 }
436 
437 #ifndef FIPS_MODULE
init_thread_deregister(void * index,int all)438 static int init_thread_deregister(void *index, int all)
439 {
440     GLOBAL_TEVENT_REGISTER *gtr;
441     int i;
442 
443     gtr = get_global_tevent_register();
444     if (gtr == NULL)
445         return 0;
446     if (!all) {
447         if (!CRYPTO_THREAD_write_lock(gtr->lock))
448             return 0;
449     } else {
450         glob_tevent_reg = NULL;
451     }
452     for (i = 0; i < sk_THREAD_EVENT_HANDLER_PTR_num(gtr->skhands); i++) {
453         THREAD_EVENT_HANDLER **hands
454             = sk_THREAD_EVENT_HANDLER_PTR_value(gtr->skhands, i);
455         THREAD_EVENT_HANDLER *curr = NULL, *prev = NULL, *tmp;
456 
457         if (hands == NULL) {
458             if (!all)
459                 CRYPTO_THREAD_unlock(gtr->lock);
460             return 0;
461         }
462         curr = *hands;
463         while (curr != NULL) {
464             if (all || curr->index == index) {
465                 if (prev != NULL)
466                     prev->next = curr->next;
467                 else
468                     *hands = curr->next;
469                 tmp = curr;
470                 curr = curr->next;
471                 OPENSSL_free(tmp);
472                 continue;
473             }
474             prev = curr;
475             curr = curr->next;
476         }
477         if (all)
478             OPENSSL_free(hands);
479     }
480     if (all) {
481         CRYPTO_THREAD_lock_free(gtr->lock);
482         sk_THREAD_EVENT_HANDLER_PTR_free(gtr->skhands);
483         OPENSSL_free(gtr);
484     } else {
485         CRYPTO_THREAD_unlock(gtr->lock);
486     }
487     return 1;
488 }
489 
ossl_init_thread_deregister(void * index)490 int ossl_init_thread_deregister(void *index)
491 {
492     return init_thread_deregister(index, 0);
493 }
494 #endif
495