struct ossl_provider_st {
/* Flag bits */
unsigned int flag_initialized:1;
+ unsigned int flag_fallback:1;
/* OpenSSL library side data */
CRYPTO_REF_COUNT refcnt;
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;
struct provider_store_st {
STACK_OF(OSSL_PROVIDER) *providers;
CRYPTO_RWLOCK *lock;
+ unsigned int use_fallbacks:1;
};
static int provider_store_index = -1;
|| (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;
}
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);
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;
*/
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;
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;
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),
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);
}
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)
{
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,
/* 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);
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
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
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().
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