From e55008a9f2ae299374dcf868b660389e84dd2e0b Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Thu, 14 Mar 2019 10:53:27 +0100 Subject: [PATCH] Replumbing: add fallback provider capability To ensure that old applications aren't left without any provider, and at the same time not forcing any default provider on applications that know how to deal with them, we device the concept of fallback providers, which are automatically activated if no other provider is already activated. Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/8480) --- crypto/cpt_err.c | 2 + crypto/err/openssl.txt | 1 + crypto/provider_core.c | 122 +++++++++++++++++++++--- doc/internal/man3/ossl_provider_new.pod | 26 +++-- include/internal/provider.h | 1 + include/openssl/cryptoerr.h | 1 + 6 files changed, 134 insertions(+), 19 deletions(-) diff --git a/crypto/cpt_err.c b/crypto/cpt_err.c index bf7985cee9..88bee489f2 100644 --- a/crypto/cpt_err.c +++ b/crypto/cpt_err.c @@ -57,6 +57,8 @@ static const ERR_STRING_DATA CRYPTO_str_functs[] = { "pkey_poly1305_init"}, {ERR_PACK(ERR_LIB_CRYPTO, CRYPTO_F_PKEY_SIPHASH_INIT, 0), "pkey_siphash_init"}, + {ERR_PACK(ERR_LIB_CRYPTO, CRYPTO_F_PROVIDER_ACTIVATE, 0), + "provider_activate"}, {ERR_PACK(ERR_LIB_CRYPTO, CRYPTO_F_SK_RESERVE, 0), "sk_reserve"}, {0, NULL} }; diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 4853a05331..9f84dbc99f 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -396,6 +396,7 @@ CRYPTO_F_OSSL_PROVIDER_NEW:131:ossl_provider_new CRYPTO_F_PKEY_HMAC_INIT:123:pkey_hmac_init CRYPTO_F_PKEY_POLY1305_INIT:124:pkey_poly1305_init CRYPTO_F_PKEY_SIPHASH_INIT:125:pkey_siphash_init +CRYPTO_F_PROVIDER_ACTIVATE:134:provider_activate CRYPTO_F_SK_RESERVE:129:sk_reserve CT_F_CTLOG_NEW:117:CTLOG_new CT_F_CTLOG_NEW_FROM_BASE64:118:CTLOG_new_from_base64 diff --git a/crypto/provider_core.c b/crypto/provider_core.c index 8af5b1f14d..c136e42e16 100644 --- a/crypto/provider_core.c +++ b/crypto/provider_core.c @@ -25,6 +25,7 @@ struct provider_store_st; /* Forward declaration */ struct ossl_provider_st { /* Flag bits */ unsigned int flag_initialized:1; + unsigned int flag_fallback:1; /* OpenSSL library side data */ CRYPTO_REF_COUNT refcnt; @@ -32,6 +33,7 @@ struct ossl_provider_st { char *name; DSO *module; OSSL_provider_init_fn *init_function; + struct provider_store_st *store; /* The store this instance belongs to */ /* Provider side functions */ OSSL_provider_teardown_fn *teardown; @@ -58,6 +60,7 @@ static int ossl_provider_cmp(const OSSL_PROVIDER * const *a, struct provider_store_st { STACK_OF(OSSL_PROVIDER) *providers; CRYPTO_RWLOCK *lock; + unsigned int use_fallbacks:1; }; static int provider_store_index = -1; @@ -80,8 +83,9 @@ static void *provider_store_new(void) || (store->providers = sk_OSSL_PROVIDER_new(ossl_provider_cmp)) == NULL || (store->lock = CRYPTO_THREAD_lock_new()) == NULL) { provider_store_free(store); - store = NULL; + return NULL; } + store->use_fallbacks = 1; return store; } @@ -185,6 +189,8 @@ OSSL_PROVIDER *ossl_provider_new(OPENSSL_CTX *libctx, const char *name, ossl_provider_free(prov); /* -1 Store reference */ ossl_provider_free(prov); /* -1 Reference that was to be returned */ prov = NULL; + } else { + prov->store = store; } CRYPTO_THREAD_unlock(store->lock); @@ -207,11 +213,13 @@ void ossl_provider_free(OSSL_PROVIDER *prov) CRYPTO_DOWN_REF(&prov->refcnt, &ref, prov->refcnt_lock); /* - * When the refcount drops down to one, there is only one reference, - * the store. + * When the refcount drops below two, the store is the only + * possible reference, or it has already been taken away from + * the store (this may happen if a provider was activated + * because it's a fallback, but isn't currently used) * When that happens, the provider is inactivated. */ - if (ref == 1 && prov->flag_initialized) { + if (ref < 2 && prov->flag_initialized) { if (prov->teardown != NULL) prov->teardown(); prov->flag_initialized = 0; @@ -246,7 +254,12 @@ void ossl_provider_free(OSSL_PROVIDER *prov) */ static const OSSL_DISPATCH *core_dispatch; /* Define further down */ -int ossl_provider_activate(OSSL_PROVIDER *prov) +/* + * Internal version that doesn't affect the store flags, and thereby avoid + * locking. Direct callers must remember to set the store flags when + * appropriate + */ +static int provider_activate(OSSL_PROVIDER *prov) { const OSSL_DISPATCH *provider_dispatch = NULL; @@ -295,7 +308,7 @@ int ossl_provider_activate(OSSL_PROVIDER *prov) if (prov->init_function == NULL || !prov->init_function(prov, core_dispatch, &provider_dispatch)) { - CRYPTOerr(CRYPTO_F_OSSL_PROVIDER_ACTIVATE, ERR_R_INIT_FAIL); + CRYPTOerr(CRYPTO_F_PROVIDER_ACTIVATE, ERR_R_INIT_FAIL); ERR_add_error_data(2, "name=", prov->name); DSO_free(prov->module); prov->module = NULL; @@ -329,6 +342,46 @@ int ossl_provider_activate(OSSL_PROVIDER *prov) return 1; } +int ossl_provider_activate(OSSL_PROVIDER *prov) +{ + if (provider_activate(prov)) { + CRYPTO_THREAD_write_lock(prov->store->lock); + prov->store->use_fallbacks = 0; + CRYPTO_THREAD_unlock(prov->store->lock); + return 1; + } + + return 0; +} + + +static int provider_forall_loaded(struct provider_store_st *store, + int *found_activated, + int (*cb)(OSSL_PROVIDER *provider, + void *cbdata), + void *cbdata) +{ + int i; + int ret = 1; + int num_provs = sk_OSSL_PROVIDER_num(store->providers); + + if (found_activated != NULL) + *found_activated = 0; + for (i = 0; i < num_provs; i++) { + OSSL_PROVIDER *prov = + sk_OSSL_PROVIDER_value(store->providers, i); + + if (prov->flag_initialized) { + if (found_activated != NULL) + *found_activated = 1; + if (!(ret = cb(prov, cbdata))) + break; + } + } + + return ret; +} + int ossl_provider_forall_loaded(OPENSSL_CTX *ctx, int (*cb)(OSSL_PROVIDER *provider, void *cbdata), @@ -339,13 +392,50 @@ int ossl_provider_forall_loaded(OPENSSL_CTX *ctx, struct provider_store_st *store = get_provider_store(ctx); if (store != NULL) { + int found_activated = 0; + CRYPTO_THREAD_read_lock(store->lock); - for (i = 0; i < sk_OSSL_PROVIDER_num(store->providers); i++) { - OSSL_PROVIDER *prov = sk_OSSL_PROVIDER_value(store->providers, i); + ret = provider_forall_loaded(store, &found_activated, cb, cbdata); - if (prov->flag_initialized - && !(ret = cb(prov, cbdata))) - break; + /* + * If there's nothing activated ever in this store, try to activate + * all fallbacks. + */ + if (!found_activated && store->use_fallbacks) { + int num_provs = sk_OSSL_PROVIDER_num(store->providers); + int activated_fallback_count = 0; + + for (i = 0; i < num_provs; i++) { + OSSL_PROVIDER *prov = + sk_OSSL_PROVIDER_value(store->providers, i); + + /* + * Note that we don't care if the activation succeeds or + * not. If it doesn't succeed, then the next loop will + * fail anyway. + */ + if (prov->flag_fallback) { + activated_fallback_count++; + provider_activate(prov); + } + } + + if (activated_fallback_count > 0) { + /* + * We assume that all fallbacks have been added to the store + * before any fallback is activated. + * TODO: We may have to reconsider this, IF we find ourselves + * adding fallbacks after any previous fallback has been + * activated. + */ + store->use_fallbacks = 0; + + /* + * Now that we've activated available fallbacks, try a + * second sweep + */ + ret = provider_forall_loaded(store, NULL, cb, cbdata); + } } CRYPTO_THREAD_unlock(store->lock); } @@ -353,6 +443,16 @@ int ossl_provider_forall_loaded(OPENSSL_CTX *ctx, return ret; } +/* Setters of Provider Object data */ +int ossl_provider_set_fallback(OSSL_PROVIDER *prov) +{ + if (prov == NULL) + return 0; + + prov->flag_fallback = 1; + return 1; +} + /* Getters of Provider Object data */ const char *ossl_provider_name(OSSL_PROVIDER *prov) { diff --git a/doc/internal/man3/ossl_provider_new.pod b/doc/internal/man3/ossl_provider_new.pod index 7633e0e242..aa984c93b8 100644 --- a/doc/internal/man3/ossl_provider_new.pod +++ b/doc/internal/man3/ossl_provider_new.pod @@ -4,7 +4,8 @@ ossl_provider_find, ossl_provider_new, ossl_provider_upref, ossl_provider_free, ossl_provider_add_module_location, -ossl_provider_activate, ossl_provider_forall_loaded, +ossl_provider_set_fallback, ossl_provider_activate, +ossl_provider_forall_loaded, ossl_provider_name, ossl_provider_dso, ossl_provider_module_name, ossl_provider_module_path, ossl_provider_teardown, ossl_provider_get_param_types, @@ -23,6 +24,7 @@ ossl_provider_get_params, ossl_provider_query_operation /* Setters */ int ossl_provider_add_module_location(OSSL_PROVIDER *prov, const char *loc); + int ossl_provider_set_fallback(OSSL_PROVIDER *prov); /* Load and initialize the Provider */ int ossl_provider_activate(OSSL_PROVIDER *prov); @@ -71,7 +73,7 @@ times as ossl_provider_activate() has. ossl_provider_find() finds an existing I in the I store by C. -The I it finds gets it's reference count +The I it finds gets its reference count incremented. ossl_provider_new() creates a new I and stores it in @@ -84,14 +86,20 @@ To indicate a built-in provider, the C argument must point at the provider initialization function for that provider. ossl_provider_free() decrements a I's reference -counter; if it drops to one, the I will be -inactivated (it's teardown function is called) but kept in the store; -if it drops down to zero, the associated module will be unloaded if -one was loaded, and the I will be freed. +counter; if it drops below 2, the I is assumed to +have fallen out of use and will be inactivated (its teardown function +is called); if it drops down to zero, the I is +assumed to have been taken out of the store, and the associated module +will be unloaded if one was loaded, and the I will be +freed. ossl_provider_add_module_location() adds a location to look for a provider module. +ossl_provider_set_fallback() marks an available provider as fallback. +Note that after this call, the I pointer that was +used can simply be dropped, but not freed. + ossl_provider_activate() "activates" the provider for the given I. What "activates" means depends on what type of I it @@ -115,6 +123,8 @@ be located in that module, and called. ossl_provider_forall_loaded() iterates over all the currently "activated" providers, and calls C for each of them. +If no providers have been "activated" yet, it tries to activate all +available fallback providers and tries another iteration. ossl_provider_name() returns the name that was given with ossl_provider_new(). @@ -178,8 +188,8 @@ it has been incremented. ossl_provider_free() doesn't return any value. -ossl_provider_add_module_location() and ossl_provider_activate() -return 1 on success, or 0 on error. +ossl_provider_add_module_location(), ossl_provider_set_fallback() and +ossl_provider_activate() return 1 on success, or 0 on error. ossl_provider_name(), ossl_provider_dso(), ossl_provider_module_name(), and ossl_provider_module_path() return a diff --git a/include/internal/provider.h b/include/internal/provider.h index ac70fcc5b6..8af20a7cfe 100644 --- a/include/internal/provider.h +++ b/include/internal/provider.h @@ -34,6 +34,7 @@ void ossl_provider_free(OSSL_PROVIDER *prov); /* Setters */ int ossl_provider_add_module_location(OSSL_PROVIDER *prov, const char *loc); +int ossl_provider_set_fallback(OSSL_PROVIDER *prov); /* * Activate the Provider diff --git a/include/openssl/cryptoerr.h b/include/openssl/cryptoerr.h index c27f05c9af..42fd3b6544 100644 --- a/include/openssl/cryptoerr.h +++ b/include/openssl/cryptoerr.h @@ -49,6 +49,7 @@ int ERR_load_CRYPTO_strings(void); # define CRYPTO_F_PKEY_HMAC_INIT 123 # define CRYPTO_F_PKEY_POLY1305_INIT 124 # define CRYPTO_F_PKEY_SIPHASH_INIT 125 +# define CRYPTO_F_PROVIDER_ACTIVATE 134 # define CRYPTO_F_SK_RESERVE 129 /* -- 2.25.1