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)
{