124fdccd6ed558fcab59b9bb9d56dced177d8f97
[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 ossl_init_thread_stop(void *arg, THREAD_EVENT_HANDLER **hands);
39
40 static THREAD_EVENT_HANDLER **
41 ossl_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 ossl_init_thread_destructor(void *hands)
80 {
81     ossl_init_thread_stop(NULL, (THREAD_EVENT_HANDLER **)hands);
82     OPENSSL_free(hands);
83 }
84
85 int init_thread(void)
86 {
87     if (!CRYPTO_THREAD_init_local(&destructor_key.value,
88                                   ossl_init_thread_destructor))
89         return 0;
90
91     return 1;
92 }
93
94 void cleanup_thread(void)
95 {
96     CRYPTO_THREAD_cleanup_local(&destructor_key.value);
97     destructor_key.sane = -1;
98 }
99
100 void OPENSSL_thread_stop(void)
101 {
102     if (destructor_key.sane != -1) {
103         THREAD_EVENT_HANDLER **hands
104             = ossl_init_get_thread_local(&destructor_key.value, 0, 0);
105         ossl_init_thread_stop(NULL, hands);
106         OPENSSL_free(hands);
107     }
108 }
109
110 void ossl_ctx_thread_stop(void *arg)
111 {
112     if (destructor_key.sane != -1) {
113         THREAD_EVENT_HANDLER **hands
114             = ossl_init_get_thread_local(&destructor_key.value, 0, 1);
115         ossl_init_thread_stop(arg, hands);
116     }
117 }
118
119 #else
120
121 static void *thread_event_ossl_ctx_new(OPENSSL_CTX *libctx)
122 {
123     THREAD_EVENT_HANDLER **hands = NULL;
124     CRYPTO_THREAD_LOCAL *tlocal = OPENSSL_zalloc(sizeof(*tlocal));
125
126     if (tlocal == NULL)
127         return NULL;
128
129     if (!CRYPTO_THREAD_init_local(tlocal,  NULL)) {
130         goto err;
131     }
132
133     hands = OPENSSL_zalloc(sizeof(*hands));
134     if (hands == NULL)
135         goto err;
136
137     if (!CRYPTO_THREAD_set_local(tlocal, hands))
138         goto err;
139
140     return tlocal;
141  err:
142     OPENSSL_free(hands);
143     OPENSSL_free(tlocal);
144     return NULL;
145 }
146
147 static void thread_event_ossl_ctx_free(void *tlocal)
148 {
149     OPENSSL_free(tlocal);
150 }
151
152 static const OPENSSL_CTX_METHOD thread_event_ossl_ctx_method = {
153     thread_event_ossl_ctx_new,
154     thread_event_ossl_ctx_free,
155 };
156
157 void ossl_ctx_thread_stop(void *arg)
158 {
159     THREAD_EVENT_HANDLER **hands;
160     OPENSSL_CTX *ctx = arg;
161     CRYPTO_THREAD_LOCAL *local
162         = openssl_ctx_get_data(ctx, OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX,
163                                &thread_event_ossl_ctx_method);
164
165     if (local == NULL)
166         return;
167     hands = ossl_init_get_thread_local(local, 0, 0);
168     ossl_init_thread_stop(arg, hands);
169     OPENSSL_free(hands);
170 }
171 #endif /* FIPS_MODE */
172
173
174 static void ossl_init_thread_stop(void *arg, THREAD_EVENT_HANDLER **hands)
175 {
176     THREAD_EVENT_HANDLER *curr, *prev = NULL;
177
178     /* Can't do much about this */
179     if (hands == NULL)
180         return;
181
182     curr = *hands;
183     while (curr != NULL) {
184         if (arg != NULL && curr->arg != arg) {
185             curr = curr->next;
186             continue;
187         }
188         curr->handfn(curr->arg);
189         prev = curr;
190         curr = curr->next;
191         if (prev == *hands)
192             *hands = curr;
193         OPENSSL_free(prev);
194     }
195 }
196
197 int ossl_init_thread_start(void *arg, OSSL_thread_stop_handler_fn handfn)
198 {
199     THREAD_EVENT_HANDLER **hands;
200     THREAD_EVENT_HANDLER *hand;
201 #ifdef FIPS_MODE
202     OPENSSL_CTX *ctx = arg;
203
204     /*
205      * In FIPS mode the list of THREAD_EVENT_HANDLERs is unique per combination
206      * of OPENSSL_CTX and thread. This is because in FIPS mode each OPENSSL_CTX
207      * gets informed about thread stop events individually.
208      */
209     CRYPTO_THREAD_LOCAL *local
210         = openssl_ctx_get_data(ctx, OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX,
211                                &thread_event_ossl_ctx_method);
212 #else
213     /*
214      * Outside of FIPS mode the list of THREAD_EVENT_HANDLERs is unique per
215      * thread, but may hold multiple OPENSSL_CTXs. We only get told about
216      * thread stop events globally, so we have to ensure all affected
217      * OPENSSL_CTXs are informed.
218      */
219     CRYPTO_THREAD_LOCAL *local = &destructor_key.value;
220 #endif
221
222     hands = ossl_init_get_thread_local(local, 1, 0);
223     if (hands == NULL)
224         return 0;
225
226 #ifdef FIPS_MODE
227     if (*hands == NULL) {
228         /*
229          * We've not yet registered any handlers for this thread. We need to get
230          * libcrypto to tell us about later thread stop events. c_thread_start
231          * is a callback to libcrypto defined in fipsprov.c
232          */
233         if (!c_thread_start(FIPS_get_provider(ctx), ossl_ctx_thread_stop))
234             return 0;
235     }
236 #endif
237
238     hand = OPENSSL_malloc(sizeof(*hand));
239     if (hand == NULL)
240         return 0;
241
242     hand->handfn = handfn;
243     hand->arg = arg;
244     hand->next = *hands;
245     *hands = hand;
246
247     return 1;
248 }