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