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 */ 31 extern OSSL_FUNC_core_thread_start_fn *c_thread_start; 32 #endif 33 34 typedef struct thread_event_handler_st THREAD_EVENT_HANDLER; 35 struct 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 45 DEFINE_SPECIAL_STACK_OF(THREAD_EVENT_HANDLER_PTR, THREAD_EVENT_HANDLER *) 46 47 typedef struct global_tevent_register_st GLOBAL_TEVENT_REGISTER; 48 struct global_tevent_register_st { 49 STACK_OF(THREAD_EVENT_HANDLER_PTR) *skhands; 50 CRYPTO_RWLOCK *lock; 51 }; 52 53 static GLOBAL_TEVENT_REGISTER *glob_tevent_reg = NULL; 54 55 static CRYPTO_ONCE tevent_register_runonce = CRYPTO_ONCE_STATIC_INIT; 56 57 DEFINE_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 76 static 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 85 static int init_thread_push_handlers(THREAD_EVENT_HANDLER **hands); 86 static void init_thread_remove_handlers(THREAD_EVENT_HANDLER **handsin); 87 static void init_thread_destructor(void *hands); 88 static int init_thread_deregister(void *arg, int all); 89 #endif 90 static void init_thread_stop(void *arg, THREAD_EVENT_HANDLER **hands); 91 92 static THREAD_EVENT_HANDLER ** 93 init_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 */ 138 static 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 */ 153 static 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 170 static 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 194 static 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 201 int 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 210 void 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 217 void 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 228 void 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 240 void 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 251 static 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 277 static void thread_event_ossl_ctx_free(void *tlocal) 278 { 279 OPENSSL_free(tlocal); 280 } 281 282 static 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 288 static void ossl_arg_thread_stop(void *arg) 289 { 290 ossl_ctx_thread_stop((OSSL_LIB_CTX *)arg); 291 } 292 293 void 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 309 static 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 352 int 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 411 static 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 463 int ossl_init_thread_deregister(void *index) 464 { 465 return init_thread_deregister(index, 0); 466 } 467 #endif 468