Provide a version of ossl_init_thread_start that works in FIPS mode
authorMatt Caswell <matt@openssl.org>
Fri, 24 May 2019 17:20:49 +0000 (18:20 +0100)
committerMatt Caswell <matt@openssl.org>
Mon, 17 Jun 2019 14:32:54 +0000 (15:32 +0100)
This will need to be hooked up in a later commit with an event sent to
the FIPS provider informing it of thread stop events.

Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/9040)

crypto/include/internal/cryptlib_int.h
crypto/initthread.c
include/internal/cryptlib.h

index e810ef0..0e7a0d4 100644 (file)
@@ -16,6 +16,7 @@ int ossl_init_thread_start(OPENSSL_CTX *ctx,
                            ossl_thread_stop_handler_fn handfn);
 int init_thread(void);
 void cleanup_thread(void);
+void fips_thread_stop(OPENSSL_CTX *ctx);
 
 /*
  * OPENSSL_INIT flags. The primary list of these is in crypto.h. Flags below
index 6e56d47..74a5f48 100644 (file)
@@ -17,6 +17,9 @@ struct thread_event_handler_st {
     THREAD_EVENT_HANDLER *next;
 };
 
+static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands);
+
+#ifndef FIPS_MODE
 /*
  * Since per-thread-specific-data destructors are not universally
  * available, i.e. not on Windows, only below CRYPTO_THREAD_LOCAL key
@@ -36,8 +39,6 @@ static union {
     CRYPTO_THREAD_LOCAL value;
 } destructor_key = { -1 };
 
-static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands);
-
 static void ossl_init_thread_destructor(void *hands)
 {
     ossl_init_thread_stop((THREAD_EVENT_HANDLER **)hands);
@@ -78,6 +79,61 @@ static THREAD_EVENT_HANDLER **ossl_init_get_thread_local(int alloc)
     return hands;
 }
 
+void OPENSSL_thread_stop(void)
+{
+    if (destructor_key.sane != -1)
+        ossl_init_thread_stop(ossl_init_get_thread_local(0));
+}
+#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));
+
+    if (tlocal == NULL)
+        return NULL;
+
+    hands = OPENSSL_zalloc(sizeof(*hands));
+    if (hands == NULL)
+        goto err;
+
+    if (!CRYPTO_THREAD_set_local(tlocal, hands))
+        goto err;
+
+    return tlocal;
+ err:
+    OPENSSL_free(hands);
+    OPENSSL_free(tlocal);
+    return NULL;
+}
+
+static void thread_event_ossl_ctx_free(void *vtlocal)
+{
+    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);
+}
+
+static const OPENSSL_CTX_METHOD thread_event_ossl_ctx_method = {
+    thread_event_ossl_ctx_new,
+    thread_event_ossl_ctx_free,
+};
+
+void fips_thread_stop(OPENSSL_CTX *ctx)
+{
+    THREAD_EVENT_HANDLER **hands;
+
+    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);
+}
+#endif /* FIPS_MODE */
+
 static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands)
 {
     THREAD_EVENT_HANDLER *curr, *prev = NULL;
@@ -97,21 +153,28 @@ static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands)
     OPENSSL_free(hands);
 }
 
-void OPENSSL_thread_stop(void)
-{
-    if (destructor_key.sane != -1)
-        ossl_init_thread_stop(ossl_init_get_thread_local(0));
-}
-
 int ossl_init_thread_start(OPENSSL_CTX *ctx, ossl_thread_stop_handler_fn handfn)
 {
     THREAD_EVENT_HANDLER **hands;
     THREAD_EVENT_HANDLER *hand;
 
-    if (!OPENSSL_init_crypto(0, NULL))
-        return 0;
-
+#ifdef FIPS_MODE
+    /*
+     * 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);
+#else
+    /*
+     * Outside of FIPS mode the list of THREAD_EVENT_HANDLERs is unique per
+     * thread, but may hold multiple OPENSSL_CTXs. We only get told about
+     * thread stop events globally, so we have to ensure all affected
+     * OPENSSL_CTXs are informed.
+     */
     hands = ossl_init_get_thread_local(1);
+#endif
 
     if (hands == NULL)
         return 0;
index 025e1ac..c40bb26 100644 (file)
@@ -149,7 +149,8 @@ typedef struct ossl_ex_data_global_st {
 # define OPENSSL_CTX_DRBG_INDEX                     5
 # define OPENSSL_CTX_DRBG_NONCE_INDEX               6
 # define OPENSSL_CTX_RAND_CRNGT_INDEX               7
-# define OPENSSL_CTX_MAX_INDEXES                    8
+# define OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX     8
+# define OPENSSL_CTX_MAX_INDEXES                    9
 
 typedef struct openssl_ctx_method {
     void *(*new_func)(OPENSSL_CTX *ctx);