74a5f4815aa6e9c2d01bfc200155dbf53013d05d
[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 "internal/cryptlib_int.h"
12
13 typedef struct thread_event_handler_st THREAD_EVENT_HANDLER;
14 struct thread_event_handler_st {
15     OPENSSL_CTX *ctx;
16     ossl_thread_stop_handler_fn handfn;
17     THREAD_EVENT_HANDLER *next;
18 };
19
20 static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands);
21
22 #ifndef FIPS_MODE
23 /*
24  * Since per-thread-specific-data destructors are not universally
25  * available, i.e. not on Windows, only below CRYPTO_THREAD_LOCAL key
26  * is assumed to have destructor associated. And then an effort is made
27  * to call this single destructor on non-pthread platform[s].
28  *
29  * Initial value is "impossible". It is used as guard value to shortcut
30  * destructor for threads terminating before libcrypto is initialized or
31  * after it's de-initialized. Access to the key doesn't have to be
32  * serialized for the said threads, because they didn't use libcrypto
33  * and it doesn't matter if they pick "impossible" or derefernce real
34  * key value and pull NULL past initialization in the first thread that
35  * intends to use libcrypto.
36  */
37 static union {
38     long sane;
39     CRYPTO_THREAD_LOCAL value;
40 } destructor_key = { -1 };
41
42 static void ossl_init_thread_destructor(void *hands)
43 {
44     ossl_init_thread_stop((THREAD_EVENT_HANDLER **)hands);
45 }
46
47 int init_thread(void)
48 {
49
50     if (!CRYPTO_THREAD_init_local(&destructor_key.value,
51                                   ossl_init_thread_destructor))
52         return 0;
53
54     return 1;
55 }
56
57 void cleanup_thread(void)
58 {
59     CRYPTO_THREAD_cleanup_local(&destructor_key.value);
60     destructor_key.sane = -1;
61 }
62
63 static THREAD_EVENT_HANDLER **ossl_init_get_thread_local(int alloc)
64 {
65     THREAD_EVENT_HANDLER **hands =
66         CRYPTO_THREAD_get_local(&destructor_key.value);
67
68     if (alloc) {
69         if (hands == NULL
70             && (hands = OPENSSL_zalloc(sizeof(*hands))) != NULL
71             && !CRYPTO_THREAD_set_local(&destructor_key.value, hands)) {
72             OPENSSL_free(hands);
73             return NULL;
74         }
75     } else {
76         CRYPTO_THREAD_set_local(&destructor_key.value, NULL);
77     }
78
79     return hands;
80 }
81
82 void OPENSSL_thread_stop(void)
83 {
84     if (destructor_key.sane != -1)
85         ossl_init_thread_stop(ossl_init_get_thread_local(0));
86 }
87 #else
88 static void *thread_event_ossl_ctx_new(OPENSSL_CTX *libctx)
89 {
90     THREAD_EVENT_HANDLER **hands = NULL;
91     CRYPTO_THREAD_LOCAL *tlocal = OPENSSL_zalloc(sizeof(CRYPTO_THREAD_LOCAL));
92
93     if (tlocal == NULL)
94         return NULL;
95
96     hands = OPENSSL_zalloc(sizeof(*hands));
97     if (hands == NULL)
98         goto err;
99
100     if (!CRYPTO_THREAD_set_local(tlocal, hands))
101         goto err;
102
103     return tlocal;
104  err:
105     OPENSSL_free(hands);
106     OPENSSL_free(tlocal);
107     return NULL;
108 }
109
110 static void thread_event_ossl_ctx_free(void *vtlocal)
111 {
112     CRYPTO_THREAD_LOCAL *tlocal = vtlocal;
113     THREAD_EVENT_HANDLER **hands = CRYPTO_THREAD_get_local(tlocal);
114
115     if (hands != NULL)
116         ossl_init_thread_stop(hands);
117
118     OPENSSL_free(tlocal);
119 }
120
121 static const OPENSSL_CTX_METHOD thread_event_ossl_ctx_method = {
122     thread_event_ossl_ctx_new,
123     thread_event_ossl_ctx_free,
124 };
125
126 void fips_thread_stop(OPENSSL_CTX *ctx)
127 {
128     THREAD_EVENT_HANDLER **hands;
129
130     hands = openssl_ctx_get_data(ctx, OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX,
131                                  &thread_event_ossl_ctx_method);
132     if (hands != NULL)
133         ossl_init_thread_stop(hands);
134 }
135 #endif /* FIPS_MODE */
136
137 static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands)
138 {
139     THREAD_EVENT_HANDLER *curr, *prev = NULL;
140
141     /* Can't do much about this */
142     if (hands == NULL)
143         return;
144
145     curr = *hands;
146     while (curr != NULL) {
147         curr->handfn(curr->ctx);
148         prev = curr;
149         curr = curr->next;
150         OPENSSL_free(prev);
151     }
152
153     OPENSSL_free(hands);
154 }
155
156 int ossl_init_thread_start(OPENSSL_CTX *ctx, ossl_thread_stop_handler_fn handfn)
157 {
158     THREAD_EVENT_HANDLER **hands;
159     THREAD_EVENT_HANDLER *hand;
160
161 #ifdef FIPS_MODE
162     /*
163      * In FIPS mode the list of THREAD_EVENT_HANDLERs is unique per combination
164      * of OPENSSL_CTX and thread. This is because in FIPS mode each OPENSSL_CTX
165      * gets informed about thread stop events individually.
166      */
167     hands = openssl_ctx_get_data(ctx, OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX,
168                                  &thread_event_ossl_ctx_method);
169 #else
170     /*
171      * Outside of FIPS mode the list of THREAD_EVENT_HANDLERs is unique per
172      * thread, but may hold multiple OPENSSL_CTXs. We only get told about
173      * thread stop events globally, so we have to ensure all affected
174      * OPENSSL_CTXs are informed.
175      */
176     hands = ossl_init_get_thread_local(1);
177 #endif
178
179     if (hands == NULL)
180         return 0;
181
182     hand = OPENSSL_malloc(sizeof(*hand));
183     if (hand == NULL)
184         return 0;
185
186     hand->handfn = handfn;
187     hand->ctx = ctx;
188     hand->next = *hands;
189     *hands = hand;
190
191     return 1;
192 }