From cd046f6c93b39d673a58c18648d8906e954c4f5d Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Wed, 17 Dec 2025 10:54:16 +0100
Subject: [PATCH] openssl: toggling CURLSSLOPT_NO_PARTIALCHAIN makes a
 different CA cache

Reported-by: Stanislav Fort

Closes #20009

CVE: CVE-2025-14819
Upstream-Status: Backport [https://github.com/curl/curl/commit/cd046f6c93b39d673a58c18648d8906e954c4f5d]
Signed-off-by: Peter Marko <peter.marko@siemens.com>
---
 lib/vtls/openssl.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index a7f169d641..7563d9a090 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -317,6 +317,7 @@ struct multi_ssl_backend_data {
   char *CAfile;         /* CAfile path used to generate X509 store */
   X509_STORE *store;    /* cached X509 store or NULL if none */
   struct curltime time; /* when the cached store was created */
+  BIT(no_partialchain); /* keep partial chain state */
 };
 #endif /* HAVE_SSL_X509_STORE_SHARE */
 
@@ -3378,12 +3379,16 @@ static bool cached_x509_store_expired(const struct Curl_easy *data,
 
 static bool cached_x509_store_different(
   struct Curl_cfilter *cf,
+                                             const struct Curl_easy *data,
   const struct multi_ssl_backend_data *mb)
 {
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  struct ssl_config_data *ssl_config =
+    Curl_ssl_cf_get_config(cf, CURL_UNCONST(data));
+  if(mb->no_partialchain != ssl_config->no_partialchain)
+    return TRUE;
   if(!mb->CAfile || !conn_config->CAfile)
     return mb->CAfile != conn_config->CAfile;
-
   return strcmp(mb->CAfile, conn_config->CAfile);
 }
 
@@ -3398,7 +3403,7 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
      multi->ssl_backend_data &&
      multi->ssl_backend_data->store &&
      !cached_x509_store_expired(data, multi->ssl_backend_data) &&
-     !cached_x509_store_different(cf, multi->ssl_backend_data)) {
+     !cached_x509_store_different(cf, data, multi->ssl_backend_data)) {
     store = multi->ssl_backend_data->store;
   }
 
@@ -3427,6 +3432,8 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
 
   if(X509_STORE_up_ref(store)) {
     char *CAfile = NULL;
+    struct ssl_config_data *ssl_config =
+      Curl_ssl_cf_get_config(cf, CURL_UNCONST(data));
 
     if(conn_config->CAfile) {
       CAfile = strdup(conn_config->CAfile);
@@ -3444,6 +3451,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
     mbackend->time = Curl_now();
     mbackend->store = store;
     mbackend->CAfile = CAfile;
+    mbackend->no_partialchain = ssl_config->no_partialchain;
   }
 }
 
