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) { - 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); + int found_activated = 0; - if (prov->flag_initialized - && !(ret = cb(prov, cbdata))) - break; + CRYPTO_THREAD_read_lock(store->lock); + ret = provider_forall_loaded(store, &found_activated, cb, cbdata); + + /* + * 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 /*