summaryrefslogtreecommitdiffstats
path: root/crypto/initthread.c
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2019-05-27 16:31:27 +0100
committerMatt Caswell <matt@openssl.org>2019-06-17 16:19:44 +0100
commitda747958c5db57dbe22c015d058be9db8a90f8f9 (patch)
tree94eb46b99a0b7b586f7ed7aa1c0fc867d248b337 /crypto/initthread.c
parente41faf5784382a5d2bc23abebcf81b9f4708f6ec (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.c148
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;