[openssl] master update

Richard Levitte levitte at openssl.org
Thu Dec 17 11:03:46 UTC 2020


The branch master has been updated
       via  74cd923a78b490d46af9c3dc5c8dc7a741c5e576 (commit)
       via  390f9bad69ce19f601abf131ceabf90aedc0d3d5 (commit)
       via  6963979f5c0f95b2152ef74645faa7344e33284d (commit)
       via  e77c13f8b73ff937819d6551ddd616fe01b989d0 (commit)
       via  054cde175664f3e7c8fe5f753c0a5cb5be75dccc (commit)
      from  4159ebca3cb3d9586d6709c7a0166a4af5676f91 (commit)


- Log -----------------------------------------------------------------
commit 74cd923a78b490d46af9c3dc5c8dc7a741c5e576
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Dec 16 17:01:06 2020 +0100

    EVP: Fix memory leak in EVP_PKEY_CTX_dup()
    
    In most error cases, EVP_PKEY_CTX_dup() would only free the EVP_PKEY_CTX
    without freeing the duplicated contents.
    
    Fixes #13503
    
    Reviewed-by: Tomas Mraz <tmraz at fedoraproject.org>
    (Merged from https://github.com/openssl/openssl/pull/13661)

commit 390f9bad69ce19f601abf131ceabf90aedc0d3d5
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Dec 16 15:15:06 2020 +0100

    CORE: Separate OSSL_PROVIDER activation from OSSL_PROVIDER reference
    
    This introduces a separate activation counter, and the function
    ossl_provider_deactivate() for provider deactivation.
    
    Something to be noted is that if the reference count goes down to
    zero, we don't care if the activation count is non-zero (i.e. someone
    forgot to call ossl_provider_deactivate()).  Since there are no more
    references to the provider, it doesn't matter.
    The important thing is that deactivation doesn't remove the provider
    as long as there are references to it, for example because there are
    live methods associated with that provider, but still makes the
    provider unavailable to create new methods from.
    
    Fixes #13503
    Fixes #12157
    
    Reviewed-by: Tomas Mraz <tmraz at fedoraproject.org>
    (Merged from https://github.com/openssl/openssl/pull/13661)

commit 6963979f5c0f95b2152ef74645faa7344e33284d
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Dec 11 11:01:09 2020 +0100

    DECODER: Adjust the library context of keys in our decoders
    
    Because decoders are coupled with keymgmts from the same provider,
    ours need to produce provider side keys the same way.  Since our
    keymgmts create key data with the provider library context, so must
    our decoders.
    
    We solve with functions to adjust the library context of decoded keys,
    and use them.
    
    Reviewed-by: Tomas Mraz <tmraz at fedoraproject.org>
    (Merged from https://github.com/openssl/openssl/pull/13661)

commit e77c13f8b73ff937819d6551ddd616fe01b989d0
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Dec 10 14:00:05 2020 +0100

    MSBLOB & PVK: Make it possible to write EVP_PKEYs with provided internal key
    
    So far, the MSBLOB and PVK writers could only handle EVP_PKEYs with
    legacy internal keys.
    
    Specially to be able to compile the loader_attic engine, we use the C
    macro OPENSSL_NO_PROVIDER_CODE to avoid building the provider specific
    things when we don't need them.  The alternative is to suck half of
    crypto/evp/ into loader_attic, and that's just not feasible.
    
    Fixes #13503
    
    Reviewed-by: Tomas Mraz <tmraz at fedoraproject.org>
    (Merged from https://github.com/openssl/openssl/pull/13661)

commit 054cde175664f3e7c8fe5f753c0a5cb5be75dccc
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Dec 10 18:33:16 2020 +0100

    DECODER EVP_PKEY: Don't store all the EVP_KEYMGMTs
    
    OSSL_DECODER_CTX_new_by_EVP_PKEY() would keep copies of all the
    EVP_KEYMGMTs it finds.
    This turns out to be fragile in certain circumstances, so we switch to
    fetch the appropriate EVP_KEYMGMT when it's time to construct an
    EVP_PKEY from the decoded data instead.  This has the added benefit
    that we now actually use the property query string that was given by
    the caller for these fetches.
    
    Fixes #13503
    
    Reviewed-by: Tomas Mraz <tmraz at fedoraproject.org>
    (Merged from https://github.com/openssl/openssl/pull/13661)

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

Summary of changes:
 crypto/dh/dh_lib.c                                 |   5 +
 crypto/dsa/dsa_lib.c                               |   5 +
 crypto/ec/ec_key.c                                 |   6 +
 crypto/ec/ecx_key.c                                |   5 +
 crypto/encode_decode/decoder_pkey.c                | 234 +++++++++------------
 crypto/evp/pmeth_lib.c                             |  46 ++--
 crypto/pem/pvkfmt.c                                |  33 ++-
 crypto/provider.c                                  |   2 +
 crypto/provider_core.c                             | 130 +++++++-----
 crypto/rsa/rsa_lib.c                               |   5 +
 doc/internal/man3/ossl_provider_new.pod            |  38 ++--
 engines/build.info                                 |   1 +
 include/crypto/dh.h                                |   1 +
 include/crypto/dsa.h                               |   1 +
 include/crypto/ec.h                                |   1 +
 include/crypto/ecx.h                               |   1 +
 include/crypto/rsa.h                               |   1 +
 include/internal/provider.h                        |   4 +-
 .../implementations/encode_decode/decode_der2key.c |  60 ++++++
 test/provider_internal_test.c                      |   3 +-
 20 files changed, 343 insertions(+), 239 deletions(-)

diff --git a/crypto/dh/dh_lib.c b/crypto/dh/dh_lib.c
index e687b04259..e8a66878ab 100644
--- a/crypto/dh/dh_lib.c
+++ b/crypto/dh/dh_lib.c
@@ -168,6 +168,11 @@ int DH_up_ref(DH *r)
     return ((i > 1) ? 1 : 0);
 }
 
+void ossl_dh_set0_libctx(DH *d, OSSL_LIB_CTX *libctx)
+{
+    d->libctx = libctx;
+}
+
 #ifndef FIPS_MODULE
 int DH_set_ex_data(DH *d, int idx, void *arg)
 {
diff --git a/crypto/dsa/dsa_lib.c b/crypto/dsa/dsa_lib.c
index 4a9f572edd..df9dd73dfd 100644
--- a/crypto/dsa/dsa_lib.c
+++ b/crypto/dsa/dsa_lib.c
@@ -247,6 +247,11 @@ int DSA_up_ref(DSA *r)
     return ((i > 1) ? 1 : 0);
 }
 
+void ossl_dsa_set0_libctx(DSA *d, OSSL_LIB_CTX *libctx)
+{
+    d->libctx = libctx;
+}
+
 void DSA_get0_pqg(const DSA *d,
                   const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
 {
diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c
index da3d6f04a2..d03c75e8aa 100644
--- a/crypto/ec/ec_key.c
+++ b/crypto/ec/ec_key.c
@@ -659,6 +659,12 @@ const char *ec_key_get0_propq(const EC_KEY *key)
     return key->propq;
 }
 
+void ec_key_set0_libctx(EC_KEY *key, OSSL_LIB_CTX *libctx)
+{
+    key->libctx = libctx;
+    /* Do we need to propagate this to the group? */
+}
+
 const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key)
 {
     return key->group;
diff --git a/crypto/ec/ecx_key.c b/crypto/ec/ecx_key.c
index db74a40c97..2b9386d522 100644
--- a/crypto/ec/ecx_key.c
+++ b/crypto/ec/ecx_key.c
@@ -73,6 +73,11 @@ void ecx_key_free(ECX_KEY *key)
     OPENSSL_free(key);
 }
 
+void ecx_key_set0_libctx(ECX_KEY *key, OSSL_LIB_CTX *libctx)
+{
+    key->libctx = libctx;
+}
+
 int ecx_key_up_ref(ECX_KEY *key)
 {
     int i;
diff --git a/crypto/encode_decode/decoder_pkey.c b/crypto/encode_decode/decoder_pkey.c
index 016d6047bd..c515cb6d44 100644
--- a/crypto/encode_decode/decoder_pkey.c
+++ b/crypto/encode_decode/decoder_pkey.c
@@ -55,9 +55,11 @@ int OSSL_DECODER_CTX_set_passphrase_cb(OSSL_DECODER_CTX *ctx,
 DEFINE_STACK_OF(EVP_KEYMGMT)
 
 struct decoder_EVP_PKEY_data_st {
+    OSSL_LIB_CTX *libctx;
+    char *propq;
+
     char *object_type;           /* recorded object data type, may be NULL */
     void **object;               /* Where the result should end up */
-    STACK_OF(EVP_KEYMGMT) *keymgmts; /* The EVP_KEYMGMTs we handle */
 };
 
 static int decoder_construct_EVP_PKEY(OSSL_DECODER_INSTANCE *decoder_inst,
@@ -67,7 +69,7 @@ static int decoder_construct_EVP_PKEY(OSSL_DECODER_INSTANCE *decoder_inst,
     struct decoder_EVP_PKEY_data_st *data = construct_data;
     OSSL_DECODER *decoder = OSSL_DECODER_INSTANCE_get_decoder(decoder_inst);
     void *decoderctx = OSSL_DECODER_INSTANCE_get_decoder_ctx(decoder_inst);
-    size_t i, end_i;
+    EVP_KEYMGMT *keymgmt = NULL;
     /*
      * |object_ref| points to a provider reference to an object, its exact
      * contents entirely opaque to us, but may be passed to any provider
@@ -101,75 +103,54 @@ static int decoder_construct_EVP_PKEY(OSSL_DECODER_INSTANCE *decoder_inst,
     object_ref = p->data;
     object_ref_sz = p->data_size;
 
-    /* We may have reached one of the goals, let's find out! */
-    end_i = sk_EVP_KEYMGMT_num(data->keymgmts);
-    for (i = 0; end_i; i++) {
-        EVP_KEYMGMT *keymgmt = sk_EVP_KEYMGMT_value(data->keymgmts, i);
+    keymgmt = EVP_KEYMGMT_fetch(data->libctx, data->object_type, data->propq);
+
+    if (keymgmt != NULL) {
+        EVP_PKEY *pkey = NULL;
+        void *keydata = NULL;
+        const OSSL_PROVIDER *keymgmt_prov = EVP_KEYMGMT_provider(keymgmt);
+        const OSSL_PROVIDER *decoder_prov = OSSL_DECODER_provider(decoder);
 
         /*
-         * There are two ways to find a matching KEYMGMT:
-         *
-         * 1.  If the object data type (recorded in |data->object_type|)
-         *     is defined, by checking it using EVP_KEYMGMT_is_a().
-         * 2.  If the object data type is NOT defined, by comparing the
-         *     EVP_KEYMGMT and OSSL_DECODER method numbers.  Since
-         *     EVP_KEYMGMT and OSSL_DECODE operate with the same
-         *     namemap, we know that the method numbers must match.
+         * If the EVP_KEYMGMT and the OSSL_DECODER are from the
+         * same provider, we assume that the KEYMGMT has a key loading
+         * function that can handle the provider reference we hold.
          *
-         * This allows individual decoders to specify variants of keys,
-         * such as a DER to RSA decoder finding a RSA-PSS key, without
-         * having to decode the exact same DER blob into the exact same
-         * internal structure twice.  This is, of course, entirely at the
-         * discretion of the decoder implementations.
+         * Otherwise, we export from the decoder and import the
+         * result in the keymgmt.
          */
-        if (data->object_type != NULL
-            ? EVP_KEYMGMT_is_a(keymgmt, data->object_type)
-            : EVP_KEYMGMT_number(keymgmt) == OSSL_DECODER_number(decoder)) {
-            EVP_PKEY *pkey = NULL;
-            void *keydata = NULL;
-            const OSSL_PROVIDER *keymgmt_prov =
-                EVP_KEYMGMT_provider(keymgmt);
-            const OSSL_PROVIDER *decoder_prov =
-                OSSL_DECODER_provider(decoder);
+        if (keymgmt_prov == decoder_prov) {
+            keydata = evp_keymgmt_load(keymgmt, object_ref, object_ref_sz);
+        } else {
+            struct evp_keymgmt_util_try_import_data_st import_data;
+
+            import_data.keymgmt = keymgmt;
+            import_data.keydata = NULL;
+            import_data.selection = OSSL_KEYMGMT_SELECT_ALL;
 
             /*
-             * If the EVP_KEYMGMT and the OSSL_DECODER are from the
-             * same provider, we assume that the KEYMGMT has a key loading
-             * function that can handle the provider reference we hold.
-             *
-             * Otherwise, we export from the decoder and import the
-             * result in the keymgmt.
+             * No need to check for errors here, the value of
+             * |import_data.keydata| is as much an indicator.
              */
-            if (keymgmt_prov == decoder_prov) {
-                keydata = evp_keymgmt_load(keymgmt, object_ref, object_ref_sz);
-            } else {
-                struct evp_keymgmt_util_try_import_data_st import_data;
-
-                import_data.keymgmt = keymgmt;
-                import_data.keydata = NULL;
-                import_data.selection = OSSL_KEYMGMT_SELECT_ALL;
-
-                /*
-                 * No need to check for errors here, the value of
-                 * |import_data.keydata| is as much an indicator.
-                 */
-                (void)decoder->export_object(decoderctx,
-                                             object_ref, object_ref_sz,
-                                             &evp_keymgmt_util_try_import,
-                                             &import_data);
-                keydata = import_data.keydata;
-                import_data.keydata = NULL;
-            }
-
-            if (keydata != NULL
-                && (pkey =
-                    evp_keymgmt_util_make_pkey(keymgmt, keydata)) == NULL)
-                evp_keymgmt_freedata(keymgmt, keydata);
-
-            *data->object = pkey;
-
-            break;
+            (void)decoder->export_object(decoderctx,
+                                         object_ref, object_ref_sz,
+                                         &evp_keymgmt_util_try_import,
+                                         &import_data);
+            keydata = import_data.keydata;
+            import_data.keydata = NULL;
         }
+
+        if (keydata != NULL
+            && (pkey = evp_keymgmt_util_make_pkey(keymgmt, keydata)) == NULL)
+            evp_keymgmt_freedata(keymgmt, keydata);
+
+        *data->object = pkey;
+
+        /*
+         * evp_keymgmt_util_make_pkey() increments the reference count when
+         * assigning the EVP_PKEY, so we can free the keymgmt here.
+         */
+        EVP_KEYMGMT_free(keymgmt);
     }
     /*
      * We successfully looked through, |*ctx->object| determines if we
@@ -183,63 +164,37 @@ static void decoder_clean_EVP_PKEY_construct_arg(void *construct_data)
     struct decoder_EVP_PKEY_data_st *data = construct_data;
 
     if (data != NULL) {
-        sk_EVP_KEYMGMT_pop_free(data->keymgmts, EVP_KEYMGMT_free);
+        OPENSSL_free(data->propq);
         OPENSSL_free(data->object_type);
         OPENSSL_free(data);
     }
 }
 
-struct collected_data_st {
-    struct decoder_EVP_PKEY_data_st *process_data;
-    const char *keytype;
-    STACK_OF(OPENSSL_CSTRING) *names;
-    OSSL_DECODER_CTX *ctx;
+static void collect_name(const char *name, void *arg)
+{
+    STACK_OF(OPENSSL_CSTRING) *names = arg;
 
-    unsigned int error_occured:1;
-};
+    sk_OPENSSL_CSTRING_push(names, name);
+}
 
 static void collect_keymgmt(EVP_KEYMGMT *keymgmt, void *arg)
 {
-    struct collected_data_st *data = arg;
-
-    if (data->keytype != NULL && !EVP_KEYMGMT_is_a(keymgmt, data->keytype))
-        return;
-    if (data->error_occured)
-        return;
-
-    data->error_occured = 1;         /* Assume the worst */
+    STACK_OF(EVP_KEYMGMT) *keymgmts = arg;
 
     if (!EVP_KEYMGMT_up_ref(keymgmt) /* ref++ */)
         return;
-    if (sk_EVP_KEYMGMT_push(data->process_data->keymgmts, keymgmt) <= 0) {
+    if (sk_EVP_KEYMGMT_push(keymgmts, keymgmt) <= 0) {
         EVP_KEYMGMT_free(keymgmt);   /* ref-- */
         return;
     }
-
-    data->error_occured = 0;         /* All is good now */
-}
-
-static void collect_name(const char *name, void *arg)
-{
-    struct collected_data_st *data = arg;
-
-    if (data->error_occured)
-        return;
-
-    data->error_occured = 1;         /* Assume the worst */
-
-    if (sk_OPENSSL_CSTRING_push(data->names, name) <= 0)
-        return;
-
-    data->error_occured = 0;         /* All is good now */
 }
 
 /*
  * The input structure check is only done on the initial decoder
  * implementations.
  */
-static int collect_decoder_check_input_structure(OSSL_DECODER_CTX *ctx,
-                                                 OSSL_DECODER_INSTANCE *di)
+static int decoder_check_input_structure(OSSL_DECODER_CTX *ctx,
+                                         OSSL_DECODER_INSTANCE *di)
 {
     int di_is_was_set = 0;
     const char *di_is =
@@ -255,15 +210,21 @@ static int collect_decoder_check_input_structure(OSSL_DECODER_CTX *ctx,
      * If the caller did give an input structure name, the decoder must have
      * a matching input structure to be accepted.
      */
-    if (di_is != NULL
-        && strcasecmp(ctx->input_structure, di_is) == 0)
+    if (di_is != NULL && strcasecmp(ctx->input_structure, di_is) == 0)
         return 1;
     return 0;
 }
 
+struct collect_decoder_data_st {
+    STACK_OF(OPENSSL_CSTRING) *names;
+    OSSL_DECODER_CTX *ctx;
+
+    unsigned int error_occured:1;
+};
+
 static void collect_decoder(OSSL_DECODER *decoder, void *arg)
 {
-    struct collected_data_st *data = arg;
+    struct collect_decoder_data_st *data = arg;
     size_t i, end_i;
     const OSSL_PROVIDER *prov = OSSL_DECODER_provider(decoder);
     void *provctx = OSSL_PROVIDER_get0_provider_ctx(prov);
@@ -295,7 +256,7 @@ static void collect_decoder(OSSL_DECODER *decoder, void *arg)
             /* If successful so far, don't free these directly */
             decoderctx = NULL;
 
-            if (collect_decoder_check_input_structure(data->ctx, di)
+            if (decoder_check_input_structure(data->ctx, di)
                 && ossl_decoder_ctx_add_decoder_inst(data->ctx, di))
                 di = NULL;      /* If successfully added, don't free it */
         }
@@ -313,66 +274,71 @@ int ossl_decoder_ctx_setup_for_EVP_PKEY(OSSL_DECODER_CTX *ctx,
                                         OSSL_LIB_CTX *libctx,
                                         const char *propquery)
 {
-    struct collected_data_st *data = NULL;
-    size_t i, end_i;
+    struct decoder_EVP_PKEY_data_st *process_data = NULL;
+    STACK_OF(EVP_KEYMGMT) *keymgmts = NULL;
+    STACK_OF(OPENSSL_CSTRING) *names = NULL;
     int ok = 0;
 
-    if ((data = OPENSSL_zalloc(sizeof(*data))) == NULL
-        || (data->process_data =
-            OPENSSL_zalloc(sizeof(*data->process_data))) == NULL
-        || (data->process_data->keymgmts = sk_EVP_KEYMGMT_new_null()) == NULL
-        || (data->names = sk_OPENSSL_CSTRING_new_null()) == NULL) {
+    if ((process_data = OPENSSL_zalloc(sizeof(*process_data))) == NULL
+        || (propquery != NULL
+            && (process_data->propq = OPENSSL_strdup(propquery)) == NULL)
+        || (keymgmts = sk_EVP_KEYMGMT_new_null()) == NULL
+        || (names = sk_OPENSSL_CSTRING_new_null()) == NULL) {
         ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_MALLOC_FAILURE);
         goto err;
     }
-    data->process_data->object = (void **)pkey;
-    data->ctx = ctx;
-    data->keytype = keytype;
 
-    /* First, find all keymgmts to form goals */
-    EVP_KEYMGMT_do_all_provided(libctx, collect_keymgmt, data);
+    process_data->object = (void **)pkey;
+    process_data->libctx = libctx;
 
-    if (data->error_occured)
-        goto err;
+    /* First, find all keymgmts to form goals */
+    EVP_KEYMGMT_do_all_provided(libctx, collect_keymgmt, keymgmts);
 
     /* Then, we collect all the keymgmt names */
-    end_i = sk_EVP_KEYMGMT_num(data->process_data->keymgmts);
-    for (i = 0; i < end_i; i++) {
-        EVP_KEYMGMT *keymgmt =
-            sk_EVP_KEYMGMT_value(data->process_data->keymgmts, i);
+    while (sk_EVP_KEYMGMT_num(keymgmts) > 0) {
+        EVP_KEYMGMT *keymgmt = sk_EVP_KEYMGMT_shift(keymgmts);
 
-        EVP_KEYMGMT_names_do_all(keymgmt, collect_name, data);
+        /*
+         * If the key type is given by the caller, we only use the matching
+         * KEYMGMTs, otherwise we use them all.
+         */
+        if (keytype == NULL || EVP_KEYMGMT_is_a(keymgmt, keytype))
+            EVP_KEYMGMT_names_do_all(keymgmt, collect_name, names);
 
-        if (data->error_occured)
-            goto err;
+        EVP_KEYMGMT_free(keymgmt);
     }
+    sk_EVP_KEYMGMT_free(keymgmts);
 
     /*
      * Finally, find all decoders that have any keymgmt of the collected
      * keymgmt names
      */
-    OSSL_DECODER_do_all_provided(libctx, collect_decoder, data);
+    {
+        struct collect_decoder_data_st collect_decoder_data = { NULL, };
 
-    if (data->error_occured)
-        goto err;
+        collect_decoder_data.names = names;
+        collect_decoder_data.ctx = ctx;
+        OSSL_DECODER_do_all_provided(libctx,
+                                     collect_decoder, &collect_decoder_data);
+        sk_OPENSSL_CSTRING_free(names);
+
+        if (collect_decoder_data.error_occured)
+            goto err;
+    }
 
     if (OSSL_DECODER_CTX_get_num_decoders(ctx) != 0) {
         if (!OSSL_DECODER_CTX_set_construct(ctx, decoder_construct_EVP_PKEY)
-            || !OSSL_DECODER_CTX_set_construct_data(ctx, data->process_data)
+            || !OSSL_DECODER_CTX_set_construct_data(ctx, process_data)
             || !OSSL_DECODER_CTX_set_cleanup(ctx,
                                              decoder_clean_EVP_PKEY_construct_arg))
             goto err;
 
-        data->process_data = NULL; /* Avoid it being freed */
+        process_data = NULL; /* Avoid it being freed */
     }
 
     ok = 1;
  err:
-    if (data != NULL) {
-        decoder_clean_EVP_PKEY_construct_arg(data->process_data);
-        sk_OPENSSL_CSTRING_free(data->names);
-        OPENSSL_free(data);
-    }
+    decoder_clean_EVP_PKEY_construct_arg(process_data);
     return ok;
 }
 
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index f817173555..8fc309dc99 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -455,12 +455,6 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *pctx)
 {
     EVP_PKEY_CTX *rctx;
 
-    if (((pctx->pmeth == NULL) || (pctx->pmeth->copy == NULL))
-            && ((EVP_PKEY_CTX_IS_DERIVE_OP(pctx)
-                 && pctx->op.kex.exchprovctx == NULL)
-                || (EVP_PKEY_CTX_IS_SIGNATURE_OP(pctx)
-                    && pctx->op.sig.sigprovctx == NULL)))
-        return NULL;
 # ifndef OPENSSL_NO_ENGINE
     /* Make sure it's safe to copy a pkey context using an ENGINE */
     if (pctx->engine && !ENGINE_init(pctx->engine)) {
@@ -483,10 +477,8 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *pctx)
     rctx->propquery = NULL;
     if (pctx->propquery != NULL) {
         rctx->propquery = OPENSSL_strdup(pctx->propquery);
-        if (rctx->propquery == NULL) {
-            OPENSSL_free(rctx);
-            return NULL;
-        }
+        if (rctx->propquery == NULL)
+            goto err;
     }
     rctx->legacy_keytype = pctx->legacy_keytype;
 
@@ -494,16 +486,16 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *pctx)
         if (pctx->op.kex.exchange != NULL) {
             rctx->op.kex.exchange = pctx->op.kex.exchange;
             if (!EVP_KEYEXCH_up_ref(rctx->op.kex.exchange))
-                goto end;
+                goto err;
         }
         if (pctx->op.kex.exchprovctx != NULL) {
             if (!ossl_assert(pctx->op.kex.exchange != NULL))
-                goto end;
+                goto err;
             rctx->op.kex.exchprovctx
                 = pctx->op.kex.exchange->dupctx(pctx->op.kex.exchprovctx);
             if (rctx->op.kex.exchprovctx == NULL) {
                 EVP_KEYEXCH_free(rctx->op.kex.exchange);
-                goto end;
+                goto err;
             }
             return rctx;
         }
@@ -511,16 +503,16 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *pctx)
         if (pctx->op.sig.signature != NULL) {
             rctx->op.sig.signature = pctx->op.sig.signature;
             if (!EVP_SIGNATURE_up_ref(rctx->op.sig.signature))
-                goto end;
+                goto err;
         }
         if (pctx->op.sig.sigprovctx != NULL) {
             if (!ossl_assert(pctx->op.sig.signature != NULL))
-                goto end;
+                goto err;
             rctx->op.sig.sigprovctx
                 = pctx->op.sig.signature->dupctx(pctx->op.sig.sigprovctx);
             if (rctx->op.sig.sigprovctx == NULL) {
                 EVP_SIGNATURE_free(rctx->op.sig.signature);
-                goto end;
+                goto err;
             }
             return rctx;
         }
@@ -528,16 +520,16 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *pctx)
         if (pctx->op.ciph.cipher != NULL) {
             rctx->op.ciph.cipher = pctx->op.ciph.cipher;
             if (!EVP_ASYM_CIPHER_up_ref(rctx->op.ciph.cipher))
-                goto end;
+                goto err;
         }
         if (pctx->op.ciph.ciphprovctx != NULL) {
             if (!ossl_assert(pctx->op.ciph.cipher != NULL))
-                goto end;
+                goto err;
             rctx->op.ciph.ciphprovctx
                 = pctx->op.ciph.cipher->dupctx(pctx->op.ciph.ciphprovctx);
             if (rctx->op.ciph.ciphprovctx == NULL) {
                 EVP_ASYM_CIPHER_free(rctx->op.ciph.cipher);
-                goto end;
+                goto err;
             }
             return rctx;
         }
@@ -545,22 +537,22 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *pctx)
         if (pctx->op.encap.kem != NULL) {
             rctx->op.encap.kem = pctx->op.encap.kem;
             if (!EVP_KEM_up_ref(rctx->op.encap.kem))
-                goto end;
+                goto err;
         }
         if (pctx->op.encap.kemprovctx != NULL) {
             if (!ossl_assert(pctx->op.encap.kem != NULL))
-                goto end;
+                goto err;
             rctx->op.encap.kemprovctx
                 = pctx->op.encap.kem->dupctx(pctx->op.encap.kemprovctx);
             if (rctx->op.encap.kemprovctx == NULL) {
                 EVP_KEM_free(rctx->op.encap.kem);
-                goto end;
+                goto err;
             }
             return rctx;
         }
     } else if (EVP_PKEY_CTX_IS_GEN_OP(pctx)) {
         /* Not supported - This would need a gen_dupctx() to work */
-        goto end;
+        goto err;
     }
 
     rctx->pmeth = pctx->pmeth;
@@ -587,17 +579,13 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *pctx)
             rctx->keymgmt = tmp_keymgmt;
             return rctx;
         }
-        goto err;
-    }
-    if (pctx->pmeth->copy(rctx, pctx) > 0)
+    } else if (pctx->pmeth->copy(rctx, pctx) > 0) {
         return rctx;
+    }
 err:
     rctx->pmeth = NULL;
     EVP_PKEY_CTX_free(rctx);
     return NULL;
-end:
-    OPENSSL_free(rctx);
-    return NULL;
 }
 
 int EVP_PKEY_meth_add0(const EVP_PKEY_METHOD *pmeth)
diff --git a/crypto/pem/pvkfmt.c b/crypto/pem/pvkfmt.c
index b0aa76b3f5..de673be005 100644
--- a/crypto/pem/pvkfmt.c
+++ b/crypto/pem/pvkfmt.c
@@ -18,13 +18,14 @@
  */
 #include "internal/deprecated.h"
 
-#include "internal/cryptlib.h"
 #include <openssl/pem.h>
-#include "crypto/pem.h"
 #include <openssl/rand.h>
 #include <openssl/bn.h>
 #include <openssl/dsa.h>
 #include <openssl/rsa.h>
+#include "internal/cryptlib.h"
+#include "crypto/pem.h"
+#include "crypto/evp.h"
 
 /*
  * Utility function: read a DWORD (4 byte unsigned integer) in little endian
@@ -461,9 +462,19 @@ static int do_i2b(unsigned char **out, const EVP_PKEY *pk, int ispub)
 {
     unsigned char *p;
     unsigned int bitlen = 0, magic = 0, keyalg = 0;
-    int outlen, noinc = 0;
-    int pktype = EVP_PKEY_id(pk);
+    int outlen = -1, noinc = 0;
+    int pktype;
+#ifndef OPENSSL_NO_PROVIDER_CODE
+    EVP_PKEY *pkcopy = NULL;
+
+    if (evp_pkey_is_provided(pk)) {
+        if (!evp_pkey_copy_downgraded(&pkcopy, pk))
+            goto end;
+        pk = pkcopy;
+    }
+#endif
 
+    pktype = EVP_PKEY_id(pk);
     if (pktype == EVP_PKEY_RSA) {
         bitlen = check_bitlen_rsa(EVP_PKEY_get0_RSA(pk), ispub, &magic);
         keyalg = MS_KEYALG_RSA_KEYX;
@@ -473,18 +484,20 @@ static int do_i2b(unsigned char **out, const EVP_PKEY *pk, int ispub)
         keyalg = MS_KEYALG_DSS_SIGN;
 #endif
     }
-    if (bitlen == 0)
-        return -1;
+    if (bitlen == 0) {
+        goto end;
+    }
     outlen = 16 + blob_length(bitlen,
                               keyalg == MS_KEYALG_DSS_SIGN ? 1 : 0, ispub);
     if (out == NULL)
-        return outlen;
+        goto end;
     if (*out)
         p = *out;
     else {
         if ((p = OPENSSL_malloc(outlen)) == NULL) {
             ERR_raise(ERR_LIB_PEM, ERR_R_MALLOC_FAILURE);
-            return -1;
+            outlen = -1;
+            goto end;
         }
         *out = p;
         noinc = 1;
@@ -507,6 +520,10 @@ static int do_i2b(unsigned char **out, const EVP_PKEY *pk, int ispub)
 #endif
     if (!noinc)
         *out += outlen;
+ end:
+#ifndef OPENSSL_NO_PROVIDER_CODE
+    EVP_PKEY_free(pkcopy);
+#endif
     return outlen;
 }
 
diff --git a/crypto/provider.c b/crypto/provider.c
index 0441fb2f2a..bd8f75a2c1 100644
--- a/crypto/provider.c
+++ b/crypto/provider.c
@@ -40,6 +40,8 @@ OSSL_PROVIDER *OSSL_PROVIDER_load(OSSL_LIB_CTX *libctx, const char *name)
 
 int OSSL_PROVIDER_unload(OSSL_PROVIDER *prov)
 {
+    if (!ossl_provider_deactivate(prov))
+        return 0;
     ossl_provider_free(prov);
     return 1;
 }
diff --git a/crypto/provider_core.c b/crypto/provider_core.c
index 954befd4a2..f0d6fb20f8 100644
--- a/crypto/provider_core.c
+++ b/crypto/provider_core.c
@@ -44,12 +44,15 @@ struct provider_store_st;        /* Forward declaration */
 struct ossl_provider_st {
     /* Flag bits */
     unsigned int flag_initialized:1;
+    unsigned int flag_activated:1;
     unsigned int flag_fallback:1; /* Can be used as fallback */
     unsigned int flag_activated_as_fallback:1;
 
     /* OpenSSL library side data */
     CRYPTO_REF_COUNT refcnt;
     CRYPTO_RWLOCK *refcnt_lock;  /* For the ref counter */
+    CRYPTO_REF_COUNT activatecnt;
+    CRYPTO_RWLOCK *activatecnt_lock; /* For the activate counter */
     char *name;
     char *path;
     DSO *module;
@@ -110,20 +113,15 @@ struct provider_store_st {
 };
 
 /*
- * provider_deactivate_free() is a wrapper around ossl_provider_free()
- * that also makes sure that activated fallback providers are deactivated.
- * This is simply done by freeing them an extra time, to compensate for the
- * refcount that provider_activate_fallbacks() gives them.
+ * provider_deactivate_free() is a wrapper around ossl_provider_deactivate()
+ * and ossl_provider_free(), called as needed.
  * Since this is only called when the provider store is being emptied, we
  * don't need to care about any lock.
  */
 static void provider_deactivate_free(OSSL_PROVIDER *prov)
 {
-    int extra_free = (prov->flag_initialized
-                      && prov->flag_activated_as_fallback);
-
-    if (extra_free)
-        ossl_provider_free(prov);
+    if (prov->flag_activated)
+        ossl_provider_deactivate(prov);
     ossl_provider_free(prov);
 }
 
@@ -251,6 +249,7 @@ static OSSL_PROVIDER *provider_new(const char *name,
     if ((prov = OPENSSL_zalloc(sizeof(*prov))) == NULL
 #ifndef HAVE_ATOMICS
         || (prov->refcnt_lock = CRYPTO_THREAD_lock_new()) == NULL
+        || (prov->activatecnt_lock = CRYPTO_THREAD_lock_new()) == NULL
 #endif
         || !ossl_provider_up_ref(prov) /* +1 One reference to be returned */
         || (prov->name = OPENSSL_strdup(name)) == NULL) {
@@ -337,38 +336,35 @@ void ossl_provider_free(OSSL_PROVIDER *prov)
         CRYPTO_DOWN_REF(&prov->refcnt, &ref, prov->refcnt_lock);
 
         /*
-         * 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.
+         * When the refcount drops to zero, we clean up the provider.
+         * Note that this also does teardown, which may seem late,
+         * considering that init happens on first activation.  However,
+         * there may be other structures hanging on to the provider after
+         * the last deactivation and may therefore need full access to the
+         * provider's services.  Therefore, we deinit late.
          */
-        if (ref < 2 && prov->flag_initialized) {
+        if (ref == 0) {
+            if (prov->flag_initialized) {
 #ifndef FIPS_MODULE
-            ossl_init_thread_deregister(prov);
+                ossl_init_thread_deregister(prov);
 #endif
-            if (prov->teardown != NULL)
-                prov->teardown(prov->provctx);
+                if (prov->teardown != NULL)
+                    prov->teardown(prov->provctx);
 #ifndef OPENSSL_NO_ERR
 # ifndef FIPS_MODULE
-            if (prov->error_strings != NULL) {
-                ERR_unload_strings(prov->error_lib, prov->error_strings);
-                OPENSSL_free(prov->error_strings);
-                prov->error_strings = NULL;
-            }
+                if (prov->error_strings != NULL) {
+                    ERR_unload_strings(prov->error_lib, prov->error_strings);
+                    OPENSSL_free(prov->error_strings);
+                    prov->error_strings = NULL;
+                }
 # endif
 #endif
-            OPENSSL_free(prov->operation_bits);
-            prov->operation_bits = NULL;
-            prov->operation_bits_sz = 0;
-            prov->flag_initialized = 0;
-        }
+                OPENSSL_free(prov->operation_bits);
+                prov->operation_bits = NULL;
+                prov->operation_bits_sz = 0;
+                prov->flag_initialized = 0;
+            }
 
-        /*
-         * When the refcount drops to zero, it has been taken out of
-         * the store.  All we have to do here is clean it out.
-         */
-        if (ref == 0) {
 #ifndef FIPS_MODULE
             DSO_free(prov->module);
 #endif
@@ -377,6 +373,7 @@ void ossl_provider_free(OSSL_PROVIDER *prov)
             sk_INFOPAIR_pop_free(prov->parameters, free_infopair);
 #ifndef HAVE_ATOMICS
             CRYPTO_THREAD_lock_free(prov->refcnt_lock);
+            CRYPTO_THREAD_lock_free(prov->activatecnt_lock);
 #endif
             OPENSSL_free(prov);
         }
@@ -460,7 +457,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_activate(OSSL_PROVIDER *prov)
+static int provider_init(OSSL_PROVIDER *prov)
 {
     const OSSL_DISPATCH *provider_dispatch = NULL;
     void *tmp_provctx = NULL;    /* safety measure */
@@ -633,18 +630,60 @@ static int provider_activate(OSSL_PROVIDER *prov)
     return 1;
 }
 
+static int provider_deactivate(OSSL_PROVIDER *prov)
+{
+    int ref = 0;
+
+    if (!ossl_assert(prov != NULL))
+        return 0;
+
+    if (CRYPTO_DOWN_REF(&prov->activatecnt, &ref, prov->activatecnt_lock) <= 0)
+        return 0;
+
+    if (ref < 1)
+        prov->flag_activated = 0;
+
+    /* We don't deinit here, that's done in ossl_provider_free() */
+    return 1;
+}
+
+static int provider_activate(OSSL_PROVIDER *prov)
+{
+    int ref = 0;
+
+    if (CRYPTO_UP_REF(&prov->activatecnt, &ref, prov->activatecnt_lock) <= 0)
+        return 0;
+
+    if (provider_init(prov)) {
+        prov->flag_activated = 1;
+
+        return 1;
+    }
+
+    provider_deactivate(prov);
+    return 0;
+}
+
 int ossl_provider_activate(OSSL_PROVIDER *prov)
 {
+    if (prov == NULL)
+        return 0;
     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;
 }
 
+int ossl_provider_deactivate(OSSL_PROVIDER *prov)
+{
+    if (prov == NULL)
+        return 0;
+    return provider_deactivate(prov);
+}
+
 void *ossl_provider_ctx(const OSSL_PROVIDER *prov)
 {
     return prov->provctx;
@@ -669,7 +708,7 @@ static int provider_forall_loaded(struct provider_store_st *store,
         OSSL_PROVIDER *prov =
             sk_OSSL_PROVIDER_value(store->providers, i);
 
-        if (prov->flag_initialized) {
+        if (prov->flag_activated) {
             if (found_activated != NULL)
                 *found_activated = 1;
             if (!(ret = cb(prov, cbdata)))
@@ -695,23 +734,14 @@ static void provider_activate_fallbacks(struct provider_store_st *store)
         for (i = 0; i < num_provs; i++) {
             OSSL_PROVIDER *prov = sk_OSSL_PROVIDER_value(store->providers, i);
 
-            /*
-             * Activated fallback providers get an extra refcount, to
-             * simulate a regular load.
-             * Note that we don't care if the activation succeeds or not,
-             * other than to maintain a correct refcount.  If the activation
-             * doesn't succeed, then any future attempt to use the fallback
-             * provider will fail anyway.
-             */
-            if (prov->flag_fallback) {
-                if (ossl_provider_up_ref(prov)) {
-                    if (!provider_activate(prov)) {
-                        ossl_provider_free(prov);
-                    } else {
+            if (ossl_provider_up_ref(prov)) {
+                if (prov->flag_fallback) {
+                    if (provider_activate(prov)) {
                         prov->flag_activated_as_fallback = 1;
                         activated_fallback_count++;
                     }
                 }
+                ossl_provider_free(prov);
             }
         }
 
@@ -765,7 +795,7 @@ int ossl_provider_available(OSSL_PROVIDER *prov)
         provider_activate_fallbacks(prov->store);
         CRYPTO_THREAD_unlock(prov->store->lock);
 
-        return prov->flag_initialized;
+        return prov->flag_activated;
     }
     return 0;
 }
diff --git a/crypto/rsa/rsa_lib.c b/crypto/rsa/rsa_lib.c
index 8e7ad45608..f4e3ff423e 100644
--- a/crypto/rsa/rsa_lib.c
+++ b/crypto/rsa/rsa_lib.c
@@ -194,6 +194,11 @@ OSSL_LIB_CTX *ossl_rsa_get0_libctx(RSA *r)
     return r->libctx;
 }
 
+void ossl_rsa_set0_libctx(RSA *r, OSSL_LIB_CTX *libctx)
+{
+    r->libctx = libctx;
+}
+
 #ifndef FIPS_MODULE
 int RSA_set_ex_data(RSA *r, int idx, void *arg)
 {
diff --git a/doc/internal/man3/ossl_provider_new.pod b/doc/internal/man3/ossl_provider_new.pod
index dc7717062c..d01673e767 100644
--- a/doc/internal/man3/ossl_provider_new.pod
+++ b/doc/internal/man3/ossl_provider_new.pod
@@ -6,7 +6,7 @@ ossl_provider_find, ossl_provider_new, ossl_provider_up_ref,
 ossl_provider_free,
 ossl_provider_set_fallback, ossl_provider_set_module_path,
 ossl_provider_add_parameter,
-ossl_provider_activate, ossl_provider_available,
+ossl_provider_activate, ossl_provider_deactivate, ossl_provider_available,
 ossl_provider_ctx,
 ossl_provider_forall_loaded,
 ossl_provider_name, ossl_provider_dso,
@@ -36,9 +36,13 @@ ossl_provider_get_capabilities
  int ossl_provider_add_parameter(OSSL_PROVIDER *prov, const char *name,
                                  const char *value);
 
- /* Load and initialize the Provider */
+ /*
+  * Activate the Provider
+  * If the Provider is a module, the module will be loaded
+  */
  int ossl_provider_activate(OSSL_PROVIDER *prov);
- /* Check if provider is available */
+ int ossl_provider_deactivate(OSSL_PROVIDER *prov);
+ /* Check if provider is available (activated) */
  int ossl_provider_available(OSSL_PROVIDER *prov);
 
  /* Return pointer to the provider's context */
@@ -89,8 +93,8 @@ Provider objects are reference counted.
 Provider objects are initially inactive, i.e. they are only recorded
 in the store, but are not used.
 They are activated with the first call to ossl_provider_activate(),
-and are inactivated when ossl_provider_free() has been called as many
-times as ossl_provider_activate() has.
+and are deactivated with the last call to ossl_provider_deactivate().
+Activation affects a separate counter.
 
 =head2 Functions
 
@@ -127,11 +131,10 @@ ossl_provider_up_ref() increments the provider object I<prov>'s
 reference count.
 
 ossl_provider_free() decrements the provider object I<prov>'s
-reference count; if it drops below 2, the provider object is assumed
-to have fallen out of use and will be deactivated (its I<teardown>
-function is called); if it drops down to zero, I<prov> is assumed to
-have been taken out of the store, and the associated module will be
-unloaded if one was loaded, and I<prov> itself will be freed.
+reference count; when it drops to zero, the provider object is assumed
+to have fallen out of use and will be deinitialized (its I<teardown>
+function is called), and the associated module will be unloaded if one
+was loaded, and I<prov> itself will be freed.
 
 ossl_provider_set_fallback() marks an available provider I<prov> as
 fallback.
@@ -155,9 +158,9 @@ Only text parameters can be given, and it's up to the provider to
 interpret them.
 
 ossl_provider_activate() "activates" the provider for the given
-provider object I<prov>.
-What "activates" means depends on what type of provider object it
-is:
+provider object I<prov> by incrementing its activation count, flagging
+it as activated, and initializing it if it isn't already initialized.
+Initializing means one of the following:
 
 =over 4
 
@@ -175,6 +178,10 @@ be located in that module, and called.
 
 =back
 
+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.
@@ -269,8 +276,9 @@ it has been incremented.
 
 ossl_provider_free() doesn't return any value.
 
-ossl_provider_set_module_path(), ossl_provider_set_fallback() and
-ossl_provider_activate() return 1 on success, or 0 on error.
+ossl_provider_set_module_path(), ossl_provider_set_fallback(),
+ossl_provider_activate() and ossl_provider_deactivate() return 1 on
+success, or 0 on error.
 
 ossl_provider_available() return 1 if the provider is available,
 otherwise 0.
diff --git a/engines/build.info b/engines/build.info
index e47f2d44a5..e275035946 100644
--- a/engines/build.info
+++ b/engines/build.info
@@ -88,6 +88,7 @@ IF[{- !$disabled{"engine"} -}]
     ENDIF
 
     SOURCE[loader_attic]=e_loader_attic.c ../crypto/pem/pvkfmt.c
+    DEFINE[loader_attic]=OPENSSL_NO_PROVIDER_CODE
     DEPEND[loader_attic]=../libcrypto
     INCLUDE[loader_attic]=../include
     IF[{- defined $target{shared_defflag} -}]
diff --git a/include/crypto/dh.h b/include/crypto/dh.h
index 3afe16935f..290cc7c0d2 100644
--- a/include/crypto/dh.h
+++ b/include/crypto/dh.h
@@ -14,6 +14,7 @@
 
 DH *dh_new_by_nid_ex(OSSL_LIB_CTX *libctx, int nid);
 DH *dh_new_ex(OSSL_LIB_CTX *libctx);
+void ossl_dh_set0_libctx(DH *d, OSSL_LIB_CTX *libctx);
 
 int dh_generate_ffc_parameters(DH *dh, int type, int pbits, int qbits,
                                BN_GENCB *cb);
diff --git a/include/crypto/dsa.h b/include/crypto/dsa.h
index 759fa4cce4..775a83c1ea 100644
--- a/include/crypto/dsa.h
+++ b/include/crypto/dsa.h
@@ -15,6 +15,7 @@
 #define DSA_PARAMGEN_TYPE_FIPS_186_2   1   /* Use legacy FIPS186-2 standard */
 
 DSA *dsa_new_with_ctx(OSSL_LIB_CTX *libctx);
+void ossl_dsa_set0_libctx(DSA *d, OSSL_LIB_CTX *libctx);
 
 int dsa_generate_ffc_parameters(DSA *dsa, int type, int pbits, int qbits,
                                 BN_GENCB *cb);
diff --git a/include/crypto/ec.h b/include/crypto/ec.h
index 451a3751a1..087457fa50 100644
--- a/include/crypto/ec.h
+++ b/include/crypto/ec.h
@@ -61,6 +61,7 @@ int ec_key_private_check(const EC_KEY *eckey);
 int ec_key_pairwise_check(const EC_KEY *eckey, BN_CTX *ctx);
 OSSL_LIB_CTX *ec_key_get_libctx(const EC_KEY *eckey);
 const char *ec_key_get0_propq(const EC_KEY *eckey);
+void ec_key_set0_libctx(EC_KEY *key, OSSL_LIB_CTX *libctx);
 
 /* Backend support */
 int ec_group_todata(const EC_GROUP *group, OSSL_PARAM_BLD *tmpl,
diff --git a/include/crypto/ecx.h b/include/crypto/ecx.h
index 4771df5fb6..df04cdb562 100644
--- a/include/crypto/ecx.h
+++ b/include/crypto/ecx.h
@@ -77,6 +77,7 @@ typedef struct ecx_key_st ECX_KEY;
 size_t ecx_key_length(ECX_KEY_TYPE type);
 ECX_KEY *ecx_key_new(OSSL_LIB_CTX *libctx, ECX_KEY_TYPE type, int haspubkey,
                      const char *propq);
+void ecx_key_set0_libctx(ECX_KEY *key, OSSL_LIB_CTX *libctx);
 unsigned char *ecx_key_allocate_privkey(ECX_KEY *key);
 void ecx_key_free(ECX_KEY *key);
 int ecx_key_up_ref(ECX_KEY *key);
diff --git a/include/crypto/rsa.h b/include/crypto/rsa.h
index ede11cfd41..cb53b5dde6 100644
--- a/include/crypto/rsa.h
+++ b/include/crypto/rsa.h
@@ -51,6 +51,7 @@ const char *ossl_rsa_oaeppss_nid2name(int md);
 
 RSA *ossl_rsa_new_with_ctx(OSSL_LIB_CTX *libctx);
 OSSL_LIB_CTX *ossl_rsa_get0_libctx(RSA *r);
+void ossl_rsa_set0_libctx(RSA *r, OSSL_LIB_CTX *libctx);
 
 int ossl_rsa_set0_all_params(RSA *r, const STACK_OF(BIGNUM) *primes,
                              const STACK_OF(BIGNUM) *exps,
diff --git a/include/internal/provider.h b/include/internal/provider.h
index ab36c93b32..7a0fc84875 100644
--- a/include/internal/provider.h
+++ b/include/internal/provider.h
@@ -47,10 +47,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
- * Inactivation is done by freeing the Provider
  */
 int ossl_provider_activate(OSSL_PROVIDER *prov);
-/* Check if the provider is available */
+int ossl_provider_deactivate(OSSL_PROVIDER *prov);
+/* Check if the provider is available (activated) */
 int ossl_provider_available(OSSL_PROVIDER *prov);
 
 /* Return pointer to the provider's context */
diff --git a/providers/implementations/encode_decode/decode_der2key.c b/providers/implementations/encode_decode/decode_der2key.c
index 17ed16235d..a91bd3b7b8 100644
--- a/providers/implementations/encode_decode/decode_der2key.c
+++ b/providers/implementations/encode_decode/decode_der2key.c
@@ -24,7 +24,11 @@
 #include <openssl/x509.h>
 #include "internal/cryptlib.h"   /* ossl_assert() */
 #include "internal/asn1.h"
+#include "crypto/dh.h"
+#include "crypto/dsa.h"
+#include "crypto/ec.h"
 #include "crypto/ecx.h"
+#include "crypto/rsa.h"
 #include "prov/bio.h"
 #include "prov/implementations.h"
 #include "prov/providercommonerr.h"
@@ -106,7 +110,9 @@ static OSSL_FUNC_decoder_freectx_fn der2key_freectx;
 static OSSL_FUNC_decoder_decode_fn der2key_decode;
 static OSSL_FUNC_decoder_export_object_fn der2key_export_object;
 
+struct der2key_ctx_st;           /* Forward declaration */
 typedef void *(extract_key_fn)(EVP_PKEY *);
+typedef void (adjust_key_fn)(void *, struct der2key_ctx_st *ctx);
 typedef void (free_key_fn)(void *);
 struct keytype_desc_st {
     const char *keytype_name;
@@ -130,10 +136,16 @@ struct keytype_desc_st {
     d2i_of_void *d2i_private_key;
     d2i_of_void *d2i_public_key;
     d2i_of_void *d2i_key_params;
+
     /*
      * For PKCS#8 decoders, we use EVP_PKEY extractors, EVP_PKEY_get1_{TYPE}()
      */
     extract_key_fn *extract_key;
+    /*
+     * For any key, we may need to make provider specific adjustments, such
+     * as ensure the key carries the correct library context.
+     */
+    adjust_key_fn *adjust_key;
     /* {type}_free() */
     free_key_fn *free_key;
 };
@@ -341,6 +353,9 @@ static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
         }
     }
 
+    if (key != NULL && ctx->desc->adjust_key != NULL)
+        ctx->desc->adjust_key(key, ctx);
+
  end:
     /*
      * Prune low-level ASN.1 parse errors from error queue, assuming
@@ -403,12 +418,18 @@ static int der2key_export_object(void *vctx,
 # define dh_d2i_key_params              (d2i_of_void *)d2i_DHparams
 # define dh_free                        (free_key_fn *)DH_free
 
+static void dh_adjust(void *key, struct der2key_ctx_st *ctx)
+{
+    ossl_dh_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
 # define dhx_evp_type                   EVP_PKEY_DHX
 # define dhx_evp_extract                (extract_key_fn *)EVP_PKEY_get1_DH
 # define dhx_d2i_private_key            NULL
 # define dhx_d2i_public_key             NULL
 # define dhx_d2i_key_params             (d2i_of_void *)d2i_DHxparams
 # define dhx_free                       (free_key_fn *)DH_free
+# define dhx_adjust                     dh_adjust
 #endif
 
 /* ---------------------------------------------------------------------- */
@@ -420,6 +441,11 @@ static int der2key_export_object(void *vctx,
 # define dsa_d2i_public_key             (d2i_of_void *)d2i_DSAPublicKey
 # define dsa_d2i_key_params             (d2i_of_void *)d2i_DSAparams
 # define dsa_free                       (free_key_fn *)DSA_free
+
+static void dsa_adjust(void *key, struct der2key_ctx_st *ctx)
+{
+    ossl_dsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
 #endif
 
 /* ---------------------------------------------------------------------- */
@@ -432,16 +458,28 @@ static int der2key_export_object(void *vctx,
 # define ec_d2i_key_params              (d2i_of_void *)d2i_ECParameters
 # define ec_free                        (free_key_fn *)EC_KEY_free
 
+static void ec_adjust(void *key, struct der2key_ctx_st *ctx)
+{
+    ec_key_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
 /*
  * ED25519, ED448, X25519, X448 only implement PKCS#8 and SubjectPublicKeyInfo,
  * so no d2i functions to be had.
  */
+
+static void ecx_key_adjust(void *key, struct der2key_ctx_st *ctx)
+{
+    ecx_key_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
 # define ed25519_evp_type               EVP_PKEY_ED25519
 # define ed25519_evp_extract            (extract_key_fn *)evp_pkey_get1_ED25519
 # define ed25519_d2i_private_key        NULL
 # define ed25519_d2i_public_key         NULL
 # define ed25519_d2i_key_params         NULL
 # define ed25519_free                   (free_key_fn *)ecx_key_free
+# define ed25519_adjust                 ecx_key_adjust
 
 # define ed448_evp_type                 EVP_PKEY_ED448
 # define ed448_evp_extract              (extract_key_fn *)evp_pkey_get1_ED448
@@ -449,6 +487,7 @@ static int der2key_export_object(void *vctx,
 # define ed448_d2i_public_key           NULL
 # define ed448_d2i_key_params           NULL
 # define ed448_free                     (free_key_fn *)ecx_key_free
+# define ed448_adjust                   ecx_key_adjust
 
 # define x25519_evp_type                EVP_PKEY_X25519
 # define x25519_evp_extract             (extract_key_fn *)evp_pkey_get1_X25519
@@ -456,6 +495,7 @@ static int der2key_export_object(void *vctx,
 # define x25519_d2i_public_key          NULL
 # define x25519_d2i_key_params          NULL
 # define x25519_free                    (free_key_fn *)ecx_key_free
+# define x25519_adjust                  ecx_key_adjust
 
 # define x448_evp_type                  EVP_PKEY_X448
 # define x448_evp_extract               (extract_key_fn *)evp_pkey_get1_X448
@@ -463,6 +503,7 @@ static int der2key_export_object(void *vctx,
 # define x448_d2i_public_key            NULL
 # define x448_d2i_key_params            NULL
 # define x448_free                      (free_key_fn *)ecx_key_free
+# define x448_adjust                    ecx_key_adjust
 #endif
 
 /* ---------------------------------------------------------------------- */
@@ -474,12 +515,18 @@ static int der2key_export_object(void *vctx,
 #define rsa_d2i_key_params              NULL
 #define rsa_free                        (free_key_fn *)RSA_free
 
+static void rsa_adjust(void *key, struct der2key_ctx_st *ctx)
+{
+    ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
 #define rsapss_evp_type                 EVP_PKEY_RSA_PSS
 #define rsapss_evp_extract              (extract_key_fn *)EVP_PKEY_get1_RSA
 #define rsapss_d2i_private_key          (d2i_of_void *)d2i_RSAPrivateKey
 #define rsapss_d2i_public_key           (d2i_of_void *)d2i_RSAPublicKey
 #define rsapss_d2i_key_params           NULL
 #define rsapss_free                     (free_key_fn *)RSA_free
+#define rsapss_adjust                   rsa_adjust
 
 /* ---------------------------------------------------------------------- */
 
@@ -494,6 +541,7 @@ static int der2key_export_object(void *vctx,
         keytype##_d2i_public_key,                       \
         NULL,                                           \
         NULL,                                           \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_type_specific_pub(keytype)                   \
@@ -503,6 +551,7 @@ static int der2key_export_object(void *vctx,
         keytype##_d2i_public_key,                       \
         NULL,                                           \
         NULL,                                           \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_type_specific_priv(keytype)                  \
@@ -512,6 +561,7 @@ static int der2key_export_object(void *vctx,
         NULL,                                           \
         NULL,                                           \
         NULL,                                           \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_type_specific_params(keytype)                \
@@ -521,6 +571,7 @@ static int der2key_export_object(void *vctx,
         NULL,                                           \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_type_specific(keytype)                       \
@@ -530,6 +581,7 @@ static int der2key_export_object(void *vctx,
         keytype##_d2i_public_key,                       \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_type_specific_no_pub(keytype)                \
@@ -540,6 +592,7 @@ static int der2key_export_object(void *vctx,
         NULL,                                           \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_PKCS8(keytype)                               \
@@ -549,6 +602,7 @@ static int der2key_export_object(void *vctx,
         NULL,                                           \
         NULL,                                           \
         keytype##_evp_extract,                          \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_SubjectPublicKeyInfo(keytype)                \
@@ -558,6 +612,7 @@ static int der2key_export_object(void *vctx,
         NULL,                                           \
         NULL,                                           \
         keytype##_evp_extract,                          \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_DH(keytype)                                  \
@@ -567,6 +622,7 @@ static int der2key_export_object(void *vctx,
         NULL,                                           \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_DHX(keytype)                                 \
@@ -576,6 +632,7 @@ static int der2key_export_object(void *vctx,
         NULL,                                           \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_DSA(keytype)                                 \
@@ -585,6 +642,7 @@ static int der2key_export_object(void *vctx,
         keytype##_d2i_public_key,                       \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_EC(keytype)                                  \
@@ -595,6 +653,7 @@ static int der2key_export_object(void *vctx,
         NULL,                                           \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        keytype##_adjust,                               \
         keytype##_free
 
 #define DO_RSA(keytype)                                 \
@@ -604,6 +663,7 @@ static int der2key_export_object(void *vctx,
         keytype##_d2i_public_key,                       \
         NULL,                                           \
         NULL,                                           \
+        keytype##_adjust,                               \
         keytype##_free
 
 /*
diff --git a/test/provider_internal_test.c b/test/provider_internal_test.c
index 478f28182d..4b2b6d5349 100644
--- a/test/provider_internal_test.c
+++ b/test/provider_internal_test.c
@@ -30,7 +30,8 @@ static int test_provider(OSSL_PROVIDER *prov, const char *expected_greeting)
         && 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)
-        && TEST_str_eq(greeting, expected_greeting);
+        && TEST_str_eq(greeting, expected_greeting)
+        && TEST_true(ossl_provider_deactivate(prov));
 
     TEST_info("Got this greeting: %s\n", greeting);
     ossl_provider_free(prov);


More information about the openssl-commits mailing list