Raise an error on syscall failure in tls_retry_write_records
[openssl.git] / crypto / evp / exchange.c
index 1a512c4283d14855ec981f4d462df198cf1b30cf..d9eed1cea5be26a852e187026fba4ff2c0213084 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2019-2023 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/crypto.h>
 #include <openssl/evp.h>
 #include <openssl/err.h>
+#include "internal/cryptlib.h"
 #include "internal/refcount.h"
-#include "crypto/evp.h"
 #include "internal/provider.h"
+#include "internal/core.h"
 #include "internal/numbers.h"   /* includes SIZE_MAX */
+#include "crypto/evp.h"
 #include "evp_local.h"
 
 static EVP_KEYEXCH *evp_keyexch_new(OSSL_PROVIDER *prov)
 {
     EVP_KEYEXCH *exchange = OPENSSL_zalloc(sizeof(EVP_KEYEXCH));
 
-    if (exchange == NULL) {
-        ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
+    if (exchange == NULL)
         return NULL;
-    }
 
-    exchange->lock = CRYPTO_THREAD_lock_new();
-    if (exchange->lock == NULL) {
-        ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
+    if (!CRYPTO_NEW_REF(&exchange->refcnt, 1)) {
         OPENSSL_free(exchange);
         return NULL;
     }
     exchange->prov = prov;
     ossl_provider_up_ref(prov);
-    exchange->refcnt = 1;
 
     return exchange;
 }
 
-static void *evp_keyexch_from_dispatch(int name_id,
-                                       const OSSL_DISPATCH *fns,
-                                       OSSL_PROVIDER *prov)
+static void *evp_keyexch_from_algorithm(int name_id,
+                                        const OSSL_ALGORITHM *algodef,
+                                        OSSL_PROVIDER *prov)
 {
+    const OSSL_DISPATCH *fns = algodef->implementation;
     EVP_KEYEXCH *exchange = NULL;
     int fncnt = 0, sparamfncnt = 0, gparamfncnt = 0;
 
     if ((exchange = evp_keyexch_new(prov)) == NULL) {
-        ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_EVP, ERR_R_EVP_LIB);
         goto err;
     }
 
     exchange->name_id = name_id;
+    if ((exchange->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL)
+        goto err;
+    exchange->description = algodef->algorithm_description;
 
     for (; fns->function_id != 0; fns++) {
         switch (fns->function_id) {
@@ -140,27 +141,28 @@ static void *evp_keyexch_from_dispatch(int name_id,
 
 void EVP_KEYEXCH_free(EVP_KEYEXCH *exchange)
 {
-    if (exchange != NULL) {
-        int i;
-
-        CRYPTO_DOWN_REF(&exchange->refcnt, &i, exchange->lock);
-        if (i > 0)
-            return;
-        ossl_provider_free(exchange->prov);
-        CRYPTO_THREAD_lock_free(exchange->lock);
-        OPENSSL_free(exchange);
-    }
+    int i;
+
+    if (exchange == NULL)
+        return;
+    CRYPTO_DOWN_REF(&exchange->refcnt, &i);
+    if (i > 0)
+        return;
+    OPENSSL_free(exchange->type_name);
+    ossl_provider_free(exchange->prov);
+    CRYPTO_FREE_REF(&exchange->refcnt);
+    OPENSSL_free(exchange);
 }
 
 int EVP_KEYEXCH_up_ref(EVP_KEYEXCH *exchange)
 {
     int ref = 0;
 
-    CRYPTO_UP_REF(&exchange->refcnt, &ref, exchange->lock);
+    CRYPTO_UP_REF(&exchange->refcnt, &ref);
     return 1;
 }
 
-OSSL_PROVIDER *EVP_KEYEXCH_provider(const EVP_KEYEXCH *exchange)
+OSSL_PROVIDER *EVP_KEYEXCH_get0_provider(const EVP_KEYEXCH *exchange)
 {
     return exchange->prov;
 }
@@ -169,18 +171,36 @@ EVP_KEYEXCH *EVP_KEYEXCH_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
                                const char *properties)
 {
     return evp_generic_fetch(ctx, OSSL_OP_KEYEXCH, algorithm, properties,
-                             evp_keyexch_from_dispatch,
+                             evp_keyexch_from_algorithm,
                              (int (*)(void *))EVP_KEYEXCH_up_ref,
                              (void (*)(void *))EVP_KEYEXCH_free);
 }
 
+EVP_KEYEXCH *evp_keyexch_fetch_from_prov(OSSL_PROVIDER *prov,
+                                         const char *algorithm,
+                                         const char *properties)
+{
+    return evp_generic_fetch_from_prov(prov, OSSL_OP_KEYEXCH,
+                                       algorithm, properties,
+                                       evp_keyexch_from_algorithm,
+                                       (int (*)(void *))EVP_KEYEXCH_up_ref,
+                                       (void (*)(void *))EVP_KEYEXCH_free);
+}
+
 int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx)
+{
+    return EVP_PKEY_derive_init_ex(ctx, NULL);
+}
+
+int EVP_PKEY_derive_init_ex(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[])
 {
     int ret;
     void *provkey = NULL;
     EVP_KEYEXCH *exchange = NULL;
     EVP_KEYMGMT *tmp_keymgmt = NULL;
+    const OSSL_PROVIDER *tmp_prov = NULL;
     const char *supported_exch = NULL;
+    int iter;
 
     if (ctx == NULL) {
         ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_NULL_PARAMETER);
@@ -190,106 +210,146 @@ int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx)
     evp_pkey_ctx_free_old_ops(ctx);
     ctx->operation = EVP_PKEY_OP_DERIVE;
 
-    /*
-     * TODO when we stop falling back to legacy, this and the ERR_pop_to_mark()
-     * calls can be removed.
-     */
     ERR_set_mark();
 
     if (evp_pkey_ctx_is_legacy(ctx))
         goto legacy;
 
     /*
-     * Ensure that the key is provided, either natively, or as a cached export.
-     * If not, goto legacy
+     * Some algorithms (e.g. legacy KDFs) don't have a pkey - so we create
+     * a blank one.
      */
-    tmp_keymgmt = ctx->keymgmt;
     if (ctx->pkey == NULL) {
-        /*
-         * Some algorithms (e.g. legacy KDFs) don't have a pkey - so we create
-         * a blank one.
-         */
         EVP_PKEY *pkey = EVP_PKEY_new();
 
-        if (pkey == NULL || !EVP_PKEY_set_type_by_keymgmt(pkey, tmp_keymgmt)) {
+        if (pkey == NULL
+            || !EVP_PKEY_set_type_by_keymgmt(pkey, ctx->keymgmt)
+            || (pkey->keydata = evp_keymgmt_newdata(ctx->keymgmt)) == NULL) {
             ERR_clear_last_mark();
             EVP_PKEY_free(pkey);
             ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
             goto err;
         }
-        provkey = pkey->keydata = evp_keymgmt_newdata(tmp_keymgmt);
-        if (provkey == NULL)
-            EVP_PKEY_free(pkey);
-        else
-            ctx->pkey = pkey;
-    } else {
-        provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
-                                            &tmp_keymgmt, ctx->propquery);
+        ctx->pkey = pkey;
     }
-    if (provkey == NULL)
-        goto legacy;
-    if (!EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
+
+    /*
+     * Try to derive the supported exch from |ctx->keymgmt|.
+     */
+    if (!ossl_assert(ctx->pkey->keymgmt == NULL
+                     || ctx->pkey->keymgmt == ctx->keymgmt)) {
+        ERR_clear_last_mark();
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    supported_exch = evp_keymgmt_util_query_operation_name(ctx->keymgmt,
+                                                           OSSL_OP_KEYEXCH);
+    if (supported_exch == NULL) {
         ERR_clear_last_mark();
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
     }
-    EVP_KEYMGMT_free(ctx->keymgmt);
-    ctx->keymgmt = tmp_keymgmt;
 
-    if (ctx->keymgmt->query_operation_name != NULL)
-        supported_exch = ctx->keymgmt->query_operation_name(OSSL_OP_KEYEXCH);
 
     /*
-     * If we didn't get a supported exch, assume there is one with the
-     * same name as the key type.
+     * We perform two iterations:
+     *
+     * 1.  Do the normal exchange fetch, using the fetching data given by
+     *     the EVP_PKEY_CTX.
+     * 2.  Do the provider specific exchange fetch, from the same provider
+     *     as |ctx->keymgmt|
+     *
+     * We then try to fetch the keymgmt from the same provider as the
+     * exchange, and try to export |ctx->pkey| to that keymgmt (when
+     * this keymgmt happens to be the same as |ctx->keymgmt|, the export
+     * is a no-op, but we call it anyway to not complicate the code even
+     * more).
+     * If the export call succeeds (returns a non-NULL provider key pointer),
+     * we're done and can perform the operation itself.  If not, we perform
+     * the second iteration, or jump to legacy.
      */
-    if (supported_exch == NULL)
-        supported_exch = ctx->keytype;
+    for (iter = 1, provkey = NULL; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree = NULL;
 
-    /*
-     * Because we cleared out old ops, we shouldn't need to worry about
-     * checking if exchange is already there.
-     */
-    exchange = EVP_KEYEXCH_fetch(ctx->libctx, supported_exch, ctx->propquery);
+        /*
+         * If we're on the second iteration, free the results from the first.
+         * They are NULL on the first iteration, so no need to check what
+         * iteration we're on.
+         */
+        EVP_KEYEXCH_free(exchange);
+        EVP_KEYMGMT_free(tmp_keymgmt);
+
+        switch (iter) {
+        case 1:
+            exchange =
+                EVP_KEYEXCH_fetch(ctx->libctx, supported_exch, ctx->propquery);
+            if (exchange != NULL)
+                tmp_prov = EVP_KEYEXCH_get0_provider(exchange);
+            break;
+        case 2:
+            tmp_prov = EVP_KEYMGMT_get0_provider(ctx->keymgmt);
+            exchange =
+                evp_keyexch_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                              supported_exch, ctx->propquery);
+            if (exchange == NULL)
+                goto legacy;
+            break;
+        }
+        if (exchange == NULL)
+            continue;
 
-    if (exchange == NULL
-        || (EVP_KEYMGMT_provider(ctx->keymgmt)
-            != EVP_KEYEXCH_provider(exchange))) {
         /*
-         * We don't need to free ctx->keymgmt here, as it's not necessarily
-         * tied to this operation.  It will be freed by EVP_PKEY_CTX_free().
+         * Ensure that the key is provided, either natively, or as a cached
+         * export.  We start by fetching the keymgmt with the same name as
+         * |ctx->keymgmt|, but from the provider of the exchange method, using
+         * the same property query as when fetching the exchange method.
+         * With the keymgmt we found (if we did), we try to export |ctx->pkey|
+         * to it (evp_pkey_export_to_provider() is smart enough to only actually
+         * export it if |tmp_keymgmt| is different from |ctx->pkey|'s keymgmt)
          */
+        tmp_keymgmt_tofree = tmp_keymgmt =
+            evp_keymgmt_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                        EVP_KEYMGMT_get0_name(ctx->keymgmt),
+                                        ctx->propquery);
+        if (tmp_keymgmt != NULL)
+            provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
+                                                  &tmp_keymgmt, ctx->propquery);
+        if (tmp_keymgmt == NULL)
+            EVP_KEYMGMT_free(tmp_keymgmt_tofree);
+    }
+
+    if (provkey == NULL) {
         EVP_KEYEXCH_free(exchange);
         goto legacy;
     }
 
-    /*
-     * TODO remove this when legacy is gone
-     * If we don't have the full support we need with provided methods,
-     * let's go see if legacy does.
-     */
     ERR_pop_to_mark();
 
     /* No more legacy from here down to legacy: */
 
+    /* A Coverity false positive with up_ref/down_ref and free */
+    /* coverity[use_after_free] */
     ctx->op.kex.exchange = exchange;
-    ctx->op.kex.exchprovctx = exchange->newctx(ossl_provider_ctx(exchange->prov));
-    if (ctx->op.kex.exchprovctx == NULL) {
+    /* A Coverity false positive with up_ref/down_ref and free */
+    /* coverity[deref_arg] */
+    ctx->op.kex.algctx = exchange->newctx(ossl_provider_ctx(exchange->prov));
+    if (ctx->op.kex.algctx == NULL) {
         /* The provider key can stay in the cache */
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
     }
-    ret = exchange->init(ctx->op.kex.exchprovctx, provkey);
+    ret = exchange->init(ctx->op.kex.algctx, provkey, params);
 
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return ret ? 1 : 0;
  err:
     evp_pkey_ctx_free_old_ops(ctx);
     ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return 0;
 
  legacy:
     /*
-     * TODO remove this when legacy is gone
      * If we don't have the full support we need with provided methods,
      * let's go see if legacy does.
      */
@@ -308,21 +368,25 @@ int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx)
     ret = ctx->pmeth->derive_init(ctx);
     if (ret <= 0)
         ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return ret;
 #endif
 }
 
-int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer)
+int EVP_PKEY_derive_set_peer_ex(EVP_PKEY_CTX *ctx, EVP_PKEY *peer,
+                                int validate_peer)
 {
-    int ret = 0;
+    int ret = 0, check;
     void *provkey = NULL;
+    EVP_PKEY_CTX *check_ctx = NULL;
+    EVP_KEYMGMT *tmp_keymgmt = NULL, *tmp_keymgmt_tofree = NULL;
 
     if (ctx == NULL) {
         ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_NULL_PARAMETER);
         return -1;
     }
 
-    if (!EVP_PKEY_CTX_IS_DERIVE_OP(ctx) || ctx->op.kex.exchprovctx == NULL)
+    if (!EVP_PKEY_CTX_IS_DERIVE_OP(ctx) || ctx->op.kex.algctx == NULL)
         goto legacy;
 
     if (ctx->op.kex.exchange->set_peer == NULL) {
@@ -330,24 +394,49 @@ int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer)
         return -2;
     }
 
-    provkey = evp_pkey_export_to_provider(peer, ctx->libctx, &ctx->keymgmt,
-                                          ctx->propquery);
+    if (validate_peer) {
+        check_ctx = EVP_PKEY_CTX_new_from_pkey(ctx->libctx, peer, ctx->propquery);
+        if (check_ctx == NULL)
+            return -1;
+        check = EVP_PKEY_public_check(check_ctx);
+        EVP_PKEY_CTX_free(check_ctx);
+        if (check <= 0)
+            return -1;
+    }
+
+    /*
+     * Ensure that the |peer| is provided, either natively, or as a cached
+     * export.  We start by fetching the keymgmt with the same name as
+     * |ctx->keymgmt|, but from the provider of the exchange method, using
+     * the same property query as when fetching the exchange method.
+     * With the keymgmt we found (if we did), we try to export |peer|
+     * to it (evp_pkey_export_to_provider() is smart enough to only actually
+     * export it if |tmp_keymgmt| is different from |peer|'s keymgmt)
+     */
+    tmp_keymgmt_tofree = tmp_keymgmt =
+        evp_keymgmt_fetch_from_prov((OSSL_PROVIDER *)
+                                    EVP_KEYEXCH_get0_provider(ctx->op.kex.exchange),
+                                    EVP_KEYMGMT_get0_name(ctx->keymgmt),
+                                    ctx->propquery);
+    if (tmp_keymgmt != NULL)
+        /* A Coverity issue with up_ref/down_ref and free */
+        /* coverity[pass_freed_arg] */
+        provkey = evp_pkey_export_to_provider(peer, ctx->libctx,
+                                              &tmp_keymgmt, ctx->propquery);
+    EVP_KEYMGMT_free(tmp_keymgmt_tofree);
+
     /*
      * If making the key provided wasn't possible, legacy may be able to pick
      * it up
      */
     if (provkey == NULL)
         goto legacy;
-    return ctx->op.kex.exchange->set_peer(ctx->op.kex.exchprovctx, provkey);
+    return ctx->op.kex.exchange->set_peer(ctx->op.kex.algctx, provkey);
 
  legacy:
 #ifdef FIPS_MODULE
     return ret;
 #else
-    /*
-     * TODO(3.0) investigate the case where the operation is deemed legacy,
-     * but the given peer key is provider only.
-     */
     if (ctx->pmeth == NULL
         || !(ctx->pmeth->derive != NULL
              || ctx->pmeth->encrypt != NULL
@@ -409,6 +498,11 @@ int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer)
 #endif
 }
 
+int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer)
+{
+    return EVP_PKEY_derive_set_peer_ex(ctx, peer, 1);
+}
+
 int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *pkeylen)
 {
     int ret;
@@ -423,10 +517,10 @@ int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *pkeylen)
         return -1;
     }
 
-    if (ctx->op.kex.exchprovctx == NULL)
+    if (ctx->op.kex.algctx == NULL)
         goto legacy;
 
-    ret = ctx->op.kex.exchange->derive(ctx->op.kex.exchprovctx, key, pkeylen,
+    ret = ctx->op.kex.exchange->derive(ctx->op.kex.algctx, key, pkeylen,
                                        key != NULL ? *pkeylen : 0);
 
     return ret;
@@ -440,14 +534,25 @@ int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *pkeylen)
         return ctx->pmeth->derive(ctx, key, pkeylen);
 }
 
-int EVP_KEYEXCH_number(const EVP_KEYEXCH *keyexch)
+int evp_keyexch_get_number(const EVP_KEYEXCH *keyexch)
 {
     return keyexch->name_id;
 }
 
+const char *EVP_KEYEXCH_get0_name(const EVP_KEYEXCH *keyexch)
+{
+    return keyexch->type_name;
+}
+
+const char *EVP_KEYEXCH_get0_description(const EVP_KEYEXCH *keyexch)
+{
+    return keyexch->description;
+}
+
 int EVP_KEYEXCH_is_a(const EVP_KEYEXCH *keyexch, const char *name)
 {
-    return evp_is_a(keyexch->prov, keyexch->name_id, NULL, name);
+    return keyexch != NULL
+           && evp_is_a(keyexch->prov, keyexch->name_id, NULL, name);
 }
 
 void EVP_KEYEXCH_do_all_provided(OSSL_LIB_CTX *libctx,
@@ -456,7 +561,8 @@ void EVP_KEYEXCH_do_all_provided(OSSL_LIB_CTX *libctx,
 {
     evp_generic_do_all(libctx, OSSL_OP_KEYEXCH,
                        (void (*)(void *, void *))fn, arg,
-                       evp_keyexch_from_dispatch,
+                       evp_keyexch_from_algorithm,
+                       (int (*)(void *))EVP_KEYEXCH_up_ref,
                        (void (*)(void *))EVP_KEYEXCH_free);
 }
 
@@ -477,7 +583,7 @@ const OSSL_PARAM *EVP_KEYEXCH_gettable_ctx_params(const EVP_KEYEXCH *keyexch)
     if (keyexch == NULL || keyexch->gettable_ctx_params == NULL)
         return NULL;
 
-    provctx = ossl_provider_ctx(EVP_KEYEXCH_provider(keyexch));
+    provctx = ossl_provider_ctx(EVP_KEYEXCH_get0_provider(keyexch));
     return keyexch->gettable_ctx_params(NULL, provctx);
 }
 
@@ -487,6 +593,6 @@ const OSSL_PARAM *EVP_KEYEXCH_settable_ctx_params(const EVP_KEYEXCH *keyexch)
 
     if (keyexch == NULL || keyexch->settable_ctx_params == NULL)
         return NULL;
-    provctx = ossl_provider_ctx(EVP_KEYEXCH_provider(keyexch));
+    provctx = ossl_provider_ctx(EVP_KEYEXCH_get0_provider(keyexch));
     return keyexch->settable_ctx_params(NULL, provctx);
 }