summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2021-04-23 12:08:27 +0100
committerMatt Caswell <matt@openssl.org>2021-05-11 14:59:43 +0100
commit7b88c184b66c0d7cfb1f76422448af6a636eea8c (patch)
tree3c35397ba7aef0a28e0d6e0eba5d5808733021ce /crypto
parent5442611dffed2c345ef83d494f2ef7ffb9cf3883 (diff)
Register callbacks with core for child provider creation/deletion
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)
Diffstat (limited to 'crypto')
-rw-r--r--crypto/provider_child.c83
-rw-r--r--crypto/provider_core.c196
2 files changed, 239 insertions, 40 deletions
diff --git a/crypto/provider_child.c b/crypto/provider_child.c
index fa1d004122..ea86379efc 100644
--- a/crypto/provider_child.c
+++ b/crypto/provider_child.c
@@ -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)
diff --git a/crypto/provider_core.c b/crypto/provider_core.c
index 3e2e2ac335..9cf7ca2f7c 100644
--- a/crypto/provider_core.c
+++ b/crypto/provider_core.c
@@ -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 }