Register callbacks with core for child provider creation/deletion
authorMatt Caswell <matt@openssl.org>
Fri, 23 Apr 2021 11:08:27 +0000 (12:08 +0100)
committerMatt Caswell <matt@openssl.org>
Tue, 11 May 2021 13:59:43 +0000 (14:59 +0100)
By adding callbacks to the core this will enable (in future commits) the
ability to add/remove child providers as the providers are added/removed
from the parent libctx.

Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14991)

crypto/provider_child.c
crypto/provider_core.c
include/openssl/core.h
include/openssl/core_dispatch.h

index fa1d0041227a897ebc1d753169e40ec1dd6ba806..ea86379efc960fffee75f04f26290a89e5240ba8 100644 (file)
@@ -18,15 +18,16 @@ DEFINE_STACK_OF(OSSL_PROVIDER)
 
 struct child_prov_globals {
     const OSSL_CORE_HANDLE *handle;
-    OSSL_CORE_PROVIDER *curr_prov;
+    const OSSL_CORE_HANDLE *curr_prov;
     STACK_OF(OSSL_PROVIDER) *childprovs;
     unsigned int isinited:1;
     CRYPTO_RWLOCK *lock;
     OSSL_FUNC_core_get_libctx_fn *c_get_libctx;
-    OSSL_FUNC_core_provider_do_all_fn *c_prov_do_all;
-    OSSL_FUNC_core_provider_name_fn *c_prov_name;
-    OSSL_FUNC_core_provider_get0_provider_ctx_fn *c_prov_get0_provider_ctx;
-    OSSL_FUNC_core_provider_get0_dispatch_fn *c_prov_get0_dispatch;
+    OSSL_FUNC_provider_register_child_cb_fn *c_provider_register_child_cb;
+    OSSL_FUNC_provider_deregister_child_cb_fn *c_provider_deregister_child_cb;
+    OSSL_FUNC_provider_name_fn *c_prov_name;
+    OSSL_FUNC_provider_get0_provider_ctx_fn *c_prov_get0_provider_ctx;
+    OSSL_FUNC_provider_get0_dispatch_fn *c_prov_get0_dispatch;
 };
 
 static void *child_prov_ossl_ctx_new(OSSL_LIB_CTX *libctx)
@@ -44,6 +45,7 @@ static void child_prov_ossl_ctx_free(void *vgbl)
 {
     struct child_prov_globals *gbl = vgbl;
 
+    gbl->c_provider_deregister_child_cb(gbl->handle);
     sk_OSSL_PROVIDER_pop_free(gbl->childprovs, ossl_prov_free);
     CRYPTO_THREAD_lock_free(gbl->lock);
     OPENSSL_free(gbl);
@@ -98,18 +100,26 @@ static int ossl_child_provider_init(const OSSL_CORE_HANDLE *handle,
     return 1;
 }
 
-static int provider_create_child_cb(OSSL_CORE_PROVIDER *prov, void *cbdata)
+static int provider_create_child_cb(const OSSL_CORE_HANDLE *prov, void *cbdata)
 {
     OSSL_LIB_CTX *ctx = cbdata;
     struct child_prov_globals *gbl;
     const char *provname;
     OSSL_PROVIDER *cprov;
+    int ret = 0;
 
     gbl = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_CHILD_PROVIDER_INDEX,
                                 &child_prov_ossl_ctx_method);
     if (gbl == NULL)
         return 0;
 
+    /*
+     * If !gbl->isinited, then we are still initing and we already hold the
+     * lock - so don't take it again.
+     */
+    if (gbl->isinited && !CRYPTO_THREAD_write_lock(gbl->lock))
+        return 0;
+
     provname = gbl->c_prov_name(prov);
 
     /*
@@ -124,19 +134,42 @@ static int provider_create_child_cb(OSSL_CORE_PROVIDER *prov, void *cbdata)
      */
     if ((cprov = ossl_provider_new(ctx, provname, ossl_child_provider_init,
                                    1)) == NULL)
-        return 0;
+        goto err;
 
     if (!ossl_provider_activate(cprov, 0)) {
         ossl_provider_free(cprov);
-        return 0;
+        goto err;
     }
     ossl_provider_set_child(cprov);
 
     if (!sk_OSSL_PROVIDER_push(gbl->childprovs, cprov)) {
-        OSSL_PROVIDER_unload(cprov);
-        return 0;
+        ossl_provider_free(cprov);
+        goto err;
     }
 
+    ret = 1;
+ err:
+    if (gbl->isinited)
+        CRYPTO_THREAD_unlock(gbl->lock);
+    return ret;
+}
+
+static int provider_remove_child_cb(const OSSL_CORE_HANDLE *prov, void *cbdata)
+{
+    OSSL_LIB_CTX *ctx = cbdata;
+    struct child_prov_globals *gbl;
+    const char *provname;
+    OSSL_PROVIDER *cprov;
+
+    gbl = ossl_lib_ctx_get_data(ctx, OSSL_LIB_CTX_CHILD_PROVIDER_INDEX,
+                                &child_prov_ossl_ctx_method);
+    if (gbl == NULL)
+        return 0;
+
+    provname = gbl->c_prov_name(prov);
+    cprov = ossl_provider_find(ctx, provname, 1);
+    OSSL_PROVIDER_unload(cprov);
+
     return 1;
 }
 
@@ -164,8 +197,10 @@ int ossl_provider_init_child_providers(OSSL_LIB_CTX *ctx)
     if (!CRYPTO_THREAD_write_lock(gbl->lock))
         return 0;
     if (!gbl->isinited) {
-        if (!gbl->c_prov_do_all(gbl->c_get_libctx(gbl->handle),
-                                provider_create_child_cb, ctx)) {
+        if (!gbl->c_provider_register_child_cb(gbl->handle,
+                                               provider_create_child_cb,
+                                               provider_remove_child_cb,
+                                               ctx)) {
             CRYPTO_THREAD_unlock(gbl->lock);
             return 0;
         }
@@ -196,18 +231,23 @@ int ossl_provider_init_as_child(OSSL_LIB_CTX *ctx,
         case OSSL_FUNC_CORE_GET_LIBCTX:
             gbl->c_get_libctx = OSSL_FUNC_core_get_libctx(in);
             break;
-        case OSSL_FUNC_CORE_PROVIDER_DO_ALL:
-            gbl->c_prov_do_all = OSSL_FUNC_core_provider_do_all(in);
+        case OSSL_FUNC_PROVIDER_REGISTER_CHILD_CB:
+            gbl->c_provider_register_child_cb
+                = OSSL_FUNC_provider_register_child_cb(in);
+            break;
+        case OSSL_FUNC_PROVIDER_DEREGISTER_CHILD_CB:
+            gbl->c_provider_deregister_child_cb
+                = OSSL_FUNC_provider_deregister_child_cb(in);
             break;
-        case OSSL_FUNC_CORE_PROVIDER_NAME:
-            gbl->c_prov_name = OSSL_FUNC_core_provider_name(in);
+        case OSSL_FUNC_PROVIDER_NAME:
+            gbl->c_prov_name = OSSL_FUNC_provider_name(in);
             break;
-        case OSSL_FUNC_CORE_PROVIDER_GET0_PROVIDER_CTX:
+        case OSSL_FUNC_PROVIDER_GET0_PROVIDER_CTX:
             gbl->c_prov_get0_provider_ctx
-                = OSSL_FUNC_core_provider_get0_provider_ctx(in);
+                = OSSL_FUNC_provider_get0_provider_ctx(in);
             break;
-        case OSSL_FUNC_CORE_PROVIDER_GET0_DISPATCH:
-            gbl->c_prov_get0_dispatch = OSSL_FUNC_core_provider_get0_dispatch(in);
+        case OSSL_FUNC_PROVIDER_GET0_DISPATCH:
+            gbl->c_prov_get0_dispatch = OSSL_FUNC_provider_get0_dispatch(in);
             break;
         default:
             /* Just ignore anything we don't understand */
@@ -215,7 +255,8 @@ int ossl_provider_init_as_child(OSSL_LIB_CTX *ctx,
         }
     }
 
-    if (gbl->c_prov_do_all == NULL
+    if (gbl->c_get_libctx == NULL
+            || gbl->c_provider_register_child_cb == NULL
             || gbl->c_prov_name == NULL
             || gbl->c_prov_get0_provider_ctx == NULL
             || gbl->c_prov_get0_dispatch == NULL)
index 3e2e2ac3357feafde3edf55b0a90c4c48d6e4116..9cf7ca2f7c5a804ea50fb6aa095272eec5fd9c77 100644 (file)
@@ -42,6 +42,14 @@ typedef struct {
 } INFOPAIR;
 DEFINE_STACK_OF(INFOPAIR)
 
+typedef struct {
+    OSSL_PROVIDER *prov;
+    int (*create_cb)(const OSSL_CORE_HANDLE *provider, void *cbdata);
+    void (*remove_cb)(const OSSL_CORE_HANDLE *provider, void *cbdata);
+    void *cbdata;
+} OSSL_PROVIDER_CHILD_CB;
+DEFINE_STACK_OF(OSSL_PROVIDER_CHILD_CB)
+
 struct provider_store_st;        /* Forward declaration */
 
 struct ossl_provider_st {
@@ -117,6 +125,7 @@ static int ossl_provider_cmp(const OSSL_PROVIDER * const *a,
 
 struct provider_store_st {
     STACK_OF(OSSL_PROVIDER) *providers;
+    STACK_OF(OSSL_PROVIDER_CHILD_CB) *child_cbs;
     CRYPTO_RWLOCK *default_path_lock;
     CRYPTO_RWLOCK *lock;
     char *default_path;
@@ -137,6 +146,11 @@ static void provider_deactivate_free(OSSL_PROVIDER *prov)
     ossl_provider_free(prov);
 }
 
+static void ossl_provider_child_cb_free(OSSL_PROVIDER_CHILD_CB *cb)
+{
+    OPENSSL_free(cb);
+}
+
 static void provider_store_free(void *vstore)
 {
     struct provider_store_st *store = vstore;
@@ -146,6 +160,8 @@ static void provider_store_free(void *vstore)
     store->freeing = 1;
     OPENSSL_free(store->default_path);
     sk_OSSL_PROVIDER_pop_free(store->providers, provider_deactivate_free);
+    sk_OSSL_PROVIDER_CHILD_CB_pop_free(store->child_cbs,
+                                       ossl_provider_child_cb_free);
     CRYPTO_THREAD_lock_free(store->default_path_lock);
     CRYPTO_THREAD_lock_free(store->lock);
     OPENSSL_free(store);
@@ -159,6 +175,7 @@ static void *provider_store_new(OSSL_LIB_CTX *ctx)
     if (store == NULL
         || (store->providers = sk_OSSL_PROVIDER_new(ossl_provider_cmp)) == NULL
         || (store->default_path_lock = CRYPTO_THREAD_lock_new()) == NULL
+        || (store->child_cbs = sk_OSSL_PROVIDER_CHILD_CB_new_null()) == NULL
         || (store->lock = CRYPTO_THREAD_lock_new()) == NULL) {
         provider_store_free(store);
         return NULL;
@@ -696,17 +713,33 @@ static int provider_init(OSSL_PROVIDER *prov, int flag_lock)
 static int provider_deactivate(OSSL_PROVIDER *prov)
 {
     int count;
+    struct provider_store_st *store;
 
     if (!ossl_assert(prov != NULL))
         return -1;
 
+    store = get_provider_store(prov->libctx);
+    if (store == NULL)
+        return 0;
+
+    if (!CRYPTO_THREAD_read_lock(store->lock))
+        return 0;
     if (!CRYPTO_THREAD_write_lock(prov->flag_lock))
         return -1;
 
-    if ((count = --prov->activatecnt) < 1)
+    if ((count = --prov->activatecnt) < 1) {
+        int i, max = sk_OSSL_PROVIDER_CHILD_CB_num(store->child_cbs);
+        OSSL_PROVIDER_CHILD_CB *child_cb;
+
         prov->flag_activated = 0;
+        for (i = 0; i < max; i++) {
+            child_cb = sk_OSSL_PROVIDER_CHILD_CB_value(store->child_cbs, i);
+            child_cb->remove_cb((OSSL_CORE_HANDLE *)prov, child_cb->cbdata);
+        }
+    }
 
     CRYPTO_THREAD_unlock(prov->flag_lock);
+    CRYPTO_THREAD_unlock(store->lock);
 
     /* We don't deinit here, that's done in ossl_provider_free() */
     return count;
@@ -716,22 +749,41 @@ static int provider_deactivate(OSSL_PROVIDER *prov)
  * Activate a provider.
  * Return -1 on failure and the activation count on success
  */
-static int provider_activate(OSSL_PROVIDER *prov, int flag_lock)
+static int provider_activate(OSSL_PROVIDER *prov, int lock)
 {
-    int count;
+    int count = -1;
 
-    if (provider_init(prov, flag_lock)) {
-        if (flag_lock && !CRYPTO_THREAD_write_lock(prov->flag_lock))
-            return -1;
-        count = ++prov->activatecnt;
-        prov->flag_activated = 1;
-        if (flag_lock)
-            CRYPTO_THREAD_unlock(prov->flag_lock);
+    if (provider_init(prov, lock)) {
+        int i, max, ret;
+        OSSL_PROVIDER_CHILD_CB *child_cb;
+        struct provider_store_st *store;
+
+        store = get_provider_store(prov->libctx);
+        if (store == NULL)
+            return 0;
 
-        return count;
+        if (lock && !CRYPTO_THREAD_read_lock(store->lock))
+            return 0;
+
+        max = sk_OSSL_PROVIDER_CHILD_CB_num(store->child_cbs);
+        if (lock && !CRYPTO_THREAD_write_lock(prov->flag_lock))
+            return 0;
+        if (ret) {
+            count = ++prov->activatecnt;
+            prov->flag_activated = 1;
+
+            for (i = 0; i < max; i++) {
+                child_cb = sk_OSSL_PROVIDER_CHILD_CB_value(store->child_cbs, i);
+                child_cb->create_cb((OSSL_CORE_HANDLE *)prov, child_cb->cbdata);
+            }
+        }
+        if (lock) {
+            CRYPTO_THREAD_unlock(prov->flag_lock);
+            CRYPTO_THREAD_unlock(store->lock);
+        }
     }
 
-    return -1;
+    return count;
 }
 
 static int provider_flush_store_cache(const OSSL_PROVIDER *prov)
@@ -819,9 +871,12 @@ static void provider_activate_fallbacks(struct provider_store_st *store)
         OSSL_PROVIDER *prov = sk_OSSL_PROVIDER_value(store->providers, i);
 
         if (ossl_provider_up_ref(prov)) {
-            if (prov->flag_fallback) {
-                if (provider_activate(prov, 1) > 0)
-                    activated_fallback_count++;
+            if (CRYPTO_THREAD_write_lock(prov->flag_lock)) {
+                if (prov->flag_fallback) {
+                    if (provider_activate(prov, 0) > 0)
+                        activated_fallback_count++;
+                }
+                CRYPTO_THREAD_unlock(prov->flag_lock);
             }
             ossl_provider_free(prov);
         }
@@ -1159,6 +1214,106 @@ void ossl_provider_set_child(OSSL_PROVIDER *prov)
     prov->ischild = 1;
 }
 
+#ifndef FIPS_MODULE
+static int ossl_provider_register_child_cb(const OSSL_CORE_HANDLE *handle,
+                                           int (*create_cb)(
+                                               const OSSL_CORE_HANDLE *provider,
+                                               void *cbdata),
+                                           void (*remove_cb)(
+                                               const OSSL_CORE_HANDLE *provider,
+                                               void *cbdata),
+                                           void *cbdata)
+{
+    /*
+     * This is really an OSSL_PROVIDER that we created and cast to
+     * OSSL_CORE_HANDLE originally. Therefore it is safe to cast it back.
+     */
+    OSSL_PROVIDER *thisprov = (OSSL_PROVIDER *)handle;
+    OSSL_PROVIDER *prov;
+    OSSL_LIB_CTX *libctx = thisprov->libctx;
+    struct provider_store_st *store = NULL;
+    int ret = 0, i, max;
+    OSSL_PROVIDER_CHILD_CB *child_cb;
+
+    if ((store = get_provider_store(libctx)) == NULL)
+        return 0;
+
+    child_cb = OPENSSL_malloc(sizeof(*child_cb));
+    if (child_cb == NULL)
+        return 0;
+    child_cb->prov = thisprov;
+    child_cb->create_cb = create_cb;
+    child_cb->remove_cb = remove_cb;
+    child_cb->cbdata = cbdata;
+
+    if (!CRYPTO_THREAD_write_lock(store->lock)) {
+        OPENSSL_free(child_cb);
+        return 0;
+    }
+    max = sk_OSSL_PROVIDER_num(store->providers);
+    for (i = 0; i < max; i++) {
+        prov = sk_OSSL_PROVIDER_value(store->providers, i);
+        if (!CRYPTO_THREAD_read_lock(prov->flag_lock))
+            break;
+        /*
+         * We hold the lock while calling the user callback. This means that the
+         * user callback must be short and simple and not do anything likely to
+         * cause a deadlock.
+         */
+        if (prov->flag_activated
+                && !create_cb((OSSL_CORE_HANDLE *)prov, cbdata))
+            break;
+        CRYPTO_THREAD_unlock(prov->flag_lock);
+    }
+    if (i == max) {
+        /* Success */
+        ret = sk_OSSL_PROVIDER_CHILD_CB_push(store->child_cbs, child_cb);
+    }
+    if (i != max || ret <= 0) {
+        /* Failed during creation. Remove everything we just added */
+        for (; i >= 0; i--) {
+            prov = sk_OSSL_PROVIDER_value(store->providers, i);
+            remove_cb((OSSL_CORE_HANDLE *)prov, cbdata);
+        }
+        OPENSSL_free(child_cb);
+        ret = 0;
+    }
+    CRYPTO_THREAD_unlock(store->lock);
+
+    return ret;
+}
+
+static void ossl_provider_deregister_child_cb(const OSSL_CORE_HANDLE *handle)
+{
+    /*
+     * This is really an OSSL_PROVIDER that we created and cast to
+     * OSSL_CORE_HANDLE originally. Therefore it is safe to cast it back.
+     */
+    OSSL_PROVIDER *thisprov = (OSSL_PROVIDER *)handle;
+    OSSL_LIB_CTX *libctx = thisprov->libctx;
+    struct provider_store_st *store = NULL;
+    int i, max;
+    OSSL_PROVIDER_CHILD_CB *child_cb;
+
+    if ((store = get_provider_store(libctx)) == NULL)
+        return;
+
+    if (!CRYPTO_THREAD_write_lock(store->lock))
+        return;
+    max = sk_OSSL_PROVIDER_CHILD_CB_num(store->child_cbs);
+    for (i = 0; i < max; i++) {
+        child_cb = sk_OSSL_PROVIDER_CHILD_CB_value(store->child_cbs, i);
+        if (child_cb->prov == thisprov) {
+            /* Found an entry */
+            sk_OSSL_PROVIDER_CHILD_CB_delete(store->child_cbs, i);
+            OPENSSL_free(child_cb);
+            break;
+        }
+    }
+    CRYPTO_THREAD_unlock(store->lock);
+}
+#endif
+
 /*-
  * Core functions for the provider
  * ===============================
@@ -1377,12 +1532,15 @@ static const OSSL_DISPATCH core_dispatch_[] = {
         (void (*)(void))CRYPTO_secure_allocated },
     { OSSL_FUNC_OPENSSL_CLEANSE, (void (*)(void))OPENSSL_cleanse },
 #ifndef FIPS_MODULE
-    { OSSL_FUNC_CORE_PROVIDER_DO_ALL, (void (*)(void))OSSL_PROVIDER_do_all },
-    { OSSL_FUNC_CORE_PROVIDER_NAME,
+    { OSSL_FUNC_PROVIDER_REGISTER_CHILD_CB,
+        (void (*)(void))ossl_provider_register_child_cb },
+    { OSSL_FUNC_PROVIDER_DEREGISTER_CHILD_CB,
+        (void (*)(void))ossl_provider_deregister_child_cb },
+    { OSSL_FUNC_PROVIDER_NAME,
         (void (*)(void))OSSL_PROVIDER_name },
-    { OSSL_FUNC_CORE_PROVIDER_GET0_PROVIDER_CTX,
+    { OSSL_FUNC_PROVIDER_GET0_PROVIDER_CTX,
         (void (*)(void))OSSL_PROVIDER_get0_provider_ctx },
-    { OSSL_FUNC_CORE_PROVIDER_GET0_DISPATCH,
+    { OSSL_FUNC_PROVIDER_GET0_DISPATCH,
         (void (*)(void))OSSL_PROVIDER_get0_dispatch },
 #endif
     { 0, NULL }
index 71e860f99debb7f71c933d8010e67f9088a44c28..3356ef20884335db093610d3fd9b27ae8428d68b 100644 (file)
@@ -30,7 +30,6 @@ extern "C" {
 typedef struct ossl_core_handle_st OSSL_CORE_HANDLE;
 typedef struct openssl_core_ctx_st OPENSSL_CORE_CTX;
 typedef struct ossl_core_bio_st OSSL_CORE_BIO;
-typedef struct ossl_core_provider_st OSSL_CORE_PROVIDER;
 
 /*
  * Dispatch table element.  function_id numbers and the functions are defined
index 6b5e2e29359286a9fce0c6f3ffdcd183238f1973..c2c6c45976d94a62a5da86efd0b0a0e7ec5bec46 100644 (file)
@@ -184,21 +184,25 @@ OSSL_CORE_MAKE_FUNC(void, cleanup_nonce, (const OSSL_CORE_HANDLE *handle,
                                           unsigned char *buf, size_t len))
 
 /* Functions to access the core's providers */
-#define OSSL_FUNC_CORE_PROVIDER_DO_ALL            105
-#define OSSL_FUNC_CORE_PROVIDER_NAME              106
-#define OSSL_FUNC_CORE_PROVIDER_GET0_PROVIDER_CTX 107
-#define OSSL_FUNC_CORE_PROVIDER_GET0_DISPATCH     108
-
-OSSL_CORE_MAKE_FUNC(int, core_provider_do_all, (const OPENSSL_CORE_CTX *ctx,
-                                                int (*cb)(OSSL_CORE_PROVIDER *provider,
-                                                          void *cbdata),
-                                                void *cbdata))
-OSSL_CORE_MAKE_FUNC(const char *, core_provider_name,
-                    (const OSSL_CORE_PROVIDER *prov))
-OSSL_CORE_MAKE_FUNC(void *, core_provider_get0_provider_ctx,
-                    (const OSSL_CORE_PROVIDER *prov))
-OSSL_CORE_MAKE_FUNC(const OSSL_DISPATCH *, core_provider_get0_dispatch,
-                    (const OSSL_CORE_PROVIDER *prov))
+#define OSSL_FUNC_PROVIDER_REGISTER_CHILD_CB   105
+#define OSSL_FUNC_PROVIDER_DEREGISTER_CHILD_CB 106
+#define OSSL_FUNC_PROVIDER_NAME                107
+#define OSSL_FUNC_PROVIDER_GET0_PROVIDER_CTX   108
+#define OSSL_FUNC_PROVIDER_GET0_DISPATCH       109
+
+OSSL_CORE_MAKE_FUNC(int, provider_register_child_cb,
+                    (const OSSL_CORE_HANDLE *handle,
+                     int (*create_cb)(const OSSL_CORE_HANDLE *provider, void *cbdata),
+                     int (*remove_cb)(const OSSL_CORE_HANDLE *provider, void *cbdata),
+                     void *cbdata))
+OSSL_CORE_MAKE_FUNC(void, provider_deregister_child_cb,
+                    (const OSSL_CORE_HANDLE *handle))
+OSSL_CORE_MAKE_FUNC(const char *, provider_name,
+                    (const OSSL_CORE_HANDLE *prov))
+OSSL_CORE_MAKE_FUNC(void *, provider_get0_provider_ctx,
+                    (const OSSL_CORE_HANDLE *prov))
+OSSL_CORE_MAKE_FUNC(const OSSL_DISPATCH *, provider_get0_dispatch,
+                    (const OSSL_CORE_HANDLE *prov))
 
 /* Functions provided by the provider to the Core, reserved numbers 1024-1535 */
 # define OSSL_FUNC_PROVIDER_TEARDOWN           1024