From 7b88c184b66c0d7cfb1f76422448af6a636eea8c Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Fri, 23 Apr 2021 12:08:27 +0100 Subject: 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 (Merged from https://github.com/openssl/openssl/pull/14991) --- crypto/provider_child.c | 83 ++++++++++++++------ crypto/provider_core.c | 196 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 239 insertions(+), 40 deletions(-) (limited to 'crypto') 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 } -- cgit v1.2.3