serialization: break the provider locating code to avoid deadlock.
[openssl.git] / crypto / serializer / serializer_pkey.c
index d3c3362f66238aac43ec55829f1ad5e48856ce19..d6120702403fbdb86ae4e9c3152bfee21fdb9873 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
 #include <openssl/params.h>
 #include <openssl/serializer.h>
 #include <openssl/core_names.h>
+#include <openssl/safestack.h>
 #include "internal/provider.h"
 #include "internal/property.h"
 #include "crypto/evp.h"
 #include "serializer_local.h"
 
+DEFINE_STACK_OF_CSTRING()
+
 int OSSL_SERIALIZER_CTX_set_cipher(OSSL_SERIALIZER_CTX *ctx,
                                    const char *cipher_name,
                                    const char *propquery)
@@ -92,77 +95,16 @@ int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx, int enc,
  */
 
 struct selected_serializer_st {
-    OPENSSL_CTX *libctx;
-    const OSSL_PROVIDER *desired_provider;
-    const char *propquery;
-
-    /*
-     * When selecting serializers, we need to check the intended use.
-     * This is governed by the |domainparams| flag in the EVP_PKEY,
-     * we must just make sure to filter on 'type=domainparams' accordingly.
-     */
-    int want_domainparams;
-
-    /*
-     * Serializers offer two functions, one that handles object data in
-     * the form of a OSSL_PARAM array, and one that directly handles a
-     * provider side object.  The latter requires that the serializer
-     * is offered by the same provider that holds that object, but is
-     * more desirable because it usually provides faster serialization.
-     *
-     * When looking up possible serializers, we save the first that can
-     * handle an OSSL_PARAM array in |first|, and the first that can
-     * handle a provider side object in |desired|.
-     */
-    OSSL_SERIALIZER *first;
-    OSSL_SERIALIZER *desired;
+    STACK_OF(OPENSSL_CSTRING) *names;
+    int error;
 };
 
-static void select_serializer(const char *name, void *data)
+static void cache_serializers(const char *name, void *data)
 {
     struct selected_serializer_st *d = data;
-    OSSL_SERIALIZER *s = NULL;
-    OSSL_PROPERTY_LIST *check =
-        d->want_domainparams
-        ? ossl_parse_query(d->libctx, "type=domainparams")
-        : NULL;
-
-    /* No need to look further if we already have the more desirable option */
-    if (d->desired != NULL)
-        return;
-
-    if ((s = OSSL_SERIALIZER_fetch(d->libctx, name, d->propquery)) != NULL) {
-        /*
-         * Extra check if domain parameters are explicitly specified:
-         * only accept serializers that have the "type=domainparams"
-         * property.
-         *
-         * For data that isn't marked as domain parameters, a domain
-         * parameters serializer is still acceptable, because a key
-         * may hold domain parameters too.
-         */
-        if (d->want_domainparams) {
-            OSSL_PROPERTY_LIST *current_props =
-                ossl_parse_property(d->libctx, OSSL_SERIALIZER_properties(s));
-            int check_cnt = ossl_property_match_count(check, current_props);
-
-            if (check_cnt == 0) {
-                OSSL_SERIALIZER_free(s);
-                return;
-            }
-        }
 
-        if (d->first == NULL && s->serialize_data != NULL) {
-            d->first = s;
-        } else if (OSSL_SERIALIZER_provider(s) == d->desired_provider
-                   && s->serialize_object != NULL) {
-            OSSL_SERIALIZER_free(d->first);
-            d->first = NULL;
-            d->desired = s;
-        } else {
-            OSSL_SERIALIZER_free(s);
-        }
-    }
+    if (sk_OPENSSL_CSTRING_push(d->names, name) <= 0)
+        d->error = 1;
 }
 
 /*
@@ -286,7 +228,7 @@ static int serializer_write_cb(const OSSL_PARAM params[], void *arg)
     OSSL_SERIALIZER_CTX *ctx = write_data->ctx;
     BIO *out = write_data->out;
 
-    return ctx->ser->serialize_data(ctx->serctx, params, out,
+    return ctx->ser->serialize_data(ctx->serctx, params, (OSSL_CORE_BIO *)out,
                                     serializer_passphrase_out_cb, ctx);
 }
 
@@ -298,9 +240,8 @@ static int serializer_write_cb(const OSSL_PARAM params[], void *arg)
 static int serializer_EVP_PKEY_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out)
 {
     const EVP_PKEY *pkey = ctx->object;
-    void *provdata = pkey->pkeys[0].provdata;
-    int domainparams = pkey->pkeys[0].domainparams;
-    EVP_KEYMGMT *keymgmt = pkey->pkeys[0].keymgmt;
+    void *keydata = pkey->keydata;
+    EVP_KEYMGMT *keymgmt = pkey->keymgmt;
 
     /*
      * OSSL_SERIALIZER_CTX_new() creates a context, even when the
@@ -319,15 +260,12 @@ static int serializer_EVP_PKEY_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out)
         write_data.ctx = ctx;
         write_data.out = out;
 
-        if (domainparams)
-            return evp_keymgmt_exportdomparams(keymgmt, provdata,
-                                               serializer_write_cb,
-                                               &write_data);
-        return evp_keymgmt_exportkey(keymgmt, provdata,
-                                     serializer_write_cb, &write_data);
+        return evp_keymgmt_export(keymgmt, keydata, ctx->selection,
+                                  &serializer_write_cb, &write_data);
     }
 
-    return ctx->ser->serialize_object(ctx->serctx, provdata, out,
+    return ctx->ser->serialize_object(ctx->serctx, keydata,
+                                      (OSSL_CORE_BIO *)out,
                                       serializer_passphrase_out_cb, ctx);
 }
 
@@ -342,7 +280,8 @@ OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
 {
     OSSL_SERIALIZER_CTX *ctx = NULL;
     OSSL_SERIALIZER *ser = NULL;
-    EVP_KEYMGMT *keymgmt = pkey->pkeys[0].keymgmt;
+    EVP_KEYMGMT *keymgmt = pkey->keymgmt;
+    int selection = OSSL_KEYMGMT_SELECT_ALL;
 
     if (!ossl_assert(pkey != NULL && propquery != NULL)) {
         ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
@@ -353,23 +292,74 @@ OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
         const OSSL_PROVIDER *desired_prov = EVP_KEYMGMT_provider(keymgmt);
         OPENSSL_CTX *libctx = ossl_provider_library_context(desired_prov);
         struct selected_serializer_st sel_data;
+        OSSL_PROPERTY_LIST *check = ossl_parse_query(libctx, "type=parameters");
+        OSSL_PROPERTY_LIST *current_props = NULL;
+        OSSL_SERIALIZER *first = NULL;
+        const char *name;
+        int i;
+
+        /*
+         * Select the serializer in two steps.  First, get the names of all of
+         * the serializers.  Then determine which is the best one to use.
+         * This has to be broken because it isn't possible to fetch the
+         * serialisers inside EVP_KEYMGMT_names_do_all() due to locking
+         * order inversions with the store lock.
+         */
+        sel_data.error = 0;
+        sel_data.names = sk_OPENSSL_CSTRING_new_null();
+        if (sel_data.names == NULL)
+            return NULL;
+        EVP_KEYMGMT_names_do_all(keymgmt, cache_serializers, &sel_data);
+        /*
+         * Ignore memory allocation errors that are indicated in sel_data.error
+         * in case a suitable provider does get found regardless.
+         */
 
-        memset(&sel_data, 0, sizeof(sel_data));
-        sel_data.libctx = libctx;
-        sel_data.desired_provider = desired_prov;
-        sel_data.propquery = propquery;
-        sel_data.want_domainparams = pkey->pkeys[0].domainparams;
-        EVP_KEYMGMT_names_do_all(keymgmt, select_serializer, &sel_data);
-
-        if (sel_data.desired != NULL) {
-            ser = sel_data.desired;
-            sel_data.desired = NULL;
-        } else if (sel_data.first != NULL) {
-            ser = sel_data.first;
-            sel_data.first = NULL;
+        /*
+         * Serializers offer two functions, one that handles object data in
+         * the form of a OSSL_PARAM array, and one that directly handles a
+         * provider side object.  The latter requires that the serializer
+         * is offered by the same provider that holds that object, but is
+         * more desirable because it usually provides faster serialization.
+         *
+         * When looking up possible serializers, we save the first that can
+         * handle an OSSL_PARAM array in |first| and use that if nothing
+         * better turns up.
+         */
+        for (i = 0; i < sk_OPENSSL_CSTRING_num(sel_data.names); i++) {
+            name = sk_OPENSSL_CSTRING_value(sel_data.names, i);
+            ser = OSSL_SERIALIZER_fetch(libctx, name, propquery);
+            if (ser != NULL) {
+                if (OSSL_SERIALIZER_provider(ser) == desired_prov
+                        && ser->serialize_object != NULL) {
+                    OSSL_SERIALIZER_free(first);
+                    break;
+                }
+                if (first == NULL && ser->serialize_data != NULL)
+                    first = ser;
+                else
+                    OSSL_SERIALIZER_free(ser);
+                ser = NULL;
+            }
+        }
+        sk_OPENSSL_CSTRING_free(sel_data.names);
+        if (ser == NULL)
+            ser = first;
+
+        if (ser != NULL) {
+            current_props =
+                ossl_parse_property(libctx, OSSL_SERIALIZER_properties(ser));
+            if (ossl_property_match_count(check, current_props) > 0)
+                selection = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS;
+            ossl_property_free(current_props);
+            ossl_property_free(check);
+        } else {
+            if (sel_data.error)
+                ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
+            else
+                ERR_raise(ERR_LIB_OSSL_SERIALIZER,
+                          OSSL_SERIALIZER_R_SERIALIZER_NOT_FOUND);
         }
-        OSSL_SERIALIZER_free(sel_data.first);
-        OSSL_SERIALIZER_free(sel_data.desired);
     }
 
     ctx = OSSL_SERIALIZER_CTX_new(ser); /* refcnt(ser)++ */
@@ -377,6 +367,7 @@ OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
 
     if (ctx != NULL) {
         /* Setup for OSSL_SERIALIZE_to_bio() */
+        ctx->selection = selection;
         ctx->object = pkey;
         ctx->do_output = serializer_EVP_PKEY_to_bio;
     }