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