summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Levitte <levitte@openssl.org>2019-03-14 10:53:27 +0100
committerRichard Levitte <levitte@openssl.org>2019-03-19 14:06:58 +0100
commite55008a9f2ae299374dcf868b660389e84dd2e0b (patch)
tree972ecb9fbe04f18923a4885947c27c907c824e41
parent6a6d9ecd1dff669c162e8ab940dac5db2e82679d (diff)
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 <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/8480)
-rw-r--r--crypto/cpt_err.c2
-rw-r--r--crypto/err/openssl.txt1
-rw-r--r--crypto/provider_core.c122
-rw-r--r--doc/internal/man3/ossl_provider_new.pod26
-rw-r--r--include/internal/provider.h1
-rw-r--r--include/openssl/cryptoerr.h1
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<provider object> in the
I<provider object> store by C<name>.
-The I<provider object> it finds gets it's reference count
+The I<provider object> it finds gets its reference count
incremented.
ossl_provider_new() creates a new I<provider object> and stores it in
@@ -84,14 +86,20 @@ To indicate a built-in provider, the C<init_function> argument must
point at the provider initialization function for that provider.
ossl_provider_free() decrements a I<provider object>'s reference
-counter; if it drops to one, the I<provider object> 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<provider object> will be freed.
+counter; if it drops below 2, the I<provider object> 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<provider object> is
+assumed to have been taken out of the store, and the associated module
+will be unloaded if one was loaded, and the I<provider object> 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<provider object> pointer that was
+used can simply be dropped, but not freed.
+
ossl_provider_activate() "activates" the provider for the given
I<provider object>.
What "activates" means depends on what type of I<provider object> 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<cb> 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
/*