summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crypto/provider.c7
-rw-r--r--crypto/provider_child.c14
-rw-r--r--crypto/provider_conf.c6
-rw-r--r--crypto/provider_core.c70
-rw-r--r--doc/internal/man3/ossl_provider_new.pod11
-rw-r--r--include/internal/provider.h3
6 files changed, 75 insertions, 36 deletions
diff --git a/crypto/provider.c b/crypto/provider.c
index c8db329837..82d980a8ae 100644
--- a/crypto/provider.c
+++ b/crypto/provider.c
@@ -18,7 +18,7 @@
OSSL_PROVIDER *OSSL_PROVIDER_try_load(OSSL_LIB_CTX *libctx, const char *name,
int retain_fallbacks)
{
- OSSL_PROVIDER *prov = NULL;
+ OSSL_PROVIDER *prov = NULL, *actual;
int isnew = 0;
/* Find it or create it */
@@ -33,13 +33,14 @@ OSSL_PROVIDER *OSSL_PROVIDER_try_load(OSSL_LIB_CTX *libctx, const char *name,
return NULL;
}
- if (isnew && !ossl_provider_add_to_store(prov, retain_fallbacks)) {
+ actual = prov;
+ if (isnew && !ossl_provider_add_to_store(prov, &actual, retain_fallbacks)) {
ossl_provider_deactivate(prov);
ossl_provider_free(prov);
return NULL;
}
- return prov;
+ return actual;
}
OSSL_PROVIDER *OSSL_PROVIDER_load(OSSL_LIB_CTX *libctx, const char *name)
diff --git a/crypto/provider_child.c b/crypto/provider_child.c
index 3cad1c564f..272d67a52d 100644
--- a/crypto/provider_child.c
+++ b/crypto/provider_child.c
@@ -127,9 +127,9 @@ static int provider_create_child_cb(const OSSL_CORE_HANDLE *prov, void *cbdata)
if ((cprov = ossl_provider_find(ctx, provname, 1)) != NULL) {
/*
- * We free the newly created ref. We rely on the provider sticking around
- * in the provider store.
- */
+ * We free the newly created ref. We rely on the provider sticking around
+ * in the provider store.
+ */
ossl_provider_free(cprov);
/*
@@ -152,17 +152,11 @@ static int provider_create_child_cb(const OSSL_CORE_HANDLE *prov, void *cbdata)
goto err;
if (!ossl_provider_set_child(cprov, prov)
- || !ossl_provider_add_to_store(cprov, 0)) {
+ || !ossl_provider_add_to_store(cprov, NULL, 0)) {
ossl_provider_deactivate(cprov);
ossl_provider_free(cprov);
goto err;
}
-
- /*
- * We free the newly created ref. We rely on the provider sticking around
- * in the provider store.
- */
- ossl_provider_free(cprov);
}
ret = 1;
diff --git a/crypto/provider_conf.c b/crypto/provider_conf.c
index 14a2d62a7e..1d4e695fb8 100644
--- a/crypto/provider_conf.c
+++ b/crypto/provider_conf.c
@@ -113,7 +113,7 @@ static int provider_conf_load(OSSL_LIB_CTX *libctx, const char *name,
int i;
STACK_OF(CONF_VALUE) *ecmds;
int soft = 0;
- OSSL_PROVIDER *prov = NULL;
+ OSSL_PROVIDER *prov = NULL, *actual = NULL;
const char *path = NULL;
long activate = 0;
int ok = 0;
@@ -173,13 +173,13 @@ static int provider_conf_load(OSSL_LIB_CTX *libctx, const char *name,
if (ok) {
if (!ossl_provider_activate(prov, 1, 0)) {
ok = 0;
- } else if (!ossl_provider_add_to_store(prov, 0)) {
+ } else if (!ossl_provider_add_to_store(prov, &actual, 0)) {
ossl_provider_deactivate(prov);
ok = 0;
} else {
if (pcgbl->activated_providers == NULL)
pcgbl->activated_providers = sk_OSSL_PROVIDER_new_null();
- sk_OSSL_PROVIDER_push(pcgbl->activated_providers, prov);
+ sk_OSSL_PROVIDER_push(pcgbl->activated_providers, actual);
ok = 1;
}
}
diff --git a/crypto/provider_core.c b/crypto/provider_core.c
index 1878ffab7c..1f688557c1 100644
--- a/crypto/provider_core.c
+++ b/crypto/provider_core.c
@@ -511,33 +511,69 @@ static int create_provider_children(OSSL_PROVIDER *prov)
return ret;
}
-int ossl_provider_add_to_store(OSSL_PROVIDER *prov, int retain_fallbacks)
+int ossl_provider_add_to_store(OSSL_PROVIDER *prov, OSSL_PROVIDER **actualprov,
+ int retain_fallbacks)
{
- struct provider_store_st *store = NULL;
- int ret = 1;
+ struct provider_store_st *store;
+ int idx;
+ OSSL_PROVIDER tmpl = { 0, };
+ OSSL_PROVIDER *actualtmp = NULL;
if ((store = get_provider_store(prov->libctx)) == NULL)
return 0;
-
- if (!ossl_provider_up_ref(prov)) {
- ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
+ if (!CRYPTO_THREAD_write_lock(store->lock))
return 0;
+
+ tmpl.name = (char *)prov->name;
+ idx = sk_OSSL_PROVIDER_find(store->providers, &tmpl);
+ if (idx == -1)
+ actualtmp = prov;
+ else
+ actualtmp = sk_OSSL_PROVIDER_value(store->providers, idx);
+
+ if (actualprov != NULL) {
+ if (!ossl_provider_up_ref(actualtmp)) {
+ ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
+ actualtmp = NULL;
+ goto err;
+ }
+ *actualprov = actualtmp;
}
- if (!CRYPTO_THREAD_write_lock(store->lock)
- || sk_OSSL_PROVIDER_push(store->providers, prov) == 0) {
- ossl_provider_free(prov);
- ret = 0;
- }
- prov->store = store;
- if (!retain_fallbacks)
- store->use_fallbacks = 0;
- if (!create_provider_children(prov)) {
- ret = 0;
+
+ if (idx == -1) {
+ if (sk_OSSL_PROVIDER_push(store->providers, prov) == 0)
+ goto err;
+ prov->store = store;
+ if (!create_provider_children(prov)) {
+ sk_OSSL_PROVIDER_delete_ptr(store->providers, prov);
+ goto err;
+ }
+ if (!retain_fallbacks)
+ store->use_fallbacks = 0;
}
+
CRYPTO_THREAD_unlock(store->lock);
- return ret;
+ if (actualtmp != prov) {
+ /*
+ * The provider is already in the store. Probably two threads
+ * independently initialised their own provider objects with the same
+ * name and raced to put them in the store. This thread lost. We
+ * deactivate the one we just created and use the one that already
+ * exists instead.
+ */
+ ossl_provider_deactivate(prov);
+ ossl_provider_free(prov);
+ }
+
+ return 1;
+
+ err:
+ CRYPTO_THREAD_unlock(store->lock);
+ if (actualprov != NULL)
+ ossl_provider_free(actualtmp);
+ return 0;
}
void ossl_provider_free(OSSL_PROVIDER *prov)
diff --git a/doc/internal/man3/ossl_provider_new.pod b/doc/internal/man3/ossl_provider_new.pod
index 928cc9b844..09b2e04117 100644
--- a/doc/internal/man3/ossl_provider_new.pod
+++ b/doc/internal/man3/ossl_provider_new.pod
@@ -55,7 +55,8 @@ ossl_provider_get_capabilities
*/
int ossl_provider_activate(OSSL_PROVIDER *prov, int upcalls, int aschild);
int ossl_provider_deactivate(OSSL_PROVIDER *prov);
- int ossl_provider_add_to_store(OSSL_PROVIDER *prov, int retain_fallbacks);
+ int ossl_provider_add_to_store(OSSL_PROVIDER *prov, OSSL_PROVIDER **actualprov,
+ int retain_fallbacks);
/* Return pointer to the provider's context */
void *ossl_provider_ctx(const OSSL_PROVIDER *prov);
@@ -229,7 +230,13 @@ that count reaches zero, the activation flag is cleared.
ossl_provider_add_to_store() adds the provider I<prov> to the provider store and
makes it available to other threads. This will prevent future automatic loading
-of fallback providers, unless I<retain_fallbacks> is true.
+of fallback providers, unless I<retain_fallbacks> is true. If a provider of the
+same name already exists in the store then it is not added but this function
+still returns success. On success the I<actualprov> value is populated with a
+pointer to the provider of the given name that is now in the store. The
+reference passed in the I<prov> argument is consumed by this function. A
+reference to the provider that should be used is passed back in the
+I<actualprov> argument.
ossl_provider_ctx() returns a context created by the provider.
Outside of the provider, it's completely opaque, but it needs to be
diff --git a/include/internal/provider.h b/include/internal/provider.h
index 9b1d9495dd..237c852e8d 100644
--- a/include/internal/provider.h
+++ b/include/internal/provider.h
@@ -58,7 +58,8 @@ int ossl_provider_disable_fallback_loading(OSSL_LIB_CTX *libctx);
*/
int ossl_provider_activate(OSSL_PROVIDER *prov, int upcalls, int aschild);
int ossl_provider_deactivate(OSSL_PROVIDER *prov);
-int ossl_provider_add_to_store(OSSL_PROVIDER *prov, int retain_fallbacks);
+int ossl_provider_add_to_store(OSSL_PROVIDER *prov, OSSL_PROVIDER **actualprov,
+ int retain_fallbacks);
/* Return pointer to the provider's context */
void *ossl_provider_ctx(const OSSL_PROVIDER *prov);