From 8c627075656cf2709680eeb5aa1826f00db2e483 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Thu, 29 Apr 2021 16:37:42 +0100 Subject: Add support for child provider to up_ref/free their parent If the ref counts on a child provider change, then this needs to be reflected in the parent so we add callbacks to do this. Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/14991) --- crypto/evp/evp_fetch.c | 2 +- crypto/provider.c | 2 +- crypto/provider_child.c | 76 ++++++++++++++++++++++++--------- crypto/provider_conf.c | 4 +- crypto/provider_core.c | 110 +++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 159 insertions(+), 35 deletions(-) (limited to 'crypto') diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c index b74379031b..6c701bf1e2 100644 --- a/crypto/evp/evp_fetch.c +++ b/crypto/evp/evp_fetch.c @@ -36,7 +36,7 @@ static void *evp_method_store_new(OSSL_LIB_CTX *ctx) static const OSSL_LIB_CTX_METHOD evp_method_store_method = { /* We want evp_method_store to be cleaned up before the provider store */ - OSSL_LIB_CTX_METHOD_HIGH_PRIORITY, + OSSL_LIB_CTX_METHOD_PRIORITY_2, evp_method_store_new, evp_method_store_free, }; diff --git a/crypto/provider.c b/crypto/provider.c index 743c2f4846..766086a47b 100644 --- a/crypto/provider.c +++ b/crypto/provider.c @@ -23,7 +23,7 @@ OSSL_PROVIDER *OSSL_PROVIDER_try_load(OSSL_LIB_CTX *libctx, const char *name, && (prov = ossl_provider_new(libctx, name, NULL, 0)) == NULL) return NULL; - if (!ossl_provider_activate(prov, retain_fallbacks)) { + if (!ossl_provider_activate(prov, retain_fallbacks, 1)) { ossl_provider_free(prov); return NULL; } diff --git a/crypto/provider_child.c b/crypto/provider_child.c index ea86379efc..71ca2bc731 100644 --- a/crypto/provider_child.c +++ b/crypto/provider_child.c @@ -7,6 +7,7 @@ * https://www.openssl.org/source/license.html */ +#include #include #include #include @@ -19,7 +20,6 @@ DEFINE_STACK_OF(OSSL_PROVIDER) struct child_prov_globals { const OSSL_CORE_HANDLE *handle; 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; @@ -28,6 +28,8 @@ struct child_prov_globals { 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; + OSSL_FUNC_provider_up_ref_fn *c_prov_up_ref; + OSSL_FUNC_provider_free_fn *c_prov_free; }; static void *child_prov_ossl_ctx_new(OSSL_LIB_CTX *libctx) @@ -35,18 +37,11 @@ static void *child_prov_ossl_ctx_new(OSSL_LIB_CTX *libctx) return OPENSSL_zalloc(sizeof(struct child_prov_globals)); } -/* Wrapper with a void return type for use with sk_OSSL_PROVIDER_pop_free */ -static void ossl_prov_free(OSSL_PROVIDER *prov) -{ - OSSL_PROVIDER_unload(prov); -} - 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); } @@ -132,18 +127,24 @@ static int provider_create_child_cb(const OSSL_CORE_HANDLE *prov, void *cbdata) * Create it - passing 1 as final param so we don't try and recursively init * children */ - if ((cprov = ossl_provider_new(ctx, provname, ossl_child_provider_init, - 1)) == NULL) + /* Find it or create it */ + if ((cprov = ossl_provider_find(ctx, provname, 1)) == NULL + && (cprov = ossl_provider_new(ctx, provname, ossl_child_provider_init, + 1)) == NULL) goto err; - if (!ossl_provider_activate(cprov, 0)) { - ossl_provider_free(cprov); + /* + * We free the newly created ref. We rely on the provider sticking around + * in the provider store. + */ + ossl_provider_free(cprov); + + if (!ossl_provider_activate(cprov, 0, 0)){ goto err; } - ossl_provider_set_child(cprov); - if (!sk_OSSL_PROVIDER_push(gbl->childprovs, cprov)) { - ossl_provider_free(cprov); + if (!ossl_provider_set_child(cprov, prov)) { + ossl_provider_deactivate(cprov); goto err; } @@ -168,7 +169,10 @@ static int provider_remove_child_cb(const OSSL_CORE_HANDLE *prov, void *cbdata) provname = gbl->c_prov_name(prov); cprov = ossl_provider_find(ctx, provname, 1); - OSSL_PROVIDER_unload(cprov); + if (!ossl_provider_deactivate(cprov)) + return 0; + /* ossl_provider_find also ups the ref count, so we free it again here */ + ossl_provider_free(cprov); return 1; } @@ -249,6 +253,13 @@ int ossl_provider_init_as_child(OSSL_LIB_CTX *ctx, case OSSL_FUNC_PROVIDER_GET0_DISPATCH: gbl->c_prov_get0_dispatch = OSSL_FUNC_provider_get0_dispatch(in); break; + case OSSL_FUNC_PROVIDER_UP_REF: + gbl->c_prov_up_ref + = OSSL_FUNC_provider_up_ref(in); + break; + case OSSL_FUNC_PROVIDER_FREE: + gbl->c_prov_free = OSSL_FUNC_provider_free(in); + break; default: /* Just ignore anything we don't understand */ break; @@ -259,15 +270,40 @@ int ossl_provider_init_as_child(OSSL_LIB_CTX *ctx, || 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) + || gbl->c_prov_get0_dispatch == NULL + || gbl->c_prov_up_ref == NULL + || gbl->c_prov_free == NULL) return 0; - gbl->childprovs = sk_OSSL_PROVIDER_new_null(); - if (gbl->childprovs == NULL) - return 0; gbl->lock = CRYPTO_THREAD_lock_new(); if (gbl->lock == NULL) return 0; return 1; } + +int ossl_provider_up_ref_parent(OSSL_PROVIDER *prov, int activate) +{ + struct child_prov_globals *gbl; + + gbl = ossl_lib_ctx_get_data(ossl_provider_libctx(prov), + OSSL_LIB_CTX_CHILD_PROVIDER_INDEX, + &child_prov_ossl_ctx_method); + if (gbl == NULL) + return 0; + + return gbl->c_prov_up_ref(ossl_provider_get_parent(prov), activate); +} + +int ossl_provider_free_parent(OSSL_PROVIDER *prov, int deactivate) +{ + struct child_prov_globals *gbl; + + gbl = ossl_lib_ctx_get_data(ossl_provider_libctx(prov), + OSSL_LIB_CTX_CHILD_PROVIDER_INDEX, + &child_prov_ossl_ctx_method); + if (gbl == NULL) + return 0; + + return gbl->c_prov_free(ossl_provider_get_parent(prov), deactivate); +} diff --git a/crypto/provider_conf.c b/crypto/provider_conf.c index 6223d27cbb..5725ef3c63 100644 --- a/crypto/provider_conf.c +++ b/crypto/provider_conf.c @@ -46,7 +46,7 @@ static void prov_conf_ossl_ctx_free(void *vpcgbl) static const OSSL_LIB_CTX_METHOD provider_conf_ossl_ctx_method = { /* Must be freed before the provider store is freed */ - OSSL_LIB_CTX_METHOD_HIGH_PRIORITY, + OSSL_LIB_CTX_METHOD_PRIORITY_2, prov_conf_ossl_ctx_new, prov_conf_ossl_ctx_free, }; @@ -164,7 +164,7 @@ static int provider_conf_load(OSSL_LIB_CTX *libctx, const char *name, ok = provider_conf_params(prov, NULL, value, cnf); if (ok && activate) { - if (!ossl_provider_activate(prov, 0)) { + if (!ossl_provider_activate(prov, 0, 1)) { ok = 0; } else { if (pcgbl->activated_providers == NULL) diff --git a/crypto/provider_core.c b/crypto/provider_core.c index 9cf7ca2f7c..f87ccfa4a2 100644 --- a/crypto/provider_core.c +++ b/crypto/provider_core.c @@ -101,6 +101,7 @@ struct ossl_provider_st { CRYPTO_RWLOCK *opbits_lock; /* Whether this provider is the child of some other provider */ + const OSSL_CORE_HANDLE *handle; unsigned int ischild:1; /* Provider side data */ @@ -124,6 +125,7 @@ static int ossl_provider_cmp(const OSSL_PROVIDER * const *a, */ struct provider_store_st { + OSSL_LIB_CTX *libctx; STACK_OF(OSSL_PROVIDER) *providers; STACK_OF(OSSL_PROVIDER_CHILD_CB) *child_cbs; CRYPTO_RWLOCK *default_path_lock; @@ -180,6 +182,7 @@ static void *provider_store_new(OSSL_LIB_CTX *ctx) provider_store_free(store); return NULL; } + store->libctx = ctx; store->use_fallbacks = 1; for (p = ossl_predefined_providers; p->name != NULL; p++) { @@ -211,7 +214,8 @@ static void *provider_store_new(OSSL_LIB_CTX *ctx) } static const OSSL_LIB_CTX_METHOD provider_store_method = { - OSSL_LIB_CTX_METHOD_DEFAULT_PRIORITY, + /* Needs to be freed before the child provider data is freed */ + OSSL_LIB_CTX_METHOD_PRIORITY_1, provider_store_new, provider_store_free, }; @@ -311,9 +315,38 @@ int ossl_provider_up_ref(OSSL_PROVIDER *prov) if (CRYPTO_UP_REF(&prov->refcnt, &ref, prov->refcnt_lock) <= 0) return 0; + +#ifndef FIPS_MODULE + if (prov->ischild) { + if (!ossl_provider_up_ref_parent(prov, 0)) { + ossl_provider_free(prov); + return 0; + } + } +#endif + return ref; } +#ifndef FIPS_MODULE +static int provider_up_ref_intern(OSSL_PROVIDER *prov, int activate) +{ + if (activate) + return ossl_provider_activate(prov, 0, 1); + + return ossl_provider_up_ref(prov); +} + +static int provider_free_intern(OSSL_PROVIDER *prov, int deactivate) +{ + if (deactivate) + return ossl_provider_deactivate(prov); + + ossl_provider_free(prov); + return 1; +} +#endif + OSSL_PROVIDER *ossl_provider_new(OSSL_LIB_CTX *libctx, const char *name, OSSL_provider_init_fn *init_function, int noconfig) @@ -424,6 +457,11 @@ void ossl_provider_free(OSSL_PROVIDER *prov) #endif OPENSSL_free(prov); } +#ifndef FIPS_MODULE + else if (prov->ischild) { + ossl_provider_free_parent(prov, 0); + } +#endif } } @@ -727,6 +765,16 @@ static int provider_deactivate(OSSL_PROVIDER *prov) if (!CRYPTO_THREAD_write_lock(prov->flag_lock)) return -1; +#ifndef FIPS_MODULE + if (prov->activatecnt == 2 && prov->ischild) { + /* + * We have had a direct activation in this child libctx so we need to + * now down the ref count in the parent provider. + */ + ossl_provider_free_parent(prov, 1); + } +#endif + if ((count = --prov->activatecnt) < 1) { int i, max = sk_OSSL_PROVIDER_CHILD_CB_num(store->child_cbs); OSSL_PROVIDER_CHILD_CB *child_cb; @@ -749,12 +797,13 @@ 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 lock) +static int provider_activate(OSSL_PROVIDER *prov, int lock, int upcalls) { int count = -1; if (provider_init(prov, lock)) { - int i, max, ret; + int ret = 1; + int i, max; OSSL_PROVIDER_CHILD_CB *child_cb; struct provider_store_st *store; @@ -768,19 +817,36 @@ static int provider_activate(OSSL_PROVIDER *prov, int lock) max = sk_OSSL_PROVIDER_CHILD_CB_num(store->child_cbs); if (lock && !CRYPTO_THREAD_write_lock(prov->flag_lock)) return 0; + +#ifndef FIPS_MODULE + if (prov->ischild && upcalls) + ret = ossl_provider_up_ref_parent(prov, 1); +#endif + 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 (prov->activatecnt == 1) { + for (i = 0; i < max; i++) { + /* + * This is newly activated (activatecnt == 1), so we need to + * create child providers as necessary. + */ + child_cb = sk_OSSL_PROVIDER_CHILD_CB_value(store->child_cbs, + i); + ret &= child_cb->create_cb((OSSL_CORE_HANDLE *)prov, + child_cb->cbdata); + } } } + if (lock) { CRYPTO_THREAD_unlock(prov->flag_lock); CRYPTO_THREAD_unlock(store->lock); } + if (!ret) + return -1; } return count; @@ -804,13 +870,14 @@ static int provider_flush_store_cache(const OSSL_PROVIDER *prov) return 1; } -int ossl_provider_activate(OSSL_PROVIDER *prov, int retain_fallbacks) +int ossl_provider_activate(OSSL_PROVIDER *prov, int retain_fallbacks, + int upcalls) { int count; if (prov == NULL) return 0; - if ((count = provider_activate(prov, 1)) > 0) { + if ((count = provider_activate(prov, 1, upcalls)) > 0) { if (!retain_fallbacks) { if (!CRYPTO_THREAD_write_lock(prov->store->lock)) { provider_deactivate(prov); @@ -873,7 +940,7 @@ static void provider_activate_fallbacks(struct provider_store_st *store) if (ossl_provider_up_ref(prov)) { if (CRYPTO_THREAD_write_lock(prov->flag_lock)) { if (prov->flag_fallback) { - if (provider_activate(prov, 0) > 0) + if (provider_activate(prov, 0, 0) > 0) activated_fallback_count++; } CRYPTO_THREAD_unlock(prov->flag_lock); @@ -949,7 +1016,7 @@ int ossl_provider_doall_activated(OSSL_LIB_CTX *ctx, * It's already activated, but we up the activated count to ensure * it remains activated until after we've called the user callback. */ - if (provider_activate(prov, 0) < 0) { + if (provider_activate(prov, 0, 1) < 0) { ossl_provider_free(prov); CRYPTO_THREAD_unlock(prov->flag_lock); goto err_unlock; @@ -1209,9 +1276,26 @@ int ossl_provider_test_operation_bit(OSSL_PROVIDER *provider, size_t bitnum, return 1; } -void ossl_provider_set_child(OSSL_PROVIDER *prov) +const OSSL_CORE_HANDLE *ossl_provider_get_parent(OSSL_PROVIDER *prov) +{ + return prov->handle; +} + +int ossl_provider_set_child(OSSL_PROVIDER *prov, const OSSL_CORE_HANDLE *handle) { + struct provider_store_st *store = NULL; + + if ((store = get_provider_store(prov->libctx)) == NULL) + return 0; + + prov->handle = handle; prov->ischild = 1; + + if (!CRYPTO_THREAD_write_lock(store->lock)) + return 0; + + CRYPTO_THREAD_unlock(store->lock); + return 1; } #ifndef FIPS_MODULE @@ -1542,6 +1626,10 @@ static const OSSL_DISPATCH core_dispatch_[] = { (void (*)(void))OSSL_PROVIDER_get0_provider_ctx }, { OSSL_FUNC_PROVIDER_GET0_DISPATCH, (void (*)(void))OSSL_PROVIDER_get0_dispatch }, + { OSSL_FUNC_PROVIDER_UP_REF, + (void (*)(void))provider_up_ref_intern }, + { OSSL_FUNC_PROVIDER_FREE, + (void (*)(void))provider_free_intern }, #endif { 0, NULL } }; -- cgit v1.2.3