diff options
author | Matt Caswell <matt@openssl.org> | 2019-05-27 16:31:27 +0100 |
---|---|---|
committer | Matt Caswell <matt@openssl.org> | 2019-06-17 16:19:44 +0100 |
commit | da747958c5db57dbe22c015d058be9db8a90f8f9 (patch) | |
tree | 94eb46b99a0b7b586f7ed7aa1c0fc867d248b337 /crypto/initthread.c | |
parent | e41faf5784382a5d2bc23abebcf81b9f4708f6ec (diff) |
Tell the FIPS provider about thread stop events
The RAND code needs to know about threads stopping in order to cleanup
local thread data. Therefore we add a callback for libcrypto to tell
providers about such events.
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9040)
Diffstat (limited to 'crypto/initthread.c')
-rw-r--r-- | crypto/initthread.c | 148 |
1 files changed, 102 insertions, 46 deletions
diff --git a/crypto/initthread.c b/crypto/initthread.c index 74a5f4815a..124fdccd6e 100644 --- a/crypto/initthread.c +++ b/crypto/initthread.c @@ -8,16 +8,53 @@ */ #include <openssl/crypto.h> +#include <openssl/core_numbers.h> #include "internal/cryptlib_int.h" +#include "internal/providercommon.h" + +#ifdef FIPS_MODE +/* + * Thread aware code may want to be told about thread stop events. We register + * to hear about those thread stop events when we see a new thread has started. + * We call the ossl_init_thread_start function to do that. In the FIPS provider + * we have our own copy of ossl_init_thread_start, which cascades notifications + * about threads stopping from libcrypto to all the code in the FIPS provider + * that needs to know about it. + * + * The FIPS provider tells libcrypto about which threads it is interested in + * by calling "c_thread_start" which is a function pointer created during + * provider initialisation (i.e. OSSL_init_provider). + */ +extern OSSL_core_thread_start_fn *c_thread_start; +#endif typedef struct thread_event_handler_st THREAD_EVENT_HANDLER; struct thread_event_handler_st { - OPENSSL_CTX *ctx; - ossl_thread_stop_handler_fn handfn; + void *arg; + OSSL_thread_stop_handler_fn handfn; THREAD_EVENT_HANDLER *next; }; -static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands); +static void ossl_init_thread_stop(void *arg, THREAD_EVENT_HANDLER **hands); + +static THREAD_EVENT_HANDLER ** +ossl_init_get_thread_local(CRYPTO_THREAD_LOCAL *local, int alloc, int keep) +{ + THREAD_EVENT_HANDLER **hands = CRYPTO_THREAD_get_local(local); + + if (alloc) { + if (hands == NULL + && (hands = OPENSSL_zalloc(sizeof(*hands))) != NULL + && !CRYPTO_THREAD_set_local(local, hands)) { + OPENSSL_free(hands); + return NULL; + } + } else if (!keep) { + CRYPTO_THREAD_set_local(local, NULL); + } + + return hands; +} #ifndef FIPS_MODE /* @@ -41,12 +78,12 @@ static union { static void ossl_init_thread_destructor(void *hands) { - ossl_init_thread_stop((THREAD_EVENT_HANDLER **)hands); + ossl_init_thread_stop(NULL, (THREAD_EVENT_HANDLER **)hands); + OPENSSL_free(hands); } int init_thread(void) { - if (!CRYPTO_THREAD_init_local(&destructor_key.value, ossl_init_thread_destructor)) return 0; @@ -60,39 +97,39 @@ void cleanup_thread(void) destructor_key.sane = -1; } -static THREAD_EVENT_HANDLER **ossl_init_get_thread_local(int alloc) +void OPENSSL_thread_stop(void) { - THREAD_EVENT_HANDLER **hands = - CRYPTO_THREAD_get_local(&destructor_key.value); - - if (alloc) { - if (hands == NULL - && (hands = OPENSSL_zalloc(sizeof(*hands))) != NULL - && !CRYPTO_THREAD_set_local(&destructor_key.value, hands)) { - OPENSSL_free(hands); - return NULL; - } - } else { - CRYPTO_THREAD_set_local(&destructor_key.value, NULL); + if (destructor_key.sane != -1) { + THREAD_EVENT_HANDLER **hands + = ossl_init_get_thread_local(&destructor_key.value, 0, 0); + ossl_init_thread_stop(NULL, hands); + OPENSSL_free(hands); } - - return hands; } -void OPENSSL_thread_stop(void) +void ossl_ctx_thread_stop(void *arg) { - if (destructor_key.sane != -1) - ossl_init_thread_stop(ossl_init_get_thread_local(0)); + if (destructor_key.sane != -1) { + THREAD_EVENT_HANDLER **hands + = ossl_init_get_thread_local(&destructor_key.value, 0, 1); + ossl_init_thread_stop(arg, hands); + } } + #else + static void *thread_event_ossl_ctx_new(OPENSSL_CTX *libctx) { THREAD_EVENT_HANDLER **hands = NULL; - CRYPTO_THREAD_LOCAL *tlocal = OPENSSL_zalloc(sizeof(CRYPTO_THREAD_LOCAL)); + CRYPTO_THREAD_LOCAL *tlocal = OPENSSL_zalloc(sizeof(*tlocal)); if (tlocal == NULL) return NULL; + if (!CRYPTO_THREAD_init_local(tlocal, NULL)) { + goto err; + } + hands = OPENSSL_zalloc(sizeof(*hands)); if (hands == NULL) goto err; @@ -107,14 +144,8 @@ static void *thread_event_ossl_ctx_new(OPENSSL_CTX *libctx) return NULL; } -static void thread_event_ossl_ctx_free(void *vtlocal) +static void thread_event_ossl_ctx_free(void *tlocal) { - CRYPTO_THREAD_LOCAL *tlocal = vtlocal; - THREAD_EVENT_HANDLER **hands = CRYPTO_THREAD_get_local(tlocal); - - if (hands != NULL) - ossl_init_thread_stop(hands); - OPENSSL_free(tlocal); } @@ -123,18 +154,24 @@ static const OPENSSL_CTX_METHOD thread_event_ossl_ctx_method = { thread_event_ossl_ctx_free, }; -void fips_thread_stop(OPENSSL_CTX *ctx) +void ossl_ctx_thread_stop(void *arg) { THREAD_EVENT_HANDLER **hands; + OPENSSL_CTX *ctx = arg; + CRYPTO_THREAD_LOCAL *local + = openssl_ctx_get_data(ctx, OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX, + &thread_event_ossl_ctx_method); - hands = openssl_ctx_get_data(ctx, OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX, - &thread_event_ossl_ctx_method); - if (hands != NULL) - ossl_init_thread_stop(hands); + if (local == NULL) + return; + hands = ossl_init_get_thread_local(local, 0, 0); + ossl_init_thread_stop(arg, hands); + OPENSSL_free(hands); } #endif /* FIPS_MODE */ -static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands) + +static void ossl_init_thread_stop(void *arg, THREAD_EVENT_HANDLER **hands) { THREAD_EVENT_HANDLER *curr, *prev = NULL; @@ -144,28 +181,34 @@ static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands) curr = *hands; while (curr != NULL) { - curr->handfn(curr->ctx); + if (arg != NULL && curr->arg != arg) { + curr = curr->next; + continue; + } + curr->handfn(curr->arg); prev = curr; curr = curr->next; + if (prev == *hands) + *hands = curr; OPENSSL_free(prev); } - - OPENSSL_free(hands); } -int ossl_init_thread_start(OPENSSL_CTX *ctx, ossl_thread_stop_handler_fn handfn) +int ossl_init_thread_start(void *arg, OSSL_thread_stop_handler_fn handfn) { THREAD_EVENT_HANDLER **hands; THREAD_EVENT_HANDLER *hand; - #ifdef FIPS_MODE + OPENSSL_CTX *ctx = arg; + /* * In FIPS mode the list of THREAD_EVENT_HANDLERs is unique per combination * of OPENSSL_CTX and thread. This is because in FIPS mode each OPENSSL_CTX * gets informed about thread stop events individually. */ - hands = openssl_ctx_get_data(ctx, OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX, - &thread_event_ossl_ctx_method); + CRYPTO_THREAD_LOCAL *local + = openssl_ctx_get_data(ctx, OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX, + &thread_event_ossl_ctx_method); #else /* * Outside of FIPS mode the list of THREAD_EVENT_HANDLERs is unique per @@ -173,18 +216,31 @@ int ossl_init_thread_start(OPENSSL_CTX *ctx, ossl_thread_stop_handler_fn handfn) * thread stop events globally, so we have to ensure all affected * OPENSSL_CTXs are informed. */ - hands = ossl_init_get_thread_local(1); + CRYPTO_THREAD_LOCAL *local = &destructor_key.value; #endif + hands = ossl_init_get_thread_local(local, 1, 0); if (hands == NULL) return 0; +#ifdef FIPS_MODE + if (*hands == NULL) { + /* + * We've not yet registered any handlers for this thread. We need to get + * libcrypto to tell us about later thread stop events. c_thread_start + * is a callback to libcrypto defined in fipsprov.c + */ + if (!c_thread_start(FIPS_get_provider(ctx), ossl_ctx_thread_stop)) + return 0; + } +#endif + hand = OPENSSL_malloc(sizeof(*hand)); if (hand == NULL) return 0; hand->handfn = handfn; - hand->ctx = ctx; + hand->arg = arg; hand->next = *hands; *hands = hand; |