b4ee177c8f26595d73bd4a46ed8f30bd4b63301a
[openssl.git] / crypto / initthread.c
1 /*
2  * Copyright 2019 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_numbers.h>
12 #include "internal/cryptlib_int.h"
13 #include "internal/providercommon.h"
14
15 #ifdef FIPS_MODE
16 /*
17  * Thread aware code may want to be told about thread stop events. We register
18  * to hear about those thread stop events when we see a new thread has started.
19  * We call the ossl_init_thread_start function to do that. In the FIPS provider
20  * we have our own copy of ossl_init_thread_start, which cascades notifications
21  * about threads stopping from libcrypto to all the code in the FIPS provider
22  * that needs to know about it.
23  * 
24  * The FIPS provider tells libcrypto about which threads it is interested in
25  * by calling "c_thread_start" which is a function pointer created during
26  * provider initialisation (i.e. OSSL_init_provider).
27  */
28 extern OSSL_core_thread_start_fn *c_thread_start;
29 #endif
30
31 typedef struct thread_event_handler_st THREAD_EVENT_HANDLER;
32 struct thread_event_handler_st {
33     void *arg;
34     OSSL_thread_stop_handler_fn handfn;
35     THREAD_EVENT_HANDLER *next;
36 };
37
38 static void init_thread_stop(void *arg, THREAD_EVENT_HANDLER **hands);
39
40 static THREAD_EVENT_HANDLER **
41 init_get_thread_local(CRYPTO_THREAD_LOCAL *local, int alloc, int keep)
42 {
43     THREAD_EVENT_HANDLER **hands = CRYPTO_THREAD_get_local(local);
44
45     if (alloc) {
46         if (hands == NULL
47             && (hands = OPENSSL_zalloc(sizeof(*hands))) != NULL
48             && !CRYPTO_THREAD_set_local(local, hands)) {
49             OPENSSL_free(hands);
50             return NULL;
51         }
52     } else if (!keep) {
53         CRYPTO_THREAD_set_local(local, NULL);
54     }
55
56     return hands;
57 }
58
59 #ifndef FIPS_MODE
60 /*
61  * Since per-thread-specific-data destructors are not universally
62  * available, i.e. not on Windows, only below CRYPTO_THREAD_LOCAL key
63  * is assumed to have destructor associated. And then an effort is made
64  * to call this single destructor on non-pthread platform[s].
65  *
66  * Initial value is "impossible". It is used as guard value to shortcut
67  * destructor for threads terminating before libcrypto is initialized or
68  * after it's de-initialized. Access to the key doesn't have to be
69  * serialized for the said threads, because they didn't use libcrypto
70  * and it doesn't matter if they pick "impossible" or derefernce real
71  * key value and pull NULL past initialization in the first thread that
72  * intends to use libcrypto.
73  */
74 static union {
75     long sane;
76     CRYPTO_THREAD_LOCAL value;
77 } destructor_key = { -1 };
78
79 static void init_thread_destructor(void *hands)
80 {
81     init_thread_stop(NULL, (THREAD_EVENT_HANDLER **)hands);
82     OPENSSL_free(hands);
83 }
84
85 int ossl_init_thread(void)
86 {
87     if (!CRYPTO_THREAD_init_local(&destructor_key.value,
88                                   init_thread_destructor))
89         return 0;
90
91     return 1;
92 }
93
94 void ossl_cleanup_thread(void)
95 {
96     CRYPTO_THREAD_cleanup_local(&destructor_key.value);
97     destructor_key.sane = -1;
98 }
99
100 void OPENSSL_thread_stop_ex(OPENSSL_CTX *ctx)
101 {
102     ctx = openssl_ctx_get_concrete(ctx);
103     /*
104      * TODO(3.0). It would be nice if we could figure out a way to do this on
105      * all threads that have used the OPENSSL_CTX when the OPENSSL_CTX is freed.
106      * This is currently not possible due to the use of thread local variables.
107      */
108     ossl_ctx_thread_stop(ctx);
109 }
110
111 void OPENSSL_thread_stop(void)
112 {
113     if (destructor_key.sane != -1) {
114         THREAD_EVENT_HANDLER **hands
115             = init_get_thread_local(&destructor_key.value, 0, 0);
116         init_thread_stop(NULL, hands);
117         OPENSSL_free(hands);
118     }
119 }
120
121 void ossl_ctx_thread_stop(void *arg)
122 {
123     if (destructor_key.sane != -1) {
124         THREAD_EVENT_HANDLER **hands
125             = init_get_thread_local(&destructor_key.value, 0, 1);
126         init_thread_stop(arg, hands);
127     }
128 }
129
130 #else
131
132 static void *thread_event_ossl_ctx_new(OPENSSL_CTX *libctx)
133 {
134     THREAD_EVENT_HANDLER **hands = NULL;
135     CRYPTO_THREAD_LOCAL *tlocal = OPENSSL_zalloc(sizeof(*tlocal));
136
137     if (tlocal == NULL)
138         return NULL;
139
140     if (!CRYPTO_THREAD_init_local(tlocal,  NULL)) {
141         goto err;
142     }
143
144     hands = OPENSSL_zalloc(sizeof(*hands));
145     if (hands == NULL)
146         goto err;
147
148     if (!CRYPTO_THREAD_set_local(tlocal, hands))
149         goto err;
150
151     return tlocal;
152  err:
153     OPENSSL_free(hands);
154     OPENSSL_free(tlocal);
155     return NULL;
156 }
157
158 static void thread_event_ossl_ctx_free(void *tlocal)
159 {
160     OPENSSL_free(tlocal);
161 }
162
163 static const OPENSSL_CTX_METHOD thread_event_ossl_ctx_method = {
164     thread_event_ossl_ctx_new,
165     thread_event_ossl_ctx_free,
166 };
167
168 void ossl_ctx_thread_stop(void *arg)
169 {
170     THREAD_EVENT_HANDLER **hands;
171     OPENSSL_CTX *ctx = arg;
172     CRYPTO_THREAD_LOCAL *local
173         = openssl_ctx_get_data(ctx, OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX,
174                                &thread_event_ossl_ctx_method);
175
176     if (local == NULL)
177         return;
178     hands = init_get_thread_local(local, 0, 0);
179     init_thread_stop(arg, hands);
180     OPENSSL_free(hands);
181 }
182 #endif /* FIPS_MODE */
183
184
185 static void init_thread_stop(void *arg, THREAD_EVENT_HANDLER **hands)
186 {
187     THREAD_EVENT_HANDLER *curr, *prev = NULL;
188
189     /* Can't do much about this */
190     if (hands == NULL)
191         return;
192
193     curr = *hands;
194     while (curr != NULL) {
195         if (arg != NULL && curr->arg != arg) {
196             curr = curr->next;
197             continue;
198         }
199         curr->handfn(curr->arg);
200         prev = curr;
201         curr = curr->next;
202         if (prev == *hands)
203             *hands = curr;
204         OPENSSL_free(prev);
205     }
206 }
207
208 int ossl_init_thread_start(void *arg, OSSL_thread_stop_handler_fn handfn)
209 {
210     THREAD_EVENT_HANDLER **hands;
211     THREAD_EVENT_HANDLER *hand;
212 #ifdef FIPS_MODE
213     OPENSSL_CTX *ctx = arg;
214
215     /*
216      * In FIPS mode the list of THREAD_EVENT_HANDLERs is unique per combination
217      * of OPENSSL_CTX and thread. This is because in FIPS mode each OPENSSL_CTX
218      * gets informed about thread stop events individually.
219      */
220     CRYPTO_THREAD_LOCAL *local
221         = openssl_ctx_get_data(ctx, OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX,
222                                &thread_event_ossl_ctx_method);
223 #else
224     /*
225      * Outside of FIPS mode the list of THREAD_EVENT_HANDLERs is unique per
226      * thread, but may hold multiple OPENSSL_CTXs. We only get told about
227      * thread stop events globally, so we have to ensure all affected
228      * OPENSSL_CTXs are informed.
229      */
230     CRYPTO_THREAD_LOCAL *local = &destructor_key.value;
231 #endif
232
233     hands = init_get_thread_local(local, 1, 0);
234     if (hands == NULL)
235         return 0;
236
237 #ifdef FIPS_MODE
238     if (*hands == NULL) {
239         /*
240          * We've not yet registered any handlers for this thread. We need to get
241          * libcrypto to tell us about later thread stop events. c_thread_start
242          * is a callback to libcrypto defined in fipsprov.c
243          */
244         if (!c_thread_start(FIPS_get_provider(ctx), ossl_ctx_thread_stop))
245             return 0;
246     }
247 #endif
248
249     hand = OPENSSL_malloc(sizeof(*hand));
250     if (hand == NULL)
251         return 0;
252
253     hand->handfn = handfn;
254     hand->arg = arg;
255     hand->next = *hands;
256     *hands = hand;
257
258     return 1;
259 }