diff options
author | Matt Caswell <matt@openssl.org> | 2021-05-04 16:23:31 +0100 |
---|---|---|
committer | Matt Caswell <matt@openssl.org> | 2021-05-11 15:03:13 +0100 |
commit | abaa2dd2981ba3c15456016c6248f539242cfb49 (patch) | |
tree | 4c1318ac7498852d82656fd742bd6de9caf0f281 | |
parent | 8c627075656cf2709680eeb5aa1826f00db2e483 (diff) | |
download | openssl-new-abaa2dd2981ba3c15456016c6248f539242cfb49.tar.gz |
Don't convert pre-existing providers into children
If a provider explicitly loads another provider into a child libctx where
it wasn't previously loaded then we don't start treating it like a child
if the parent libctx subsequently loads the same provider.
Fixes #14925
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14991)
-rw-r--r-- | crypto/provider_child.c | 71 | ||||
-rw-r--r-- | crypto/provider_core.c | 61 | ||||
-rw-r--r-- | include/internal/provider.h | 5 |
3 files changed, 105 insertions, 32 deletions
diff --git a/crypto/provider_child.c b/crypto/provider_child.c index 71ca2bc731..0ca61c0686 100644 --- a/crypto/provider_child.c +++ b/crypto/provider_child.c @@ -123,29 +123,44 @@ static int provider_create_child_cb(const OSSL_CORE_HANDLE *prov, void *cbdata) */ gbl->curr_prov = prov; - /* - * Create it - passing 1 as final param so we don't try and recursively init - * children - */ - /* 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; - - /* - * 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; - } - - if (!ossl_provider_set_child(cprov, prov)) { - ossl_provider_deactivate(cprov); - goto err; + if ((cprov = ossl_provider_find(ctx, provname, 1)) != NULL) { + /* + * We free the newly created ref. We rely on the provider sticking around + * in the provider store. + */ + ossl_provider_free(cprov); + + /* + * The provider already exists. It could be an unused built-in, or a + * previously created child, or it could have been explicitly loaded. If + * explicitly loaded it cannot be converted to a child and we ignore it + * - i.e. we don't start treating it like a child. + */ + if (!ossl_provider_convert_to_child(cprov, prov, + ossl_child_provider_init)) + goto err; + } else { + /* + * 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) + goto err; + + /* + * 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; + + if (!ossl_provider_set_child(cprov, prov)) { + ossl_provider_deactivate(cprov); + goto err; + } } ret = 1; @@ -169,10 +184,16 @@ 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); - if (!ossl_provider_deactivate(cprov)) + if (cprov == NULL) return 0; - /* ossl_provider_find also ups the ref count, so we free it again here */ + /* + * ossl_provider_find ups the ref count, so we free it again here. We can + * rely on the provider store reference count. + */ ossl_provider_free(cprov); + if (ossl_provider_is_child(cprov) + && !ossl_provider_deactivate(cprov)) + return 0; return 1; } diff --git a/crypto/provider_core.c b/crypto/provider_core.c index f87ccfa4a2..598f1ba214 100644 --- a/crypto/provider_core.c +++ b/crypto/provider_core.c @@ -57,6 +57,7 @@ struct ossl_provider_st { unsigned int flag_initialized:1; unsigned int flag_activated:1; unsigned int flag_fallback:1; /* Can be used as fallback */ + unsigned int flag_couldbechild:1; /* Getting and setting the flags require synchronization */ CRYPTO_RWLOCK *flag_lock; @@ -306,6 +307,7 @@ static OSSL_PROVIDER *provider_new(const char *name, } prov->init_function = init_function; + prov->flag_couldbechild = 1; return prov; } @@ -646,6 +648,7 @@ static int provider_init(OSSL_PROVIDER *prov, int flag_lock) } prov->provctx = tmp_provctx; prov->dispatch = provider_dispatch; + prov->flag_couldbechild = 0; for (; provider_dispatch->function_id != 0; provider_dispatch++) { switch (provider_dispatch->function_id) { @@ -1281,23 +1284,60 @@ 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) +int ossl_provider_is_child(const OSSL_PROVIDER *prov) { - struct provider_store_st *store = NULL; - - if ((store = get_provider_store(prov->libctx)) == NULL) - return 0; + return prov->ischild; +} +int ossl_provider_set_child(OSSL_PROVIDER *prov, const OSSL_CORE_HANDLE *handle) +{ prov->handle = handle; prov->ischild = 1; - if (!CRYPTO_THREAD_write_lock(store->lock)) + return 1; +} + +int ossl_provider_convert_to_child(OSSL_PROVIDER *prov, + const OSSL_CORE_HANDLE *handle, + OSSL_provider_init_fn *init_function) +{ + int flush = 0; + + if (!CRYPTO_THREAD_write_lock(prov->store->lock)) return 0; + if (!CRYPTO_THREAD_write_lock(prov->flag_lock)) { + CRYPTO_THREAD_unlock(prov->store->lock); + return 0; + } + /* + * The provider could be in one of three states: (1) Already a child, + * (2) Not a child (but eligible to be one), or (3) Not a child (not + * eligible to be one). + */ + if (prov->flag_couldbechild) { + ossl_provider_set_child(prov, handle); + prov->init_function = init_function; + } + if (prov->ischild && provider_activate(prov, 0, 0)) { + flush = 1; + prov->store->use_fallbacks = 0; + } - CRYPTO_THREAD_unlock(store->lock); + CRYPTO_THREAD_unlock(prov->flag_lock); + CRYPTO_THREAD_unlock(prov->store->lock); + + if (flush) + provider_flush_store_cache(prov); + + /* + * We report success whether or not the provider was eligible for conversion + * to a child. If its not elgibile then it has already been loaded as a non + * child provider and we should keep it like that. + */ return 1; } + #ifndef FIPS_MODULE static int ossl_provider_register_child_cb(const OSSL_CORE_HANDLE *handle, int (*create_cb)( @@ -1337,6 +1377,13 @@ static int ossl_provider_register_child_cb(const OSSL_CORE_HANDLE *handle, max = sk_OSSL_PROVIDER_num(store->providers); for (i = 0; i < max; i++) { prov = sk_OSSL_PROVIDER_value(store->providers, i); + /* + * We require register_child_cb to be called during a provider init + * function. The currently initing provider will never be activated yet + * and we we should not attempt to aquire the flag_lock for it. + */ + if (prov == thisprov) + continue; if (!CRYPTO_THREAD_read_lock(prov->flag_lock)) break; /* diff --git a/include/internal/provider.h b/include/internal/provider.h index 7d5ccccbd1..5b0af7a335 100644 --- a/include/internal/provider.h +++ b/include/internal/provider.h @@ -41,7 +41,12 @@ int ossl_provider_set_fallback(OSSL_PROVIDER *prov); int ossl_provider_set_module_path(OSSL_PROVIDER *prov, const char *module_path); int ossl_provider_add_parameter(OSSL_PROVIDER *prov, const char *name, const char *value); + +int ossl_provider_is_child(const OSSL_PROVIDER *prov); int ossl_provider_set_child(OSSL_PROVIDER *prov, const OSSL_CORE_HANDLE *handle); +int ossl_provider_convert_to_child(OSSL_PROVIDER *prov, + const OSSL_CORE_HANDLE *handle, + OSSL_provider_init_fn *init_function); const OSSL_CORE_HANDLE *ossl_provider_get_parent(OSSL_PROVIDER *prov); int ossl_provider_up_ref_parent(OSSL_PROVIDER *prov, int activate); int ossl_provider_free_parent(OSSL_PROVIDER *prov, int deactivate); |