[openssl] master update

Matt Caswell matt at openssl.org
Thu Jun 24 13:49:10 UTC 2021


The branch master has been updated
       via  59a783d05ae379335f70261126d19859ae5a855d (commit)
       via  d382c4652570766fc7a9ccfc63e7a62aea3d5bcb (commit)
       via  29d46e09ce0e66b5dc8faecfa48d5f667217302e (commit)
       via  b7248964a40d272bb2edc7d2f94b9ce35c804cff (commit)
       via  549b5cb4edbc8d537ae73b6f5614efdd98ed79f4 (commit)
       via  dcbb2be7f76776e86dbdc1b6599c199a16af3742 (commit)
       via  f109e96559097b882ad772b6b6396abfb1818cfe (commit)
       via  b91687c567abdd37cc1920be543eb1961a7351b4 (commit)
       via  814c2018e11c99aeb3d84e0fee2b3943ff4039c8 (commit)
       via  eb2263da9abf3676cbcac672eee8a26416a8c309 (commit)
       via  d5fbd5b4eda592fd43e8688f69f3bac4ca5dd2da (commit)
       via  29aff653150c363be2d84f789a10b46d99d5cab9 (commit)
       via  352d482a2990cc04adff48aeda9c080d4a839f1e (commit)
       via  1d74203cf5d8542d349fbb2d5f35ad40994dec9f (commit)
       via  8d4dec0d4b3055b4c2e7ece5ac99b67b3e77995e (commit)
      from  ab7554e5a08966c159054ae7df18a879bfe3865f (commit)


- Log -----------------------------------------------------------------
commit 59a783d05ae379335f70261126d19859ae5a855d
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Jun 22 15:39:40 2021 +0100

    Fix a race in ossl_provider_add_to_store()
    
    If two threads both attempt to load the same provider at the same time,
    they will first both check to see if the provider already exists. If it
    doesn't then they will both then create new provider objects and call the
    init function. However only one of the threads will be successful in adding
    the provider to the store. For the "losing" thread we should still return
    "success", but we should deinitialise and free the no longer required
    provider object, and return the object that exists in the store.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit d382c4652570766fc7a9ccfc63e7a62aea3d5bcb
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Jun 22 12:07:48 2021 +0100

    Move OPENSSL_add_builtin back into provider.c
    
    An earlier stage of the refactor in the last few commits moved this
    function out of provider.c because it needed access to the provider
    structure internals. The final version however no longer needs this so
    it is moved back again.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit 29d46e09ce0e66b5dc8faecfa48d5f667217302e
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 21 17:09:32 2021 +0100

    Update documentation following updates to the provider code
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit b7248964a40d272bb2edc7d2f94b9ce35c804cff
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 21 15:59:41 2021 +0100

    make struct provider_info_st a full type
    
    Create the OSSL_PROVIDER_INFO to replace struct provider_info_st.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit 549b5cb4edbc8d537ae73b6f5614efdd98ed79f4
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 21 15:37:48 2021 +0100

    Don't skip the current provider in ossl_provider_register_child_cb
    
    This restriction was in place to avoid problems with recursive attempts
    to aquire the flag lock/store lock from within a provider's init function.
    Since those locks are no longer held when calling the init function there
    is no reason for the restriction any more.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit dcbb2be7f76776e86dbdc1b6599c199a16af3742
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 21 13:01:57 2021 +0100

    Add a test to check that RAND_bytes_ex() works with a child lib ctx
    
    Previously, when locks were held while calling a provider init function,
    then RAND_bytes_ex() would fail if called from the init function and
    used in conjunction with a child lib ctx. We add an explicit test of that.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit f109e96559097b882ad772b6b6396abfb1818cfe
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 21 12:49:59 2021 +0100

    Don't hold any locks while calling the provider init function
    
    Previously providers were added to the store first, and then subsequently
    initialised. This meant that during initialisation the provider object
    could be shared between multiple threads and hence the locks needed to be
    held. However this causes problems because the provider init function is
    essentially a user callback and could do virtually anything. There are
    many API calls that could be invoked that could subsequently attempt to
    acquire the locks. This will fail because the locks are already held.
    
    However, now we have refactored things so that the provider is created and
    initialised before being added to the store. Therefore at the point of
    initialisation the provider object is not shared with other threads and so
    no locks need to be held.
    
    Fixes #15793
    Fixes #15712
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit b91687c567abdd37cc1920be543eb1961a7351b4
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 21 12:13:31 2021 +0100

    Only associate a provider with a store once it has been added to it
    
    This means we can distinguish providers that have been added to the
    store, and those which haven't yet been.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit 814c2018e11c99aeb3d84e0fee2b3943ff4039c8
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 21 12:08:39 2021 +0100

    Merge ossl_provider_activate() and ossl_provider_activate_child()
    
    These 2 functions have become so close to each other that they may as well
    be just one function.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit eb2263da9abf3676cbcac672eee8a26416a8c309
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 21 11:34:04 2021 +0100

    Set use_fallbacks to zero when we add a provider to the store
    
    Update use_fallbacks to zero when we add a provider to the store rather
    than when we activate it. Its only at the point that we add it to the store
    that it is actually usable and visible to other threads.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit d5fbd5b4eda592fd43e8688f69f3bac4ca5dd2da
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 21 11:06:12 2021 +0100

    Remove flag_couldbechild
    
    Now that a provider is no longer put into the store until after it has
    been activated we don't need flag_couldbechild any more. This flag was
    used to indicate whether a provider was eligible for conversion into a
    child provider or not. This was only really interesting for predefined
    providers that were automatically created.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit 29aff653150c363be2d84f789a10b46d99d5cab9
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 21 09:23:30 2021 +0100

    Add a new provider to the store only after we activate it
    
    Rather than creating the provider, adding to the store and then activating
    it, we do things the other way around, i.e. activate first and then add to
    the store. This means that the activation should occur before other threads
    are aware of the provider.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit 352d482a2990cc04adff48aeda9c080d4a839f1e
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jun 18 15:56:54 2021 +0100

    Instantiate configuration supplied providers when we need them
    
    If provider specified in a config file are not "activated" then we defer
    instantiating the provider object until it is actually needed.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit 1d74203cf5d8542d349fbb2d5f35ad40994dec9f
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jun 18 12:28:40 2021 +0100

    Instantiate user-added builtin providers when we need them
    
    Previously we created the provider object for builtin providers at the
    point that OPENSSL_add_builtin() was called. Instead we delay that until
    the provider is actually loaded.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

commit 8d4dec0d4b3055b4c2e7ece5ac99b67b3e77995e
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jun 18 10:08:23 2021 +0100

    Instantiate predefined providers just-in-time
    
    Previously we instantiated all the predefined providers at the point that
    we create the provider store. Instead we move them to be instantiated as we
    need them.
    
    Reviewed-by: Paul Dale <pauli at openssl.org>
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/15854)

-----------------------------------------------------------------------

Summary of changes:
 crypto/provider.c                       |  56 ++--
 crypto/provider_child.c                 |  26 +-
 crypto/provider_conf.c                  |  88 +++--
 crypto/provider_core.c                  | 563 +++++++++++++++++++-------------
 crypto/provider_local.h                 |  23 +-
 crypto/provider_predefined.c            |  14 +-
 doc/internal/man3/ossl_provider_new.pod |  36 +-
 doc/man3/OSSL_LIB_CTX.pod               |  20 +-
 doc/man7/provider.pod                   |   3 +
 include/internal/provider.h             |  10 +-
 include/internal/symhacks.h             |   3 -
 test/provfetchtest.c                    |  11 +
 test/provider_internal_test.c           |   2 +-
 13 files changed, 513 insertions(+), 342 deletions(-)

diff --git a/crypto/provider.c b/crypto/provider.c
index 52647b2e77..82d980a8ae 100644
--- a/crypto/provider.c
+++ b/crypto/provider.c
@@ -7,28 +7,40 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include <string.h>
 #include <openssl/err.h>
 #include <openssl/cryptoerr.h>
 #include <openssl/provider.h>
 #include <openssl/core_names.h>
 #include "internal/provider.h"
+#include "provider_local.h"
 
 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 */
-    if ((prov = ossl_provider_find(libctx, name, 0)) == NULL
-        && (prov = ossl_provider_new(libctx, name, NULL, 0)) == NULL)
+    if ((prov = ossl_provider_find(libctx, name, 0)) == NULL) {
+        if ((prov = ossl_provider_new(libctx, name, NULL, 0)) == NULL)
+            return NULL;
+        isnew = 1;
+    }
+
+    if (!ossl_provider_activate(prov, 1, 0)) {
+        ossl_provider_free(prov);
         return NULL;
+    }
 
-    if (!ossl_provider_activate(prov, retain_fallbacks, 1)) {
+    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)
@@ -47,18 +59,6 @@ int OSSL_PROVIDER_unload(OSSL_PROVIDER *prov)
     return 1;
 }
 
-int OSSL_PROVIDER_available(OSSL_LIB_CTX *libctx, const char *name)
-{
-    OSSL_PROVIDER *prov = NULL;
-    int available = 0;
-
-    /* Find it or create it */
-    prov = ossl_provider_find(libctx, name, 0);
-    available = ossl_provider_available(prov);
-    ossl_provider_free(prov);
-    return available;
-}
-
 const OSSL_PARAM *OSSL_PROVIDER_gettable_params(const OSSL_PROVIDER *prov)
 {
     return ossl_provider_gettable_params(prov);
@@ -109,23 +109,23 @@ int OSSL_PROVIDER_get_capabilities(const OSSL_PROVIDER *prov,
 int OSSL_PROVIDER_add_builtin(OSSL_LIB_CTX *libctx, const char *name,
                               OSSL_provider_init_fn *init_fn)
 {
-    OSSL_PROVIDER *prov = NULL;
+    OSSL_PROVIDER_INFO entry;
 
     if (name == NULL || init_fn == NULL) {
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
     }
-
-    /* Create it */
-    if ((prov = ossl_provider_new(libctx, name, init_fn, 0)) == NULL)
+    memset(&entry, 0, sizeof(entry));
+    entry.name = OPENSSL_strdup(name);
+    if (entry.name == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
         return 0;
-
-    /*
-     * It's safely stored in the internal store at this point,
-     * free the returned extra reference
-     */
-    ossl_provider_free(prov);
-
+    }
+    entry.init = init_fn;
+    if (!ossl_provider_info_add_to_store(libctx, &entry)) {
+        ossl_provider_info_clear(&entry);
+        return 0;
+    }
     return 1;
 }
 
diff --git a/crypto/provider_child.c b/crypto/provider_child.c
index 7ab161b795..272d67a52d 100644
--- a/crypto/provider_child.c
+++ b/crypto/provider_child.c
@@ -127,19 +127,17 @@ 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);
 
         /*
-         * The provider already exists. It could be an unused built-in, or a
-         * previously created child, or it could have been explicitly loaded. If
-         * explicitly loaded it cannot be converted to a child and we ignore it
-         * - i.e. we don't start treating it like a child.
+         * The provider already exists. It could be a previously created child,
+         * or it could have been explicitly loaded. If explicitly loaded we
+         * ignore it - i.e. we don't start treating it like a child.
          */
-        if (!ossl_provider_convert_to_child(cprov, prov,
-                                            ossl_child_provider_init))
+        if (!ossl_provider_activate(cprov, 0, 1))
             goto err;
     } else {
         /*
@@ -150,17 +148,13 @@ static int provider_create_child_cb(const OSSL_CORE_HANDLE *prov, void *cbdata)
                                        1)) == NULL)
             goto err;
 
-        /*
-        * We free the newly created ref. We rely on the provider sticking around
-        * in the provider store.
-        */
-        ossl_provider_free(cprov);
-
         if (!ossl_provider_activate(cprov, 0, 0))
             goto err;
 
-        if (!ossl_provider_set_child(cprov, prov)) {
+        if (!ossl_provider_set_child(cprov, prov)
+            || !ossl_provider_add_to_store(cprov, NULL, 0)) {
             ossl_provider_deactivate(cprov);
+            ossl_provider_free(cprov);
             goto err;
         }
     }
diff --git a/crypto/provider_conf.c b/crypto/provider_conf.c
index 977d469808..1d4e695fb8 100644
--- a/crypto/provider_conf.c
+++ b/crypto/provider_conf.c
@@ -14,6 +14,7 @@
 #include <openssl/safestack.h>
 #include "internal/provider.h"
 #include "internal/cryptlib.h"
+#include "provider_local.h"
 
 DEFINE_STACK_OF(OSSL_PROVIDER)
 
@@ -61,6 +62,7 @@ static const char *skip_dot(const char *name)
 }
 
 static int provider_conf_params(OSSL_PROVIDER *prov,
+                                OSSL_PROVIDER_INFO *provinfo,
                                 const char *name, const char *value,
                                 const CONF *cnf)
 {
@@ -88,14 +90,18 @@ static int provider_conf_params(OSSL_PROVIDER *prov,
                 return 0;
             buffer[buffer_len] = '\0';
             OPENSSL_strlcat(buffer, sectconf->name, sizeof(buffer));
-            if (!provider_conf_params(prov, buffer, sectconf->value, cnf))
+            if (!provider_conf_params(prov, provinfo, buffer, sectconf->value,
+                                      cnf))
                 return 0;
         }
 
         OSSL_TRACE1(CONF, "Provider params: finish section %s\n", value);
     } else {
         OSSL_TRACE2(CONF, "Provider params: %s = %s\n", name, value);
-        ok = ossl_provider_add_parameter(prov, name, value);
+        if (prov != NULL)
+            ok = ossl_provider_add_parameter(prov, name, value);
+        else
+            ok = ossl_provider_info_add_parameter(provinfo, name, value);
     }
 
     return ok;
@@ -107,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;
@@ -149,33 +155,65 @@ static int provider_conf_load(OSSL_LIB_CTX *libctx, const char *name,
             activate = 1;
     }
 
-    prov = ossl_provider_find(libctx, name, 1);
-    if (prov == NULL)
-        prov = ossl_provider_new(libctx, name, NULL, 1);
-    if (prov == NULL) {
-        if (soft)
-            ERR_clear_error();
-        return 0;
-    }
+    if (activate) {
+        prov = ossl_provider_find(libctx, name, 1);
+        if (prov == NULL)
+            prov = ossl_provider_new(libctx, name, NULL, 1);
+        if (prov == NULL) {
+            if (soft)
+                ERR_clear_error();
+            return 0;
+        }
 
-    if (path != NULL)
-        ossl_provider_set_module_path(prov, path);
+        if (path != NULL)
+            ossl_provider_set_module_path(prov, path);
+
+        ok = provider_conf_params(prov, NULL, NULL, value, cnf);
+
+        if (ok) {
+            if (!ossl_provider_activate(prov, 1, 0)) {
+                ok = 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, actual);
+                ok = 1;
+            }
+        }
 
-    ok = provider_conf_params(prov, NULL, value, cnf);
+        if (!ok)
+            ossl_provider_free(prov);
+    } else {
+        OSSL_PROVIDER_INFO entry;
 
-    if (ok && activate) {
-        if (!ossl_provider_activate(prov, 0, 1)) {
-            ok = 0;
-        } else {
-            if (pcgbl->activated_providers == NULL)
-                pcgbl->activated_providers = sk_OSSL_PROVIDER_new_null();
-            sk_OSSL_PROVIDER_push(pcgbl->activated_providers, prov);
-            ok = 1;
+        memset(&entry, 0, sizeof(entry));
+        ok = 1;
+        if (name != NULL) {
+            entry.name = OPENSSL_strdup(name);
+            if (entry.name == NULL) {
+                ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
+                ok = 0;
+            }
+        }
+        if (ok && path != NULL) {
+            entry.path = OPENSSL_strdup(path);
+            if (entry.path == NULL) {
+                ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
+                ok = 0;
+            }
+        }
+        if (ok)
+            ok = provider_conf_params(NULL, &entry, NULL, value, cnf);
+        if (ok && (entry.path != NULL || entry.parameters != NULL))
+            ok = ossl_provider_info_add_to_store(libctx, &entry);
+        if (!ok || (entry.path == NULL && entry.parameters == NULL)) {
+            ossl_provider_info_clear(&entry);
         }
-    }
 
-    if (!(activate && ok))
-        ossl_provider_free(prov);
+    }
 
     return ok;
 }
diff --git a/crypto/provider_core.c b/crypto/provider_core.c
index 4c423a6bda..1f688557c1 100644
--- a/crypto/provider_core.c
+++ b/crypto/provider_core.c
@@ -29,19 +29,14 @@
 #endif
 
 static OSSL_PROVIDER *provider_new(const char *name,
-                                   OSSL_provider_init_fn *init_function);
+                                   OSSL_provider_init_fn *init_function,
+                                   STACK_OF(INFOPAIR) *parameters);
 
 /*-
  * Provider Object structure
  * =========================
  */
 
-typedef struct {
-    char *name;
-    char *value;
-} INFOPAIR;
-DEFINE_STACK_OF(INFOPAIR)
-
 #ifndef FIPS_MODULE
 typedef struct {
     OSSL_PROVIDER *prov;
@@ -60,9 +55,6 @@ struct ossl_provider_st {
     unsigned int flag_initialized:1;
     unsigned int flag_activated:1;
     unsigned int flag_fallback:1; /* Can be used as fallback */
-#ifndef FIPS_MODULE
-    unsigned int flag_couldbechild:1;
-#endif
 
     /* Getting and setting the flags require synchronization */
     CRYPTO_RWLOCK *flag_lock;
@@ -139,6 +131,9 @@ struct provider_store_st {
     CRYPTO_RWLOCK *default_path_lock;
     CRYPTO_RWLOCK *lock;
     char *default_path;
+    OSSL_PROVIDER_INFO *provinfo;
+    size_t numprovinfo;
+    size_t provinfosz;
     unsigned int use_fallbacks:1;
     unsigned int freeing:1;
 };
@@ -163,9 +158,47 @@ static void ossl_provider_child_cb_free(OSSL_PROVIDER_CHILD_CB *cb)
 }
 #endif
 
+static void infopair_free(INFOPAIR *pair)
+{
+    OPENSSL_free(pair->name);
+    OPENSSL_free(pair->value);
+    OPENSSL_free(pair);
+}
+
+static INFOPAIR *infopair_copy(const INFOPAIR *src)
+{
+    INFOPAIR *dest = OPENSSL_zalloc(sizeof(*dest));
+
+    if (dest == NULL)
+        return NULL;
+    if (src->name != NULL) {
+        dest->name = OPENSSL_strdup(src->name);
+        if (dest->name == NULL)
+            goto err;
+    }
+    if (src->value != NULL) {
+        dest->value = OPENSSL_strdup(src->value);
+        if (dest->value == NULL)
+            goto err;
+    }
+    return dest;
+ err:
+    OPENSSL_free(dest->name);
+    OPENSSL_free(dest);
+    return NULL;
+}
+
+void ossl_provider_info_clear(OSSL_PROVIDER_INFO *info)
+{
+    OPENSSL_free(info->name);
+    OPENSSL_free(info->path);
+    sk_INFOPAIR_pop_free(info->parameters, infopair_free);
+}
+
 static void provider_store_free(void *vstore)
 {
     struct provider_store_st *store = vstore;
+    size_t i;
 
     if (store == NULL)
         return;
@@ -178,13 +211,15 @@ static void provider_store_free(void *vstore)
 #endif
     CRYPTO_THREAD_lock_free(store->default_path_lock);
     CRYPTO_THREAD_lock_free(store->lock);
+    for (i = 0; i < store->numprovinfo; i++)
+        ossl_provider_info_clear(&store->provinfo[i]);
+    OPENSSL_free(store->provinfo);
     OPENSSL_free(store);
 }
 
 static void *provider_store_new(OSSL_LIB_CTX *ctx)
 {
     struct provider_store_st *store = OPENSSL_zalloc(sizeof(*store));
-    const struct predefined_providers_st *p = NULL;
 
     if (store == NULL
         || (store->providers = sk_OSSL_PROVIDER_new(ossl_provider_cmp)) == NULL
@@ -199,31 +234,6 @@ static void *provider_store_new(OSSL_LIB_CTX *ctx)
     store->libctx = ctx;
     store->use_fallbacks = 1;
 
-    for (p = ossl_predefined_providers; p->name != NULL; p++) {
-        OSSL_PROVIDER *prov = NULL;
-
-        /*
-         * We use the internal constructor directly here,
-         * otherwise we get a call loop
-         */
-        prov = provider_new(p->name, p->init);
-
-        if (prov == NULL
-            || sk_OSSL_PROVIDER_push(store->providers, prov) == 0) {
-            ossl_provider_free(prov);
-            provider_store_free(store);
-            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
-            return NULL;
-        }
-        prov->libctx = ctx;
-        prov->store = store;
-#ifndef FIPS_MODULE
-        prov->error_lib = ERR_get_next_error_library();
-#endif
-        if(p->is_fallback)
-            ossl_provider_set_fallback(prov);
-    }
-
     return store;
 }
 
@@ -259,6 +269,56 @@ int ossl_provider_disable_fallback_loading(OSSL_LIB_CTX *libctx)
     return 0;
 }
 
+#define BUILTINS_BLOCK_SIZE     10
+
+int ossl_provider_info_add_to_store(OSSL_LIB_CTX *libctx,
+                                    OSSL_PROVIDER_INFO *entry)
+{
+    struct provider_store_st *store = get_provider_store(libctx);
+    int ret = 0;
+
+    if (entry->name == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (store == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    if (!CRYPTO_THREAD_write_lock(store->lock))
+        return 0;
+    if (store->provinfosz == 0) {
+        store->provinfo = OPENSSL_zalloc(sizeof(*store->provinfo)
+                                         * BUILTINS_BLOCK_SIZE);
+        if (store->provinfo == NULL) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        store->provinfosz = BUILTINS_BLOCK_SIZE;
+    } else if (store->numprovinfo == store->provinfosz) {
+        OSSL_PROVIDER_INFO *tmpbuiltins;
+        size_t newsz = store->provinfosz + BUILTINS_BLOCK_SIZE;
+
+        tmpbuiltins = OPENSSL_realloc(store->provinfo,
+                                      sizeof(*store->provinfo) * newsz);
+        if (tmpbuiltins == NULL) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        store->provinfo = tmpbuiltins;
+        store->provinfosz = newsz;
+    }
+    store->provinfo[store->numprovinfo] = *entry;
+    store->numprovinfo++;
+
+    ret = 1;
+ err:
+    CRYPTO_THREAD_unlock(store->lock);
+    return ret;
+}
+
 OSSL_PROVIDER *ossl_provider_find(OSSL_LIB_CTX *libctx, const char *name,
                                   int noconfig)
 {
@@ -299,7 +359,8 @@ OSSL_PROVIDER *ossl_provider_find(OSSL_LIB_CTX *libctx, const char *name,
  */
 
 static OSSL_PROVIDER *provider_new(const char *name,
-                                   OSSL_provider_init_fn *init_function)
+                                   OSSL_provider_init_fn *init_function,
+                                   STACK_OF(INFOPAIR) *parameters)
 {
     OSSL_PROVIDER *prov = NULL;
 
@@ -309,7 +370,10 @@ static OSSL_PROVIDER *provider_new(const char *name,
 #endif
         || (prov->opbits_lock = CRYPTO_THREAD_lock_new()) == NULL
         || (prov->flag_lock = CRYPTO_THREAD_lock_new()) == NULL
-        || (prov->name = OPENSSL_strdup(name)) == NULL) {
+        || (prov->name = OPENSSL_strdup(name)) == NULL
+        || (prov->parameters = sk_INFOPAIR_deep_copy(parameters,
+                                                     infopair_copy,
+                                                     infopair_free)) == NULL) {
         ossl_provider_free(prov);
         ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
         return NULL;
@@ -317,9 +381,7 @@ static OSSL_PROVIDER *provider_new(const char *name,
 
     prov->refcnt = 1; /* 1 One reference to be returned */
     prov->init_function = init_function;
-#ifndef FIPS_MODULE
-    prov->flag_couldbechild = 1;
-#endif
+
     return prov;
 }
 
@@ -346,7 +408,7 @@ int ossl_provider_up_ref(OSSL_PROVIDER *prov)
 static int provider_up_ref_intern(OSSL_PROVIDER *prov, int activate)
 {
     if (activate)
-        return ossl_provider_activate(prov, 0, 1);
+        return ossl_provider_activate(prov, 1, 0);
 
     return ossl_provider_up_ref(prov);
 }
@@ -366,6 +428,7 @@ OSSL_PROVIDER *ossl_provider_new(OSSL_LIB_CTX *libctx, const char *name,
                                  int noconfig)
 {
     struct provider_store_st *store = NULL;
+    OSSL_PROVIDER_INFO template;
     OSSL_PROVIDER *prov = NULL;
 
     if ((store = get_provider_store(libctx)) == NULL)
@@ -379,44 +442,138 @@ OSSL_PROVIDER *ossl_provider_new(OSSL_LIB_CTX *libctx, const char *name,
         return NULL;
     }
 
+    memset(&template, 0, sizeof(template));
+    if (init_function == NULL) {
+        const OSSL_PROVIDER_INFO *p;
+        size_t i;
+
+        /* Check if this is a predefined builtin provider */
+        for (p = ossl_predefined_providers; p->name != NULL; p++) {
+            if (strcmp(p->name, name) == 0) {
+                template = *p;
+                break;
+            }
+        }
+        if (p->name == NULL) {
+            /* Check if this is a user added builtin provider */
+            if (!CRYPTO_THREAD_read_lock(store->lock))
+                return NULL;
+            for (i = 0, p = store->provinfo; i < store->numprovinfo; p++, i++) {
+                if (strcmp(p->name, name) == 0) {
+                    template = *p;
+                    break;
+                }
+            }
+            CRYPTO_THREAD_unlock(store->lock);
+        }
+    } else {
+        template.init = init_function;
+    }
+
     /* provider_new() generates an error, so no need here */
-    if ((prov = provider_new(name, init_function)) == NULL)
+    if ((prov = provider_new(name, template.init, template.parameters)) == NULL)
         return NULL;
 
-    if (!CRYPTO_THREAD_write_lock(store->lock))
-        return NULL;
-    if (!ossl_provider_up_ref(prov)) { /* +1 One reference for the store */
-        ossl_provider_free(prov); /* -1 Reference that was to be returned */
-        prov = NULL;
-    } else if (sk_OSSL_PROVIDER_push(store->providers, prov) == 0) {
-        ossl_provider_free(prov); /* -1 Store reference */
-        ossl_provider_free(prov); /* -1 Reference that was to be returned */
-        prov = NULL;
-    } else {
-        prov->libctx = libctx;
-        prov->store = store;
+    prov->libctx = libctx;
 #ifndef FIPS_MODULE
-        prov->error_lib = ERR_get_next_error_library();
+    prov->error_lib = ERR_get_next_error_library();
 #endif
-    }
-    CRYPTO_THREAD_unlock(store->lock);
-
-    if (prov == NULL)
-        ERR_raise(ERR_LIB_CRYPTO, ERR_R_MALLOC_FAILURE);
 
     /*
      * At this point, the provider is only partially "loaded".  To be
-     * fully "loaded", ossl_provider_activate() must also be called.
+     * fully "loaded", ossl_provider_activate() must also be called and it must
+     * then be added to the provider store.
      */
 
     return prov;
 }
 
-static void free_infopair(INFOPAIR *pair)
+/* Assumes that the store lock is held */
+static int create_provider_children(OSSL_PROVIDER *prov)
 {
-    OPENSSL_free(pair->name);
-    OPENSSL_free(pair->value);
-    OPENSSL_free(pair);
+    int ret = 1;
+#ifndef FIPS_MODULE
+    struct provider_store_st *store = prov->store;
+    OSSL_PROVIDER_CHILD_CB *child_cb;
+    int i, max;
+
+    max = sk_OSSL_PROVIDER_CHILD_CB_num(store->child_cbs);
+    for (i = 0; i < max; i++) {
+        /*
+         * This is newly activated (activatecnt == 1), so we need to
+         * create child providers as necessary.
+         */
+        child_cb = sk_OSSL_PROVIDER_CHILD_CB_value(store->child_cbs, i);
+        ret &= child_cb->create_cb((OSSL_CORE_HANDLE *)prov, child_cb->cbdata);
+    }
+#endif
+
+    return ret;
+}
+
+int ossl_provider_add_to_store(OSSL_PROVIDER *prov, OSSL_PROVIDER **actualprov,
+                               int retain_fallbacks)
+{
+    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 (!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 (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);
+
+    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)
@@ -463,7 +620,7 @@ void ossl_provider_free(OSSL_PROVIDER *prov)
 #endif
             OPENSSL_free(prov->name);
             OPENSSL_free(prov->path);
-            sk_INFOPAIR_pop_free(prov->parameters, free_infopair);
+            sk_INFOPAIR_pop_free(prov->parameters, infopair_free);
             CRYPTO_THREAD_lock_free(prov->opbits_lock);
             CRYPTO_THREAD_lock_free(prov->flag_lock);
 #ifndef HAVE_ATOMICS
@@ -492,17 +649,17 @@ int ossl_provider_set_module_path(OSSL_PROVIDER *prov, const char *module_path)
     return 0;
 }
 
-int ossl_provider_add_parameter(OSSL_PROVIDER *prov,
-                                const char *name, const char *value)
+static int infopair_add(STACK_OF(INFOPAIR) **infopairsk, const char *name,
+                        const char *value)
 {
     INFOPAIR *pair = NULL;
 
     if ((pair = OPENSSL_zalloc(sizeof(*pair))) != NULL
-        && (prov->parameters != NULL
-            || (prov->parameters = sk_INFOPAIR_new_null()) != NULL)
+        && (*infopairsk != NULL
+            || (*infopairsk = sk_INFOPAIR_new_null()) != NULL)
         && (pair->name = OPENSSL_strdup(name)) != NULL
         && (pair->value = OPENSSL_strdup(value)) != NULL
-        && sk_INFOPAIR_push(prov->parameters, pair) > 0)
+        && sk_INFOPAIR_push(*infopairsk, pair) > 0)
         return 1;
 
     if (pair != NULL) {
@@ -514,6 +671,19 @@ int ossl_provider_add_parameter(OSSL_PROVIDER *prov,
     return 0;
 }
 
+int ossl_provider_add_parameter(OSSL_PROVIDER *prov,
+                                const char *name, const char *value)
+{
+    return infopair_add(&prov->parameters, name, value);
+}
+
+int ossl_provider_info_add_parameter(OSSL_PROVIDER_INFO *provinfo,
+                                     const char *name,
+                                     const char *value)
+{
+    return infopair_add(&provinfo->parameters, name, value);
+}
+
 /*
  * Provider activation.
  *
@@ -557,7 +727,7 @@ int OSSL_PROVIDER_set_default_search_path(OSSL_LIB_CTX *libctx,
  * locking.  Direct callers must remember to set the store flags when
  * appropriate.
  */
-static int provider_init(OSSL_PROVIDER *prov, int flag_lock)
+static int provider_init(OSSL_PROVIDER *prov)
 {
     const OSSL_DISPATCH *provider_dispatch = NULL;
     void *tmp_provctx = NULL;    /* safety measure */
@@ -568,16 +738,8 @@ static int provider_init(OSSL_PROVIDER *prov, int flag_lock)
 #endif
     int ok = 0;
 
-    /*
-     * The flag lock is used to lock init, not only because the flag is
-     * checked here and set at the end, but also because this function
-     * modifies a number of things in the provider structure that this
-     * function needs to perform under lock anyway.
-     */
-    if (flag_lock && !CRYPTO_THREAD_write_lock(prov->flag_lock))
-        goto end;
-    if (prov->flag_initialized) {
-        ok = 1;
+    if (!ossl_assert(!prov->flag_initialized)) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
         goto end;
     }
 
@@ -661,9 +823,6 @@ static int provider_init(OSSL_PROVIDER *prov, int flag_lock)
     }
     prov->provctx = tmp_provctx;
     prov->dispatch = provider_dispatch;
-#ifndef FIPS_MODULE
-    prov->flag_couldbechild = 0;
-#endif
 
     for (; provider_dispatch->function_id != 0; provider_dispatch++) {
         switch (provider_dispatch->function_id) {
@@ -757,8 +916,6 @@ static int provider_init(OSSL_PROVIDER *prov, int flag_lock)
     ok = 1;
 
  end:
-    if (flag_lock)
-        CRYPTO_THREAD_unlock(prov->flag_lock);
     return ok;
 }
 
@@ -824,59 +981,47 @@ static int provider_deactivate(OSSL_PROVIDER *prov)
 static int provider_activate(OSSL_PROVIDER *prov, int lock, int upcalls)
 {
     int count = -1;
+    struct provider_store_st *store;
+    int ret = 1;
 
-    if (provider_init(prov, lock)) {
-        int ret = 1;
-        struct provider_store_st *store;
-
-        store = get_provider_store(prov->libctx);
-        if (store == NULL)
+    store = prov->store;
+    /*
+    * If the provider hasn't been added to the store, then we don't need
+    * any locks because we've not shared it with other threads.
+    */
+    if (store == NULL) {
+        lock = 0;
+        if (!provider_init(prov))
             return -1;
+    }
 
-        if (lock && !CRYPTO_THREAD_read_lock(store->lock))
-            return -1;
+    if (lock && !CRYPTO_THREAD_read_lock(store->lock))
+        return -1;
 
-        if (lock && !CRYPTO_THREAD_write_lock(prov->flag_lock)) {
-            CRYPTO_THREAD_unlock(store->lock);
-            return -1;
-        }
+    if (lock && !CRYPTO_THREAD_write_lock(prov->flag_lock)) {
+        CRYPTO_THREAD_unlock(store->lock);
+        return -1;
+    }
 
 #ifndef FIPS_MODULE
-        if (prov->ischild && upcalls)
-            ret = ossl_provider_up_ref_parent(prov, 1);
+    if (prov->ischild && upcalls)
+        ret = ossl_provider_up_ref_parent(prov, 1);
 #endif
 
-        if (ret) {
-            count = ++prov->activatecnt;
-            prov->flag_activated = 1;
+    if (ret) {
+        count = ++prov->activatecnt;
+        prov->flag_activated = 1;
 
-#ifndef FIPS_MODULE
-            if (prov->activatecnt == 1) {
-                OSSL_PROVIDER_CHILD_CB *child_cb;
-                int i, max;
-
-                max = sk_OSSL_PROVIDER_CHILD_CB_num(store->child_cbs);
-                for (i = 0; i < max; i++) {
-                    /*
-                     * This is newly activated (activatecnt == 1), so we need to
-                     * create child providers as necessary.
-                     */
-                    child_cb = sk_OSSL_PROVIDER_CHILD_CB_value(store->child_cbs,
-                                                               i);
-                    ret &= child_cb->create_cb((OSSL_CORE_HANDLE *)prov,
-                                               child_cb->cbdata);
-                }
-            }
-#endif
-        }
+        if (prov->activatecnt == 1 && store != NULL)
+            ret = create_provider_children(prov);
+    }
 
-        if (lock) {
-            CRYPTO_THREAD_unlock(prov->flag_lock);
-            CRYPTO_THREAD_unlock(store->lock);
-        }
-        if (!ret)
-            return -1;
+    if (lock) {
+        CRYPTO_THREAD_unlock(prov->flag_lock);
+        CRYPTO_THREAD_unlock(store->lock);
     }
+    if (!ret)
+        return -1;
 
     return count;
 }
@@ -899,24 +1044,23 @@ static int provider_flush_store_cache(const OSSL_PROVIDER *prov)
     return 1;
 }
 
-int ossl_provider_activate(OSSL_PROVIDER *prov, int retain_fallbacks,
-                           int upcalls)
+int ossl_provider_activate(OSSL_PROVIDER *prov, int upcalls, int aschild)
 {
     int count;
 
     if (prov == NULL)
         return 0;
-    if ((count = provider_activate(prov, 1, upcalls)) > 0) {
-        if (!retain_fallbacks) {
-            if (!CRYPTO_THREAD_write_lock(prov->store->lock)) {
-                provider_deactivate(prov);
-                return 0;
-            }
-            prov->store->use_fallbacks = 0;
-            CRYPTO_THREAD_unlock(prov->store->lock);
-        }
+#ifndef FIPS_MODULE
+    /*
+     * If aschild is true, then we only actually do the activation if the
+     * provider is a child. If its not, this is still success.
+     */
+    if (aschild && !prov->ischild)
+        return 1;
+#endif
+    if ((count = provider_activate(prov, 1, upcalls)) > 0)
         return count == 1 ? provider_flush_store_cache(prov) : 1;
-    }
+
     return 0;
 }
 
@@ -939,53 +1083,71 @@ void *ossl_provider_ctx(const OSSL_PROVIDER *prov)
  * and then sets store->use_fallbacks = 0, so the second call and so on is
  * effectively a no-op.
  */
-static void provider_activate_fallbacks(struct provider_store_st *store)
+static int provider_activate_fallbacks(struct provider_store_st *store)
 {
     int use_fallbacks;
-    int num_provs;
     int activated_fallback_count = 0;
-    int i;
+    int ret = 0;
+    const OSSL_PROVIDER_INFO *p;
 
     if (!CRYPTO_THREAD_read_lock(store->lock))
-        return;
+        return 0;
     use_fallbacks = store->use_fallbacks;
     CRYPTO_THREAD_unlock(store->lock);
     if (!use_fallbacks)
-        return;
+        return 1;
 
     if (!CRYPTO_THREAD_write_lock(store->lock))
-        return;
+        return 0;
     /* Check again, just in case another thread changed it */
     use_fallbacks = store->use_fallbacks;
     if (!use_fallbacks) {
         CRYPTO_THREAD_unlock(store->lock);
-        return;
+        return 1;
     }
 
-    num_provs = sk_OSSL_PROVIDER_num(store->providers);
-    for (i = 0; i < num_provs; i++) {
-        OSSL_PROVIDER *prov = sk_OSSL_PROVIDER_value(store->providers, i);
+    for (p = ossl_predefined_providers; p->name != NULL; p++) {
+        OSSL_PROVIDER *prov = NULL;
 
-        if (ossl_provider_up_ref(prov)) {
-            if (CRYPTO_THREAD_write_lock(prov->flag_lock)) {
-                if (prov->flag_fallback) {
-                    if (provider_activate(prov, 0, 0) > 0)
-                        activated_fallback_count++;
-                }
-                CRYPTO_THREAD_unlock(prov->flag_lock);
-            }
+        if (!p->is_fallback)
+            continue;
+        /*
+         * We use the internal constructor directly here,
+         * otherwise we get a call loop
+         */
+        prov = provider_new(p->name, p->init, NULL);
+        if (prov == NULL)
+            goto err;
+        prov->libctx = store->libctx;
+#ifndef FIPS_MODULE
+        prov->error_lib = ERR_get_next_error_library();
+#endif
+
+        /*
+         * We are calling provider_activate while holding the store lock. This
+         * means the init function will be called while holding a lock. Normally
+         * we try to avoid calling a user callback while holding a lock.
+         * However, fallbacks are never third party providers so we accept this.
+         */
+        if (provider_activate(prov, 0, 0) < 0) {
             ossl_provider_free(prov);
+            goto err;
         }
+        prov->store = store;
+        if (sk_OSSL_PROVIDER_push(store->providers, prov) == 0) {
+            ossl_provider_free(prov);
+            goto err;
+        }
+        activated_fallback_count++;
     }
 
-    /*
-     * We assume that all fallbacks have been added to the store before
-     * any fallback is activated.
-     */
-    if (activated_fallback_count > 0)
+    if (activated_fallback_count > 0) {
         store->use_fallbacks = 0;
-
+        ret = 1;
+    }
+ err:
     CRYPTO_THREAD_unlock(store->lock);
+    return ret;
 }
 
 int ossl_provider_doall_activated(OSSL_LIB_CTX *ctx,
@@ -1008,7 +1170,8 @@ int ossl_provider_doall_activated(OSSL_LIB_CTX *ctx,
 
     if (store == NULL)
         return 1;
-    provider_activate_fallbacks(store);
+    if (!provider_activate_fallbacks(store))
+        return 0;
 
     /*
      * Under lock, grab a copy of the provider list and up_ref each
@@ -1085,20 +1248,24 @@ int ossl_provider_doall_activated(OSSL_LIB_CTX *ctx,
     return ret;
 }
 
-int ossl_provider_available(OSSL_PROVIDER *prov)
+int OSSL_PROVIDER_available(OSSL_LIB_CTX *libctx, const char *name)
 {
-    int ret;
+    OSSL_PROVIDER *prov = NULL;
+    int available = 0;
+    struct provider_store_st *store = get_provider_store(libctx);
 
-    if (prov != NULL) {
-        provider_activate_fallbacks(prov->store);
+    if (store == NULL || !provider_activate_fallbacks(store))
+        return 0;
 
+    prov = ossl_provider_find(libctx, name, 0);
+    if (prov != NULL) {
         if (!CRYPTO_THREAD_read_lock(prov->flag_lock))
             return 0;
-        ret = prov->flag_activated;
+        available = prov->flag_activated;
         CRYPTO_THREAD_unlock(prov->flag_lock);
-        return ret;
+        ossl_provider_free(prov);
     }
-    return 0;
+    return available;
 }
 
 /* Setters of Provider Object data */
@@ -1323,46 +1490,6 @@ int ossl_provider_set_child(OSSL_PROVIDER *prov, const OSSL_CORE_HANDLE *handle)
     return 1;
 }
 
-int ossl_provider_convert_to_child(OSSL_PROVIDER *prov,
-                                   const OSSL_CORE_HANDLE *handle,
-                                   OSSL_provider_init_fn *init_function)
-{
-    int flush = 0;
-
-    if (!CRYPTO_THREAD_write_lock(prov->store->lock))
-        return 0;
-    if (!CRYPTO_THREAD_write_lock(prov->flag_lock)) {
-        CRYPTO_THREAD_unlock(prov->store->lock);
-        return 0;
-    }
-    /*
-     * The provider could be in one of three states: (1) Already a child,
-     * (2) Not a child (but eligible to be one), or (3) Not a child (not
-     * eligible to be one).
-     */
-    if (prov->flag_couldbechild) {
-        ossl_provider_set_child(prov, handle);
-        prov->init_function = init_function;
-    }
-    if (prov->ischild && provider_activate(prov, 0, 0)) {
-        flush = 1;
-        prov->store->use_fallbacks = 0;
-    }
-
-    CRYPTO_THREAD_unlock(prov->flag_lock);
-    CRYPTO_THREAD_unlock(prov->store->lock);
-
-    if (flush)
-        provider_flush_store_cache(prov);
-
-    /*
-     * We report success whether or not the provider was eligible for conversion
-     * to a child. If its not elgibile then it has already been loaded as a non
-     * child provider and we should keep it like that.
-     */
-    return 1;
-}
-
 int ossl_provider_default_props_update(OSSL_LIB_CTX *libctx, const char *props)
 {
 #ifndef FIPS_MODULE
@@ -1436,13 +1563,7 @@ static int ossl_provider_register_child_cb(const OSSL_CORE_HANDLE *handle,
     max = sk_OSSL_PROVIDER_num(store->providers);
     for (i = 0; i < max; i++) {
         prov = sk_OSSL_PROVIDER_value(store->providers, i);
-        /*
-         * We require register_child_cb to be called during a provider init
-         * function. The currently initing provider will never be activated yet
-         * and we we should not attempt to aquire the flag_lock for it.
-         */
-        if (prov == thisprov)
-            continue;
+
         if (!CRYPTO_THREAD_read_lock(prov->flag_lock))
             break;
         /*
diff --git a/crypto/provider_local.h b/crypto/provider_local.h
index 12baf032f1..e0bcbcb9f9 100644
--- a/crypto/provider_local.h
+++ b/crypto/provider_local.h
@@ -9,10 +9,25 @@
 
 #include <openssl/core.h>
 
-struct predefined_providers_st {
-    const char *name;
+typedef struct {
+    char *name;
+    char *value;
+} INFOPAIR;
+DEFINE_STACK_OF(INFOPAIR)
+
+typedef struct {
+    char *name;
+    char *path;
     OSSL_provider_init_fn *init;
+    STACK_OF(INFOPAIR) *parameters;
     unsigned int is_fallback:1;
-};
+} OSSL_PROVIDER_INFO;
+
+extern const OSSL_PROVIDER_INFO ossl_predefined_providers[];
 
-extern const struct predefined_providers_st ossl_predefined_providers[];
+void ossl_provider_info_clear(OSSL_PROVIDER_INFO *info);
+int ossl_provider_info_add_to_store(OSSL_LIB_CTX *libctx,
+                                    OSSL_PROVIDER_INFO *entry);
+int ossl_provider_info_add_parameter(OSSL_PROVIDER_INFO *provinfo,
+                                     const char *name,
+                                     const char *value);
diff --git a/crypto/provider_predefined.c b/crypto/provider_predefined.c
index 142a6d97d1..068e0b7cd9 100644
--- a/crypto/provider_predefined.c
+++ b/crypto/provider_predefined.c
@@ -17,16 +17,16 @@ OSSL_provider_init_fn ossl_fips_intern_provider_init;
 #ifdef STATIC_LEGACY
 OSSL_provider_init_fn ossl_legacy_provider_init;
 #endif
-const struct predefined_providers_st ossl_predefined_providers[] = {
+const OSSL_PROVIDER_INFO ossl_predefined_providers[] = {
 #ifdef FIPS_MODULE
-    { "fips", ossl_fips_intern_provider_init, 1 },
+    { "fips", NULL, ossl_fips_intern_provider_init, NULL, 1 },
 #else
-    { "default", ossl_default_provider_init, 1 },
+    { "default", NULL, ossl_default_provider_init, NULL, 1 },
 # ifdef STATIC_LEGACY
-    { "legacy", ossl_legacy_provider_init, 0 },
+    { "legacy", NULL, ossl_legacy_provider_init, NULL, 0 },
 # endif
-    { "base", ossl_base_provider_init, 0 },
-    { "null", ossl_null_provider_init, 0 },
+    { "base", NULL, ossl_base_provider_init, NULL, 0 },
+    { "null", NULL, ossl_null_provider_init, NULL, 0 },
 #endif
-    { NULL, NULL, 0 }
+    { NULL, NULL, NULL, NULL, 0 }
 };
diff --git a/doc/internal/man3/ossl_provider_new.pod b/doc/internal/man3/ossl_provider_new.pod
index ed2d6993b3..09b2e04117 100644
--- a/doc/internal/man3/ossl_provider_new.pod
+++ b/doc/internal/man3/ossl_provider_new.pod
@@ -9,7 +9,7 @@ ossl_provider_add_parameter, ossl_provider_set_child, ossl_provider_get_parent,
 ossl_provider_up_ref_parent, ossl_provider_free_parent,
 ossl_provider_default_props_update, ossl_provider_get0_dispatch,
 ossl_provider_init_as_child,
-ossl_provider_activate, ossl_provider_deactivate, ossl_provider_available,
+ossl_provider_activate, ossl_provider_deactivate, ossl_provider_add_to_store,
 ossl_provider_ctx,
 ossl_provider_doall_activated,
 ossl_provider_name, ossl_provider_dso,
@@ -53,11 +53,10 @@ ossl_provider_get_capabilities
   * Activate the Provider
   * If the Provider is a module, the module will be loaded
   */
- int ossl_provider_activate(OSSL_PROVIDER *prov, int retain_fallbacks,
-                            int upcalls);
+ int ossl_provider_activate(OSSL_PROVIDER *prov, int upcalls, int aschild);
  int ossl_provider_deactivate(OSSL_PROVIDER *prov);
- /* Check if provider is available (activated) */
- int ossl_provider_available(OSSL_PROVIDER *prov);
+ 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);
@@ -220,18 +219,24 @@ be located in that module, and called.
 
 =back
 
-If I<retain_fallbacks> is zero, fallbacks are disabled.  If it is nonzero,
-fallbacks are left unchanged. If I<upcalls> is nonzero then, if this is a child
-provider, upcalls to the parent libctx will be made to inform it of an
-up-ref.
+If I<upcalls> is nonzero then, if this is a child provider, upcalls to the
+parent libctx will be made to inform it of an up-ref. If I<aschild> is nonzero
+then the provider will only be activated if it is a child provider. Otherwise
+no action is taken and ossl_provider_activate() returns success.
 
 ossl_provider_deactivate() "deactivates" the provider for the given
 provider object I<prov> by decrementing its activation count.  When
 that count reaches zero, the activation flag is cleared.
 
-ossl_provider_available() activates all fallbacks if no provider is
-activated yet, then checks if given provider object I<prov> is
-activated.
+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. 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
@@ -345,11 +350,8 @@ called for any activated providers.
 
 ossl_provider_set_module_path(), ossl_provider_set_fallback(),
 ossl_provider_activate(), ossl_provider_activate_leave_fallbacks() and
-ossl_provider_deactivate(), ossl_provider_default_props_update() return 1 on
-success, or 0 on error.
-
-ossl_provider_available() return 1 if the provider is available,
-otherwise 0.
+ossl_provider_deactivate(), ossl_provider_add_to_store(),
+ossl_provider_default_props_update() 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/doc/man3/OSSL_LIB_CTX.pod b/doc/man3/OSSL_LIB_CTX.pod
index d51816ead7..98a3896fe4 100644
--- a/doc/man3/OSSL_LIB_CTX.pod
+++ b/doc/man3/OSSL_LIB_CTX.pod
@@ -75,19 +75,13 @@ context. If L<EVP_set_default_properties(3)> is called directly on a child
 library context then the new properties will override anything from the parent
 library context and mirroring of the properties will stop.
 
-OSSL_LIB_CTX_new_child() must only be called from within the scope of a
-provider's B<OSSL_provider_init> function (see L<provider-base(7)>). Calling it
-outside of that function may succeed but may not correctly mirror all providers
-and is considered undefined behaviour. When called from within the scope of a
-provider's B<OSSL_provider_init> function the currently initialising provider is
-not yet available in the application's library context and therefore will
-similarly not yet be available in the newly constructed child library context.
-As soon as the B<OSSL_provider_init> function returns then the new provider is
-available in the application's library context and will be similarly mirrored in
-the child library context. Since the current provider is still initialising
-the provider should not attempt to perform fetches, or call any function that
-performs a fetch using the child library context until after the initialisation
-function has completed.
+When OSSL_LIB_CTX_new_child() is called from within the scope of a provider's
+B<OSSL_provider_init> function the currently initialising provider is not yet
+available in the application's library context and therefore will similarly not
+yet be available in the newly constructed child library context. As soon as the
+B<OSSL_provider_init> function returns then the new provider is available in the
+application's library context and will be similarly mirrored in the child
+library context.
 
 OSSL_LIB_CTX_load_config() loads a configuration file using the given C<ctx>.
 This can be used to associate a library context with providers that are loaded
diff --git a/doc/man7/provider.pod b/doc/man7/provider.pod
index 797ef45553..7074a5cad1 100644
--- a/doc/man7/provider.pod
+++ b/doc/man7/provider.pod
@@ -69,6 +69,9 @@ the provider multiple simultaneous uses.
 This pointer will be passed to various operation functions offered by
 the provider.
 
+Note that the provider will not be made available for applications to use until
+the initialization function has completed and returned successfully.
+
 One of the functions the provider offers to the OpenSSL libraries is
 the central mechanism for the OpenSSL libraries to get access to
 operation implementations for diverse algorithms.
diff --git a/include/internal/provider.h b/include/internal/provider.h
index df20c76f90..237c852e8d 100644
--- a/include/internal/provider.h
+++ b/include/internal/provider.h
@@ -44,9 +44,6 @@ int ossl_provider_add_parameter(OSSL_PROVIDER *prov, const char *name,
 
 int ossl_provider_is_child(const OSSL_PROVIDER *prov);
 int ossl_provider_set_child(OSSL_PROVIDER *prov, const OSSL_CORE_HANDLE *handle);
-int ossl_provider_convert_to_child(OSSL_PROVIDER *prov,
-                                   const OSSL_CORE_HANDLE *handle,
-                                   OSSL_provider_init_fn *init_function);
 const OSSL_CORE_HANDLE *ossl_provider_get_parent(OSSL_PROVIDER *prov);
 int ossl_provider_up_ref_parent(OSSL_PROVIDER *prov, int activate);
 int ossl_provider_free_parent(OSSL_PROVIDER *prov, int deactivate);
@@ -59,11 +56,10 @@ int ossl_provider_disable_fallback_loading(OSSL_LIB_CTX *libctx);
  * Activate the Provider
  * If the Provider is a module, the module will be loaded
  */
-int ossl_provider_activate(OSSL_PROVIDER *prov, int retain_fallbacks,
-                           int upcalls);
+int ossl_provider_activate(OSSL_PROVIDER *prov, int upcalls, int aschild);
 int ossl_provider_deactivate(OSSL_PROVIDER *prov);
-/* Check if the provider is available (activated) */
-int ossl_provider_available(OSSL_PROVIDER *prov);
+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);
diff --git a/include/internal/symhacks.h b/include/internal/symhacks.h
index 564351642f..33bae51e49 100644
--- a/include/internal/symhacks.h
+++ b/include/internal/symhacks.h
@@ -15,9 +15,6 @@
 
 # if defined(OPENSSL_SYS_VMS)
 
-/* ossl_provider_available vs OSSL_PROVIDER_available */
-#  undef ossl_provider_available
-#  define ossl_provider_available                 ossl_int_prov_available
 /* ossl_provider_gettable_params vs OSSL_PROVIDER_gettable_params */
 #  undef ossl_provider_gettable_params
 #  define ossl_provider_gettable_params            ossl_int_prov_gettable_params
diff --git a/test/provfetchtest.c b/test/provfetchtest.c
index 8717a03bc1..ca154dd463 100644
--- a/test/provfetchtest.c
+++ b/test/provfetchtest.c
@@ -204,10 +204,18 @@ static int dummy_provider_init(const OSSL_CORE_HANDLE *handle,
                                void **provctx)
 {
     OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new_child(handle, in);
+    unsigned char buf[32];
 
     *provctx = (void *)libctx;
     *out = dummy_dispatch_table;
 
+    /*
+     * Do some work using the child libctx, to make sure this is possible from
+     * inside the init function.
+     */
+    if (!RAND_bytes_ex(libctx, buf, sizeof(buf), 0))
+        return 0;
+
     return 1;
 }
 
@@ -222,6 +230,7 @@ static int fetch_test(int tst)
 {
     OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new();
     OSSL_PROVIDER *dummyprov = NULL;
+    OSSL_PROVIDER *nullprov = NULL;
     OSSL_DECODER *decoder = NULL;
     OSSL_ENCODER *encoder = NULL;
     OSSL_STORE_LOADER *loader = NULL;
@@ -233,6 +242,7 @@ static int fetch_test(int tst)
 
     if (!TEST_true(OSSL_PROVIDER_add_builtin(libctx, "dummy-prov",
                                              dummy_provider_init))
+            || !TEST_ptr(nullprov = OSSL_PROVIDER_load(libctx, "default"))
             || !TEST_ptr(dummyprov = OSSL_PROVIDER_load(libctx, "dummy-prov")))
         goto err;
 
@@ -267,6 +277,7 @@ static int fetch_test(int tst)
     OSSL_ENCODER_free(encoder);
     OSSL_STORE_LOADER_free(loader);
     OSSL_PROVIDER_unload(dummyprov);
+    OSSL_PROVIDER_unload(nullprov);
     OSSL_LIB_CTX_free(libctx);
     return testresult;
 }
diff --git a/test/provider_internal_test.c b/test/provider_internal_test.c
index 7a37ef8c24..d9cc68d59d 100644
--- a/test/provider_internal_test.c
+++ b/test/provider_internal_test.c
@@ -26,7 +26,7 @@ static int test_provider(OSSL_PROVIDER *prov, const char *expected_greeting)
     int ret = 0;
 
     ret =
-        TEST_true(ossl_provider_activate(prov, 0, 1))
+        TEST_true(ossl_provider_activate(prov, 1, 0))
         && TEST_true(ossl_provider_get_params(prov, greeting_request))
         && TEST_ptr(greeting = greeting_request[0].data)
         && TEST_size_t_gt(greeting_request[0].data_size, 0)


More information about the openssl-commits mailing list