openembedded-core.lists.openembedded.org archive mirror
 help / color / mirror / Atom feed
* [OE-core][wrynose][PATCH 0/8] curl: Security fixes
@ 2026-08-05  8:30 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:30 ` [OE-core][wrynose][PATCH 1/8] curl: fix CVE-2026-8286 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-08-05  8:30 UTC (permalink / raw)
  To: openembedded-core

From: Deepak Rathore <deeratho@cisco.com>

This series addresses multiple curl security vulnerabilities affecting
the curl 8.19.0 version provided by the Wrynose branch.

Five changes backport upstream security fixes:

- CVE-2026-8286: Include TLS configuration when matching connections
  that may be upgraded using STARTTLS.
- CVE-2026-8927: Clear Digest authentication state when an
  environment-configured proxy changes.
- CVE-2026-8932: Include the complete mTLS client credential
  configuration in connection reuse and TLS session-cache matching.
- CVE-2026-8458: Include the SASL service name in connection reuse
  matching.
- CVE-2026-11856: Clear Digest authentication state when the origin,
  proxy, or credentials change.

Three changes add CVE_STATUS annotations for vulnerabilities whose
applicability depends on the Wrynose curl configuration:

- CVE-2026-8924 is not applicable because the recipe explicitly builds
    curl without Public Suffix List support using --without-libpsl.
- CVE-2026-9547 is not applicable because the vulnerable libssh backend
  is not available in the recipe. The supported libssh2 backend is not
  affected.
- CVE-2026-12064 is marked unpatched when the optional libssh2
  PACKAGECONFIG is enabled and not-applicable-config otherwise, because
  the affected SCP/SFTP support is provided through libssh2.

The manual adaptations required for curl 8.19.0 are documented in the
Backport Changes sections of the corresponding source patches.

Validation performed:

- All the curl patches are prepared on top of this upstream curl patch which is
under review: https://lists.openembedded.org/g/openembedded-core/topic/120530931
- All recipe changes apply using git am without three-way fallback.
- All existing Wrynose patches and the proposed source patches apply to
  the checksum-verified curl 8.19.0 source with no fuzz.
- A host build using the GnuTLS backend completed successfully.
- Regression tests 1686, 3303, and 3304 passed.

Deepak Rathore (8):
  curl: fix CVE-2026-8286
  curl: set CVE_STATUS for CVE-2026-8924
  curl: fix CVE-2026-8927
  curl: fix CVE-2026-8932
  curl: fix CVE-2026-8458
  curl: fix CVE-2026-11856
  curl: set CVE_STATUS for CVE-2026-9547
  curl: set CVE_STATUS for CVE-2026-12064

 .../curl/curl/CVE-2026-11856_p1.patch         |  372 ++++++
 .../curl/curl/CVE-2026-11856_p2.patch         |   72 ++
 .../curl/curl/CVE-2026-8286.patch             |   81 ++
 .../curl/curl/CVE-2026-8458.patch             |  202 +++
 .../curl/curl/CVE-2026-8927.patch             |  349 +++++
 .../curl/curl/CVE-2026-8932-dependent.patch   |   71 +
 .../curl/curl/CVE-2026-8932.patch             | 1148 +++++++++++++++++
 meta/recipes-support/curl/curl_8.19.0.bb      |   11 +
 8 files changed, 2306 insertions(+)
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-11856_p1.patch
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-11856_p2.patch
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-8286.patch
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-8458.patch
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-8927.patch
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-8932-dependent.patch
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-8932.patch

-- 
2.35.6



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [OE-core][wrynose][PATCH 1/8] curl: fix CVE-2026-8286
  2026-08-05  8:30 [OE-core][wrynose][PATCH 0/8] curl: Security fixes Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-08-05  8:30 ` Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:30 ` [OE-core][wrynose][PATCH 2/8] curl: set CVE_STATUS for CVE-2026-8924 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-08-05  8:30 UTC (permalink / raw)
  To: openembedded-core

From: Deepak Rathore <deeratho@cisco.com>

This patch applies the upstream curl security fix backport for
CVE-2026-8286. The upstream fix commit is referenced in [1],
and the public curl advisory is referenced in [2]. The backported
commit link is also recorded in the embedded patch header.

[1] https://github.com/curl/curl/commit/a86efdd7ca5433de9231e650f18247de8319ad16
[2] https://curl.se/docs/CVE-2026-8286.html

Signed-off-by: Deepak Rathore <deeratho@cisco.com>
---
 .../curl/curl/CVE-2026-8286.patch             | 81 +++++++++++++++++++
 meta/recipes-support/curl/curl_8.19.0.bb      |  1 +
 2 files changed, 82 insertions(+)
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-8286.patch

diff --git a/meta/recipes-support/curl/curl/CVE-2026-8286.patch b/meta/recipes-support/curl/curl/CVE-2026-8286.patch
new file mode 100644
index 0000000000..6fa42887a1
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-8286.patch
@@ -0,0 +1,81 @@
+From 90ff17f6bfe1d358c26fa25ab457bc420a9847b2 Mon Sep 17 00:00:00 2001
+From: Stefan Eissing <stefan@eissing.org>
+Date: Thu, 7 May 2026 10:30:07 +0200
+Subject: [PATCH] url: fix connection reuse for starttls protocols
+
+When a connection is tested for reuse in a transfer that *may* upgrade
+to TLS (commonly via STARTTLS), the SSL configuration must match the
+existing connection.
+
+Reported-by: Andrew Nesbit
+Closes #21522
+
+CVE: CVE-2026-8286
+Upstream-Status: Backport [https://github.com/curl/curl/commit/a86efdd7ca5433de9231e650f18247de8319ad16]
+
+Backport Changes:
+- Wrynose applies upstream commit [1] before this patch. That commit
+  adds req_tls to struct url_conn_match and initializes it in
+  url_attach_existing().
+- This backport replaces that local req_tls state with the upstream
+  may_tls/require_tls split and updates url_match_ssl_use(),
+  url_match_ssl_config(), and url_attach_existing() to preserve the
+  upstream STARTTLS reuse behavior on the Wrynose 8.19.0 codebase.
+
+[1] https://github.com/curl/curl/commit/507e7be573b0a76fca597b75ff7cb27a66e7d865
+
+(cherry picked from commit a86efdd7ca5433de9231e650f18247de8319ad16)
+Signed-off-by: Deepak Rathore <deeratho@cisco.com>
+---
+ lib/url.c | 15 ++++++++++-----
+ 1 file changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/lib/url.c b/lib/url.c
+index 4ebff50ef1..6c1375f8e3 100644
+--- a/lib/url.c
++++ b/lib/url.c
+@@ -745,7 +745,11 @@ struct url_conn_match {
+   BIT(want_proxy_ntlm_http);
+   BIT(want_nego_http);
+   BIT(want_proxy_nego_http);
+-  BIT(req_tls); /* require TLS use from a clear-text start */
++  BIT(may_tls); /* May upgrade clear-text connection to TLS, can only reuse
++                 * connections that have matching TLS configuration.
++                 * Always TRUE if `req_tls` is TRUE. */
++  BIT(require_tls); /* Requires TLS use from a clear-text start, can only
++                 * reuse connections that have TLS. */
+   BIT(wait_pipe);
+   BIT(force_reuse);
+   BIT(seen_pending_conn);
+@@ -897,7 +901,7 @@ static bool url_match_ssl_use(struct connectdata *conn,
+        (get_protocol_family(conn->scheme) != m->needle->scheme->protocol))
+       return FALSE;
+   }
+-  else if(m->req_tls)
++  else if(m->require_tls)
+     /* a clear-text STARTTLS protocol with required TLS */
+     return FALSE;
+   return TRUE;
+@@ -1090,8 +1094,8 @@ static bool url_match_destination(struct connectdata *conn,
+ static bool url_match_ssl_config(struct connectdata *conn,
+                                  struct url_conn_match *m)
+ {
+-  /* If talking TLS, conn needs to use the same SSL options. */
+-  if((m->needle->scheme->flags & PROTOPT_SSL) &&
++  /* If talking/upgrading to TLS, conn needs to use the same SSL options. */
++  if(((m->needle->scheme->flags & PROTOPT_SSL) || m->may_tls) &&
+      !Curl_ssl_conn_config_match(m->data, conn, FALSE)) {
+     DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T
+                  " has different SSL parameters, cannot reuse",
+@@ -1364,7 +1368,8 @@ static bool url_attach_existing(struct Curl_easy *data,
+     (needle->scheme->protocol & PROTO_FAMILY_HTTP);
+ #endif
+ #endif
+-  match.req_tls = data->set.use_ssl >= CURLUSESSL_CONTROL;
++  match.require_tls = data->set.use_ssl >= CURLUSESSL_CONTROL;
++  match.may_tls = data->set.use_ssl > CURLUSESSL_NONE;
+ 
+   /* Find a connection in the pool that matches what "data + needle"
+    * requires. If a suitable candidate is found, it is attached to "data". */
+-- 
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 5ba881bd76..ae57776eab 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -23,6 +23,7 @@ SRC_URI = " \
     file://CVE-2026-6429.patch \
     file://CVE-2026-7168.patch \
     file://CVE-2026-4873.patch \
+    file://CVE-2026-8286.patch \
 "
 
 SRC_URI:append:class-nativesdk = " \
-- 
2.35.6



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [OE-core][wrynose][PATCH 2/8] curl: set CVE_STATUS for CVE-2026-8924
  2026-08-05  8:30 [OE-core][wrynose][PATCH 0/8] curl: Security fixes Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:30 ` [OE-core][wrynose][PATCH 1/8] curl: fix CVE-2026-8286 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-08-05  8:30 ` Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:30 ` [OE-core][wrynose][PATCH 3/8] curl: fix CVE-2026-8927 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-08-05  8:30 UTC (permalink / raw)
  To: openembedded-core

From: Deepak Rathore <deeratho@cisco.com>

Analysis:
- CVE-2026-8924 protects cookie domain handling when public
  suffix list support is enabled in curl. [1]
- Wrynose curl 8.19.0 is configured with --without-libpsl,
  so the PSL check path is not built by the default recipe. [2]
- Hence mark this CVE as not-applicable-config for the default
  Wrynose recipe configuration.

Reference:
[1] https://curl.se/docs/CVE-2026-8924.html
[2] https://git.openembedded.org/openembedded-core/tree/meta/recipes-support/curl/curl_8.19.0.bb?h=wrynose

Signed-off-by: Deepak Rathore <deeratho@cisco.com>
---
 meta/recipes-support/curl/curl_8.19.0.bb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index ae57776eab..5bec405eb7 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -35,6 +35,7 @@ SRC_URI[sha256sum] = "4eb41489790d19e190d7ac7e18e82857cdd68af8f4e66b292ced562d33
 # Curl has used many names over the years...
 CVE_PRODUCT = "haxx:curl haxx:libcurl curl:curl curl:libcurl libcurl:libcurl daniel_stenberg:curl"
 CVE_STATUS[CVE-2024-32928] = "ignored: CURLOPT_SSL_VERIFYPEER was disabled on google cloud services causing a potential man in the middle attack"
+CVE_STATUS[CVE-2026-8924] = "not-applicable-config: public suffix list support is disabled by the recipe with --without-libpsl"
 CVE_STATUS[CVE-2026-10536] = "${@bb.utils.contains('PACKAGECONFIG', 'nghttp2', 'unpatched', 'not-applicable-config: applicable only with HTTP/2', d)}"
 
 inherit autotools pkgconfig binconfig multilib_header ptest
-- 
2.35.6



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [OE-core][wrynose][PATCH 3/8] curl: fix CVE-2026-8927
  2026-08-05  8:30 [OE-core][wrynose][PATCH 0/8] curl: Security fixes Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:30 ` [OE-core][wrynose][PATCH 1/8] curl: fix CVE-2026-8286 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:30 ` [OE-core][wrynose][PATCH 2/8] curl: set CVE_STATUS for CVE-2026-8924 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-08-05  8:30 ` Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:30 ` [OE-core][wrynose][PATCH 4/8] curl: fix CVE-2026-8932 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-08-05  8:30 UTC (permalink / raw)
  To: openembedded-core

From: Deepak Rathore <deeratho@cisco.com>

This patch applies the upstream curl security fix backport for
CVE-2026-8927. The upstream fix commit is referenced in [1],
and the public curl advisory is referenced in [2]. The backported
commit link is also recorded in the embedded patch header.

[1] https://github.com/curl/curl/commit/5c225384b8d52c67ce8259c6e4203bc57aacb567
[2] https://curl.se/docs/CVE-2026-8927.html

Signed-off-by: Deepak Rathore <deeratho@cisco.com>
---
 .../curl/curl/CVE-2026-8927.patch             | 349 ++++++++++++++++++
 meta/recipes-support/curl/curl_8.19.0.bb      |   1 +
 2 files changed, 350 insertions(+)
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-8927.patch

diff --git a/meta/recipes-support/curl/curl/CVE-2026-8927.patch b/meta/recipes-support/curl/curl/CVE-2026-8927.patch
new file mode 100644
index 0000000000..653d908eb5
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-8927.patch
@@ -0,0 +1,349 @@
+From c3e9c57b5cd128f33250caf86184e23dc1e8c4e5 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel@haxx.se>
+Date: Thu, 30 Jul 2026 04:29:18 -0700
+Subject: [PATCH] url: detect proxy changes read from environment
+
+When a proxy is set from an environment variable, detect if that proxy
+is not the same as previously and flush state.
+
+Verified by test1647: verify changing proxy with env variables and make
+sure Digest state is flushed in the second use
+
+Closes #21666
+
+CVE: CVE-2026-8927
+Upstream-Status: Backport [https://github.com/curl/curl/commit/5c225384b8d52c67ce8259c6e4203bc57aacb567]
+
+Backport Changes:
+- Kept Wrynose test registry ordering and added only the new upstream
+  test1647/lib1647 regression test entries.
+- Added the explicit vauth/vauth.h include because Wrynose's url.c did
+  not already include the Digest cleanup prototype used by this fix.
+- Placed the env proxy comparison before parse_proxy() because Wrynose
+  frees the temporary proxy string immediately after parse_proxy() copies
+  it.
+
+(cherry picked from commit 5c225384b8d52c67ce8259c6e4203bc57aacb567)
+Signed-off-by: Deepak Rathore <deeratho@cisco.com>
+---
+ lib/url.c                  |  12 ++++
+ lib/urldata.h              |   1 +
+ tests/data/Makefile.am     |   2 +-
+ tests/data/test1647        | 103 +++++++++++++++++++++++++++++++
+ tests/libtest/Makefile.inc |   1 +
+ tests/libtest/lib1647.c    | 120 +++++++++++++++++++++++++++++++++++++
+ 6 files changed, 238 insertions(+), 1 deletion(-)
+ create mode 100644 tests/data/test1647
+ create mode 100644 tests/libtest/lib1647.c
+
+diff --git a/lib/url.c b/lib/url.c
+index 6c1375f8e3..1cd3955e64 100644
+--- a/lib/url.c
++++ b/lib/url.c
+@@ -99,6 +99,7 @@
+ #include "headers.h"
+ #include "curlx/strerr.h"
+ #include "curlx/strparse.h"
++#include "vauth/vauth.h"
+ 
+ /* And now for the protocols */
+ #include "ftp.h"
+@@ -326,6 +327,9 @@ CURLcode Curl_close(struct Curl_easy **datap)
+   Curl_freeset(data);
+   Curl_headers_cleanup(data);
+   Curl_netrc_cleanup(&data->state.netrc);
++#ifndef CURL_DISABLE_DIGEST_AUTH
++  curlx_free(data->state.envproxy);
++#endif
+   curlx_free(data);
+   return CURLE_OK;
+ }
+@@ -2354,6 +2358,14 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
+   if(proxy || socksproxy) {
+     long ptype = conn->http_proxy.proxytype;
+     if(proxy) {
++#ifndef CURL_DISABLE_DIGEST_AUTH
++      if(!Curl_safecmp(data->state.envproxy, proxy)) {
++        /* proxy changed */
++        Curl_auth_digest_cleanup(&data->state.proxydigest);
++        curlx_free(data->state.envproxy);
++        data->state.envproxy = curlx_strdup(proxy);
++      }
++#endif
+       result = parse_proxy(data, conn, proxy, ptype);
+       Curl_safefree(proxy); /* parse_proxy copies the proxy string */
+       if(result)
+diff --git a/lib/urldata.h b/lib/urldata.h
+index d71337c8f6..65005c4b34 100644
+--- a/lib/urldata.h
++++ b/lib/urldata.h
+@@ -959,6 +959,7 @@ struct UrlState {
+   void (*prev_signal)(int sig);
+ #endif
+ #ifndef CURL_DISABLE_DIGEST_AUTH
++  char *envproxy; /* last proxy string used for proxy-related state */
+   struct digestdata digest;      /* state data for host Digest auth */
+   struct digestdata proxydigest; /* state data for proxy Digest auth */
+ #endif
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index 1e84b26820..bcee03b938 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -218,7 +218,7 @@ test1620 test1621 test1622 test1623 test1624 \
+ \
+ test1630 test1631 test1632 test1633 test1634 test1635 test1636 test1637 \
+ \
+-test1640 test1641 test1642 test1643 \
++test1640 test1641 test1642 test1643 test1647 \
+ \
+ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \
+ test1658 \
+diff --git a/tests/data/test1647 b/tests/data/test1647
+new file mode 100644
+index 0000000000..a87487fa9f
+--- /dev/null
++++ b/tests/data/test1647
+@@ -0,0 +1,103 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++HTTP
++HTTP GET
++HTTP proxy
++HTTP proxy Digest auth
++multi
++</keywords>
++</info>
++
++# Server-side
++<reply>
++
++# this is returned first since we get no proxy-auth
++<data crlf="headers" nocheck="yes">
++HTTP/1.1 407 Authorization Required to proxy me my dear
++Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345"
++Content-Length: 33
++
++And you should ignore this data.
++</data>
++
++# then this is returned when we get proxy-auth
++<data1000 crlf="headers">
++HTTP/1.1 200 OK
++Content-Length: 21
++Server: no
++
++Nice proxy auth sir!
++</data1000>
++
++<connect crlf="headers">
++HTTP/1.1 401 OK
++Content-Length: 21
++Server: no
++
++Denied access. Leave
++</connect>
++
++</reply>
++
++# Client-side
++<client>
++<server>
++http
++https-proxy
++https
++</server>
++# tool is what to use instead of 'curl'
++<tool>
++lib%TESTNUMBER
++</tool>
++<features>
++!SSPI
++crypto
++proxy
++digest
++Debug
++</features>
++<setenv>
++http_proxy=%HOSTIP:%HTTPPORT
++https_proxy=https://%HOSTIP:%HTTPSPROXYPORT
++CURL_ENTROPY=99376
++</setenv>
++<name>
++HTTP proxy auth Digest, then change proxy with env var and do it again
++</name>
++<command>
++http://test.remote.example.com/path/%TESTNUMBER https://another.example.com:%HTTPSPORT/ daniel:monkey123 another:bump456
++</command>
++</client>
++
++# Verify data after the test has been "shot"
++<verify>
++<protocol crlf="headers">
++GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1
++Host: test.remote.example.com
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1
++Host: test.remote.example.com
++Proxy-Authorization: Digest username="daniel", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="7a1672891aff03248887b1a6674b8096"
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++</protocol>
++
++<proxy crlf="headers">
++CONNECT another.example.com:%HTTPSPORT HTTP/1.1
++Host: another.example.com:%HTTPSPORT
++Proxy-Connection: Keep-Alive
++
++</proxy>
++
++# CONNECT fails
++<errorcode>
++7
++</errorcode>
++</verify>
++</testcase>
+diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
+index 96b82bc059..e938b87bc5 100644
+--- a/tests/libtest/Makefile.inc
++++ b/tests/libtest/Makefile.inc
+@@ -100,6 +100,7 @@ TESTS_C = \
+   lib1582.c lib1588.c \
+   lib1591.c lib1592.c lib1593.c lib1594.c                     lib1597.c \
+   lib1598.c lib1599.c \
++  lib1647.c \
+   lib1662.c \
+   lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \
+   lib1908.c           lib1910.c lib1911.c lib1912.c lib1913.c \
+diff --git a/tests/libtest/lib1647.c b/tests/libtest/lib1647.c
+new file mode 100644
+index 0000000000..8060e1bfe9
+--- /dev/null
++++ b/tests/libtest/lib1647.c
+@@ -0,0 +1,120 @@
++/***************************************************************************
++ *                                  _   _ ____  _
++ *  Project                     ___| | | |  _ \| |
++ *                             / __| | | | |_) | |
++ *                            | (__| |_| |  _ <| |___
++ *                             \___|\___/|_| \_\_____|
++ *
++ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
++ *
++ * This software is licensed as described in the file COPYING, which
++ * you should have received as part of this distribution. The terms
++ * are also available at https://curl.se/docs/copyright.html.
++ *
++ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
++ * copies of the Software, and permit persons to whom the Software is
++ * furnished to do so, under the terms of the COPYING file.
++ *
++ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
++ * KIND, either express or implied.
++ *
++ * SPDX-License-Identifier: curl
++ *
++ ***************************************************************************/
++/*
++ * argv1 = the first URL
++ * argv2 = URL2
++ * argv3 = credentials 1
++ * argv4 = credentials 2
++ */
++
++#include "first.h"
++
++/* this is meant to pick up the proxy from the environment variable */
++static CURLcode init1647(CURL *curl, const char *url, const char *userpwd)
++{
++  CURLcode result = CURLE_OK;
++
++  res_easy_setopt(curl, CURLOPT_URL, url);
++  if(result)
++    goto init_failed;
++
++  res_easy_setopt(curl, CURLOPT_PROXYUSERPWD, userpwd);
++  if(result)
++    goto init_failed;
++
++  res_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
++  if(result)
++    goto init_failed;
++
++  res_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
++  if(result)
++    goto init_failed;
++
++  res_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
++  if(result)
++    goto init_failed;
++
++  res_easy_setopt(curl, CURLOPT_PROXY_SSL_VERIFYPEER, 0L);
++  if(result)
++    goto init_failed;
++
++  res_easy_setopt(curl, CURLOPT_PROXY_SSL_VERIFYHOST, 0L);
++  if(result)
++    goto init_failed;
++
++  res_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
++  if(result)
++    goto init_failed;
++
++  return CURLE_OK; /* success */
++
++init_failed:
++  return result; /* failure */
++}
++
++static CURLcode run1647(CURL *curl, const char *url, const char *userpwd)
++{
++  CURLcode result = CURLE_OK;
++
++  result = init1647(curl, url, userpwd);
++  if(result)
++    return result;
++
++  return curl_easy_perform(curl);
++}
++
++static CURLcode test_lib1647(const char *URL)
++{
++  CURLcode result = CURLE_OK;
++  CURL *curl = NULL;
++
++  res_global_init(CURL_GLOBAL_ALL);
++  if(result)
++    return result;
++
++  curl = curl_easy_init();
++  if(!curl) {
++    curl_mfprintf(stderr, "curl_easy_init() failed\n");
++    curl_global_cleanup();
++    return TEST_ERR_MAJOR_BAD;
++  }
++
++  start_test_timing();
++
++  curl_mprintf("--- First get '%s'\n", URL);
++  result = run1647(curl, URL, libtest_arg3);
++  if(result)
++    goto test_cleanup;
++
++  curl_mprintf("--- Then get '%s'\n", libtest_arg2);
++  result = run1647(curl, libtest_arg2, libtest_arg4);
++
++test_cleanup:
++
++  /* proper cleanup sequence - type PB */
++
++  curl_easy_cleanup(curl);
++  curl_global_cleanup();
++  return result;
++}
+-- 
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 5bec405eb7..193366f657 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -24,6 +24,7 @@ SRC_URI = " \
     file://CVE-2026-7168.patch \
     file://CVE-2026-4873.patch \
     file://CVE-2026-8286.patch \
+    file://CVE-2026-8927.patch \
 "
 
 SRC_URI:append:class-nativesdk = " \
-- 
2.35.6



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [OE-core][wrynose][PATCH 4/8] curl: fix CVE-2026-8932
  2026-08-05  8:30 [OE-core][wrynose][PATCH 0/8] curl: Security fixes Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (2 preceding siblings ...)
  2026-08-05  8:30 ` [OE-core][wrynose][PATCH 3/8] curl: fix CVE-2026-8927 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-08-05  8:30 ` Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:31 ` [OE-core][wrynose][PATCH 5/8] curl: fix CVE-2026-8458 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-08-05  8:30 UTC (permalink / raw)
  To: openembedded-core

From: Deepak Rathore <deeratho@cisco.com>

This patch applies the upstream curl security fix backport for
CVE-2026-8932. The upstream dependency commit is referenced
in [1], the upstream fix commit is referenced in [2], and the
public curl advisory is referenced in [3]. The individual
backported commit links are recorded in the embedded patch
headers.

[1] https://github.com/curl/curl/commit/67bf021e97bbebadade7e40217a4967042ac6a07
[2] https://github.com/curl/curl/commit/7541ae569d82fb308a5e2d94916027da4fa3ba3e
[3] https://curl.se/docs/CVE-2026-8932.html

Signed-off-by: Deepak Rathore <deeratho@cisco.com>
---
 .../curl/curl/CVE-2026-8932-dependent.patch   |   71 +
 .../curl/curl/CVE-2026-8932.patch             | 1148 +++++++++++++++++
 meta/recipes-support/curl/curl_8.19.0.bb      |    2 +
 3 files changed, 1221 insertions(+)
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-8932-dependent.patch
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-8932.patch

diff --git a/meta/recipes-support/curl/curl/CVE-2026-8932-dependent.patch b/meta/recipes-support/curl/curl/CVE-2026-8932-dependent.patch
new file mode 100644
index 0000000000..c64ced329a
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-8932-dependent.patch
@@ -0,0 +1,71 @@
+From a79f8dae1aa632155be430143b4685f0b91d1694 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel@haxx.se>
+Date: Wed, 6 May 2026 23:59:22 +0200
+Subject: [PATCH] mbedtls: null terminate the private key blob
+
+Unfortunately, mbedtls_pk_parse_key() requires the data to be
+null-terminated if the data is PEM encoded (even when provided the exact
+length), so this function needs to make a copy that has one.
+
+Reported-by: Elise Vance
+Closes #21515
+
+CVE: CVE-2026-8932
+Upstream-Status: Backport [https://github.com/curl/curl/commit/67bf021e97bbebadade7e40217a4967042ac6a07]
+
+Backport Changes:
+- Added as a prerequisite for upstream commit [1] because Wrynose curl
+  8.19.0 did not already contain this mbedTLS private-key blob fix.
+
+[1] https://github.com/curl/curl/commit/7541ae569d82fb308a5e2d94916027da4fa3ba3e
+
+(cherry picked from commit 67bf021e97bbebadade7e40217a4967042ac6a07)
+Signed-off-by: Deepak Rathore <deeratho@cisco.com>
+---
+ lib/vtls/mbedtls.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c
+index 2bac406c35..54f0ac9fd4 100644
+--- a/lib/vtls/mbedtls.c
++++ b/lib/vtls/mbedtls.c
+@@ -697,11 +697,17 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf,
+     }
+     else {
+       const struct curl_blob *ssl_key_blob = ssl_config->key_blob;
+-      const unsigned char *key_data =
+-        (const unsigned char *)ssl_key_blob->data;
+       const char *passwd = ssl_config->key_passwd;
++      /* Unfortunately, mbedtls_pk_parse_key() requires the data to be
++         null-terminated if the data is PEM encoded (even when provided the
++         exact length). */
++      unsigned char *newblob = curlx_memdup0(ssl_key_blob->data,
++                                             ssl_key_blob->len);
++      if(!newblob)
++        return CURLE_OUT_OF_MEMORY;
++
+ #if MBEDTLS_VERSION_NUMBER >= 0x04000000
+-      ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len,
++      ret = mbedtls_pk_parse_key(&backend->pk, newblob, ssl_key_blob->len,
+                                  (const unsigned char *)passwd,
+                                  passwd ? strlen(passwd) : 0);
+       if(ret == 0 &&
+@@ -713,7 +719,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf,
+                                  PSA_KEY_USAGE_SIGN_HASH)))
+         ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
+ #else
+-      ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len,
++      ret = mbedtls_pk_parse_key(&backend->pk, newblob, ssl_key_blob->len,
+                                  (const unsigned char *)passwd,
+                                  passwd ? strlen(passwd) : 0,
+                                  mbedtls_ctr_drbg_random,
+@@ -722,6 +728,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf,
+                        mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
+         ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
+ #endif
++      curlx_free(newblob);
+
+       if(ret) {
+         mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl/CVE-2026-8932.patch b/meta/recipes-support/curl/curl/CVE-2026-8932.patch
new file mode 100644
index 0000000000..8fec1a5adb
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-8932.patch
@@ -0,0 +1,1148 @@
+From c1cf1ddf723cdfcbd071394a6b4e73ea995e100a Mon Sep 17 00:00:00 2001
+From: Joshua Rogers <MegaManSec@users.noreply.github.com>
+Date: Tue, 14 Jul 2026 04:38:15 -0700
+Subject: [PATCH] tls: fix incomplete mTLS config in conn reuse and session
+ cache
+
+cert_type, key, key_type, key_passwd and key_blob lived in
+ssl_config_data but not in ssl_primary_config, so they were invisible to
+match_ssl_primary_config() and to the TLS session cache peer key.
+
+Two easy handles sharing a connection pool could reuse each other's
+authenticated connections when they differed only on SSLKEY, SSLKEYTYPE,
+KEYPASSWD, SSLCERTTYPE or SSLKEYBLOB. The second handle would silently
+inherit the first handle's authenticated identity.
+
+Promote all five fields into ssl_primary_config so the conn-reuse
+predicate and session cache key cover the complete client credential
+set. Also replace the fixed ":CCERT" session cache marker with the
+actual clientcert path so sessions are not shared across different
+client certificates.
+
+Verified by test 3303 and 3304
+
+Reported-By: Joshua Rogers (AISLE Research)
+Closes #21667
+
+CVE: CVE-2026-8932
+Upstream-Status: Backport [https://github.com/curl/curl/commit/7541ae569d82fb308a5e2d94916027da4fa3ba3e]
+
+Backport Changes:
+- Split prerequisite upstream commit [1] before this patch. This patch
+  now applies on top of that dependency and does not fold in the
+  mbedTLS private-key blob null-termination change.
+- Kept Wrynose Curl_safefree() helper usage and local result-variable
+  style while preserving the upstream mTLS credential matching and
+  session-cache key behavior.
+- Wrynose curl 8.19.0 does not have upstream Curl_peer. The
+  Curl_ssl_peer_key_build() helper keeps Wrynose connectdata and
+  conn_to_host/conn_to_port keying while adding the mTLS key fields.
+- Kept Wrynose test registry ordering and added only the new upstream
+  test3303/test3304 and unit3303/unit3304 regression test entries.
+
+[1] https://github.com/curl/curl/commit/67bf021e97bbebadade7e40217a4967042ac6a07
+
+(cherry picked from commit 7541ae569d82fb308a5e2d94916027da4fa3ba3e)
+Signed-off-by: Deepak Rathore <deeratho@cisco.com>
+---
+ lib/ldap.c              |   4 +-
+ lib/urldata.h           |  10 +--
+ lib/vssh/libssh.c       |   6 +-
+ lib/vssh/libssh2.c      |   2 +-
+ lib/vtls/gtls.c         |  14 ++--
+ lib/vtls/mbedtls.c      |  24 +++---
+ lib/vtls/openssl.c      |   7 +-
+ lib/vtls/rustls.c       |  13 ++--
+ lib/vtls/schannel.c     |  10 +--
+ lib/vtls/vtls.c         |  37 ++++++---
+ lib/vtls/vtls_scache.c  | 105 +++++++++++++++++++++-----
+ lib/vtls/vtls_scache.h  |  16 ++++
+ lib/vtls/wolfssl.c      |  14 ++--
+ tests/data/Makefile.am  |   3 +
+ tests/data/test3303     |  20 +++++
+ tests/data/test3304     |  20 +++++
+ tests/unit/Makefile.inc |   3 +-
+ tests/unit/unit3303.c   | 127 +++++++++++++++++++++++++++++++
+ tests/unit/unit3304.c   | 163 ++++++++++++++++++++++++++++++++++++++++
+ 19 files changed, 517 insertions(+), 81 deletions(-)
+ create mode 100644 tests/data/test3303
+ create mode 100644 tests/data/test3304
+ create mode 100644 tests/unit/unit3303.c
+ create mode 100644 tests/unit/unit3304.c
+
+diff --git a/lib/ldap.c b/lib/ldap.c
+index e223078b03..5bf76572c8 100644
+--- a/lib/ldap.c
++++ b/lib/ldap.c
+@@ -329,8 +329,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
+ #ifdef LDAP_OPT_X_TLS
+     if(conn->ssl_config.verifypeer) {
+       /* OpenLDAP SDK supports BASE64 files. */
+-      if(data->set.ssl.cert_type &&
+-         !curl_strequal(data->set.ssl.cert_type, "PEM")) {
++      if(data->set.ssl.primary.cert_type &&
++         !curl_strequal(data->set.ssl.primary.cert_type, "PEM")) {
+         failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type");
+         result = CURLE_SSL_CERTPROBLEM;
+         goto quit;
+diff --git a/lib/urldata.h b/lib/urldata.h
+index 65005c4b34..dce181d99d 100644
+--- a/lib/urldata.h
++++ b/lib/urldata.h
+@@ -230,9 +230,14 @@ struct ssl_primary_config {
+   char *signature_algorithms; /* list of signature algorithms to use */
+   char *pinned_key;
+   char *CRLfile;         /* CRL to check certificate revocation */
++  char *cert_type;       /* format for certificate (default: PEM) */
++  char *key;             /* private key filename */
++  char *key_type;        /* format for private key (default: PEM) */
++  char *key_passwd;      /* plain text private key password */
+   struct curl_blob *cert_blob;
+   struct curl_blob *ca_info_blob;
+   struct curl_blob *issuercert_blob;
++  struct curl_blob *key_blob;
+ #ifdef USE_TLS_SRP
+   char *username; /* TLS username (for, e.g., SRP) */
+   char *password; /* TLS password (for, e.g., SRP) */
+@@ -252,11 +257,6 @@ struct ssl_config_data {
+   long certverifyresult; /* result from the certificate verification */
+   curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */
+   void *fsslctxp;        /* parameter for call back */
+-  char *cert_type; /* format for certificate (default: PEM) */
+-  char *key; /* private key filename */
+-  struct curl_blob *key_blob;
+-  char *key_type; /* format for private key (default: PEM) */
+-  char *key_passwd; /* plain text private key password */
+   BIT(certinfo);     /* gather lots of certificate info */
+   BIT(earlydata);    /* use TLS 1.3 early data */
+   BIT(enable_beast); /* allow this flaw for interoperability's sake */
+diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c
+index 3a2a52e1f8..db767f7d00 100644
+--- a/lib/vssh/libssh.c
++++ b/lib/vssh/libssh.c
+@@ -791,7 +791,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data,
+   /* Two choices, (1) private key was given on CMD,
+    * (2) use the "default" keys. */
+   if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
+-    if(sshc->pubkey && !data->set.ssl.key_passwd) {
++    if(sshc->pubkey && !data->set.ssl.primary.key_passwd) {
+       rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, sshc->pubkey);
+       if(rc == SSH_AUTH_AGAIN)
+         return SSH_AGAIN;
+@@ -804,7 +804,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data,
+
+     rc = ssh_pki_import_privkey_file(data->
+                                      set.str[STRING_SSH_PRIVATE_KEY],
+-                                     data->set.ssl.key_passwd, NULL,
++                                     data->set.ssl.primary.key_passwd, NULL,
+                                      NULL, &sshc->privkey);
+     if(rc != SSH_OK) {
+       failf(data, "Could not load private key file %s",
+@@ -817,7 +817,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data,
+   }
+   else {
+     rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
+-                                     data->set.ssl.key_passwd);
++                                     data->set.ssl.primary.key_passwd);
+     if(rc == SSH_AUTH_AGAIN)
+       return SSH_AGAIN;
+
+diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c
+index 63f5735b83..3c25fe7448 100644
+--- a/lib/vssh/libssh2.c
++++ b/lib/vssh/libssh2.c
+@@ -1144,7 +1144,7 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data,
+       return CURLE_OUT_OF_MEMORY;
+     }
+
+-    sshc->passphrase = data->set.ssl.key_passwd;
++    sshc->passphrase = data->set.ssl.primary.key_passwd;
+     if(!sshc->passphrase)
+       sshc->passphrase = "";
+
+diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
+index a0a465c360..bfeb948542 100644
+--- a/lib/vtls/gtls.c
++++ b/lib/vtls/gtls.c
+@@ -988,10 +988,11 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
+       if(result)
+         return result;
+     }
+-    if(ssl_config->cert_type && curl_strequal(ssl_config->cert_type, "P12")) {
++    if(ssl_config->primary.cert_type &&
++       curl_strequal(ssl_config->primary.cert_type, "P12")) {
+       rc = gnutls_certificate_set_x509_simple_pkcs12_file(
+         gtls->shared_creds->creds, config->clientcert, GNUTLS_X509_FMT_DER,
+-        ssl_config->key_passwd ? ssl_config->key_passwd : "");
++        ssl_config->primary.key_passwd ? ssl_config->primary.key_passwd : "");
+       if(rc != GNUTLS_E_SUCCESS) {
+         failf(data,
+               "error reading X.509 potentially-encrypted key or certificate "
+@@ -1009,14 +1010,15 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
+       rc = gnutls_certificate_set_x509_key_file2(
+            gtls->shared_creds->creds,
+            config->clientcert,
+-           ssl_config->key ? ssl_config->key : config->clientcert,
+-           gnutls_do_file_type(ssl_config->cert_type),
+-           ssl_config->key_passwd,
++           ssl_config->primary.key ? ssl_config->primary.key :
++                                     config->clientcert,
++           gnutls_do_file_type(ssl_config->primary.cert_type),
++           ssl_config->primary.key_passwd,
+            supported_key_encryption_algorithms);
+       if(rc != GNUTLS_E_SUCCESS) {
+         failf(data,
+               "error reading X.509 %skey file: %s",
+-              ssl_config->key_passwd ? "potentially-encrypted " : "",
++              ssl_config->primary.key_passwd ? "potentially-encrypted " : "",
+               gnutls_strerror(rc));
+         return CURLE_SSL_CONNECT_ERROR;
+       }
+diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c
+index 54f0ac9fd4..d2199df884 100644
+--- a/lib/vtls/mbedtls.c
++++ b/lib/vtls/mbedtls.c
+@@ -484,7 +484,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf,
+   const char * const ssl_capath = conn_config->CApath;
+ #ifdef MBEDTLS_PEM_PARSE_C
+   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+-  const char * const ssl_cert_type = ssl_config->cert_type;
++  const char * const ssl_cert_type = ssl_config->primary.cert_type;
+ #endif
+   int ret = -1;
+   char errorbuf[128];
+@@ -579,7 +579,7 @@ static CURLcode mbed_load_clicert(struct Curl_cfilter *cf,
+   char * const ssl_cert = ssl_config->primary.clientcert;
+   const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
+ #ifdef MBEDTLS_PEM_PARSE_C
+-  const char * const ssl_cert_type = ssl_config->cert_type;
++  const char * const ssl_cert_type = ssl_config->primary.cert_type;
+ #endif
+   int ret = -1;
+   char errorbuf[128];
+@@ -660,12 +660,12 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf,
+
+   mbedtls_pk_init(&backend->pk);
+
+-  if(ssl_config->key || ssl_config->key_blob) {
+-    if(ssl_config->key) {
++  if(ssl_config->primary.key || ssl_config->primary.key_blob) {
++    if(ssl_config->primary.key) {
+ #ifdef MBEDTLS_FS_IO
+ #if MBEDTLS_VERSION_NUMBER >= 0x04000000
+-      ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key,
+-                                     ssl_config->key_passwd);
++      ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->primary.key,
++                                     ssl_config->primary.key_passwd);
+       if(ret == 0 &&
+          !(mbedtls_pk_can_do_psa(&backend->pk,
+                                  PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH),
+@@ -675,8 +675,8 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf,
+                                  PSA_KEY_USAGE_SIGN_HASH)))
+         ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
+ #else
+-      ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key,
+-                                     ssl_config->key_passwd,
++      ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->primary.key,
++                                     ssl_config->primary.key_passwd,
+                                      mbedtls_ctr_drbg_random,
+                                      &rng.drbg);
+       if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
+@@ -687,7 +687,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf,
+       if(ret) {
+         mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+         failf(data, "mbedTLS: error reading private key %s: (-0x%04X) %s",
+-              ssl_config->key, -ret, errorbuf);
++              ssl_config->primary.key, -ret, errorbuf);
+         return CURLE_SSL_CERTPROBLEM;
+       }
+ #else
+@@ -696,8 +696,8 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf,
+ #endif
+     }
+     else {
+-      const struct curl_blob *ssl_key_blob = ssl_config->key_blob;
+-      const char *passwd = ssl_config->key_passwd;
++      const struct curl_blob *ssl_key_blob = ssl_config->primary.key_blob;
++      const char *passwd = ssl_config->primary.key_passwd;
+       /* Unfortunately, mbedtls_pk_parse_key() requires the data to be
+          null-terminated if the data is PEM encoded (even when provided the
+          exact length). */
+@@ -931,7 +931,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf,
+ #endif
+     );
+
+-  if(ssl_config->key || ssl_config->key_blob) {
++  if(ssl_config->primary.key || ssl_config->primary.key_blob) {
+     mbedtls_ssl_conf_own_cert(&backend->config, &backend->clicert,
+                               &backend->pk);
+   }
+diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
+index 50bf1e0476..9cbab14b41 100644
+--- a/lib/vtls/openssl.c
++++ b/lib/vtls/openssl.c
+@@ -3724,7 +3724,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
+   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+   char * const ssl_cert = ssl_config->primary.clientcert;
+   const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
+-  const char * const ssl_cert_type = ssl_config->cert_type;
++  const char * const ssl_cert_type = ssl_config->primary.cert_type;
+   unsigned int ssl_version_min;
+   char error_buffer[256];
+
+@@ -3888,8 +3888,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
+   if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
+     result = client_cert(data, octx->ssl_ctx,
+                          ssl_cert, ssl_cert_blob, ssl_cert_type,
+-                         ssl_config->key, ssl_config->key_blob,
+-                         ssl_config->key_type, ssl_config->key_passwd);
++                         ssl_config->primary.key, ssl_config->primary.key_blob,
++                         ssl_config->primary.key_type,
++                         ssl_config->primary.key_passwd);
+     if(result)
+       /* failf() is already done in client_cert() */
+       return result;
+diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c
+index 061b444bce..564b10aa47 100644
+--- a/lib/vtls/rustls.c
++++ b/lib/vtls/rustls.c
+@@ -841,14 +841,14 @@ init_config_builder_client_auth(struct Curl_easy *data,
+   const struct rustls_certified_key *certified_key = NULL;
+   CURLcode result = CURLE_OK;
+
+-  if(conn_config->clientcert && !ssl_config->key) {
++  if(conn_config->clientcert && !ssl_config->primary.key) {
+     failf(data, "rustls: must provide key with certificate '%s'",
+           conn_config->clientcert);
+     return CURLE_SSL_CERTPROBLEM;
+   }
+-  else if(!conn_config->clientcert && ssl_config->key) {
++  else if(!conn_config->clientcert && ssl_config->primary.key) {
+     failf(data, "rustls: must provide certificate with key '%s'",
+-          ssl_config->key);
++          ssl_config->primary.key);
+     return CURLE_SSL_CERTPROBLEM;
+   }
+
+@@ -862,8 +862,9 @@ init_config_builder_client_auth(struct Curl_easy *data,
+     goto cleanup;
+   }
+
+-  if(!read_file_into(ssl_config->key, &key_contents)) {
+-    failf(data, "rustls: failed to read key file: '%s'", ssl_config->key);
++  if(!read_file_into(ssl_config->primary.key, &key_contents)) {
++    failf(data, "rustls: failed to read key file: '%s'",
++          ssl_config->primary.key);
+     result = CURLE_SSL_CERTPROBLEM;
+     goto cleanup;
+   }
+@@ -1053,7 +1054,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf,
+     }
+   }
+
+-  if(conn_config->clientcert || ssl_config->key) {
++  if(conn_config->clientcert || ssl_config->primary.key) {
+     result = init_config_builder_client_auth(data,
+                                              conn_config,
+                                              ssl_config,
+diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c
+index 650a0e38ca..a4f57c7c0a 100644
+--- a/lib/vtls/schannel.c
++++ b/lib/vtls/schannel.c
+@@ -414,8 +414,8 @@ static CURLcode get_client_cert(struct Curl_easy *data,
+       }
+     }
+
+-    if((fInCert || blob) && data->set.ssl.cert_type &&
+-       !curl_strequal(data->set.ssl.cert_type, "P12")) {
++    if((fInCert || blob) && data->set.ssl.primary.cert_type &&
++       !curl_strequal(data->set.ssl.primary.cert_type, "P12")) {
+       failf(data, "schannel: certificate format compatibility error "
+             "for %s",
+             blob ? "(memory blob)" : data->set.ssl.primary.clientcert);
+@@ -465,15 +465,15 @@ static CURLcode get_client_cert(struct Curl_easy *data,
+       datablob.pbData = (BYTE *)certdata;
+       datablob.cbData = (DWORD)certsize;
+
+-      if(data->set.ssl.key_passwd)
+-        pwd_len = strlen(data->set.ssl.key_passwd);
++      if(data->set.ssl.primary.key_passwd)
++        pwd_len = strlen(data->set.ssl.primary.key_passwd);
+       pszPassword = (WCHAR *)curlx_malloc(sizeof(WCHAR) * (pwd_len + 1));
+       if(pszPassword) {
+         int str_w_len = 0;
+         if(pwd_len > 0)
+           str_w_len = MultiByteToWideChar(CP_UTF8,
+                                           MB_ERR_INVALID_CHARS,
+-                                          data->set.ssl.key_passwd,
++                                          data->set.ssl.primary.key_passwd,
+                                           (int)pwd_len,
+                                           pszPassword, (int)(pwd_len + 1));
+
+diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
+index f7201d18d6..6dcc67dccd 100644
+--- a/lib/vtls/vtls.c
++++ b/lib/vtls/vtls.c
+@@ -204,6 +204,7 @@ static bool match_ssl_primary_config(struct Curl_easy *data,
+      blobcmp(c1->cert_blob, c2->cert_blob) &&
+      blobcmp(c1->ca_info_blob, c2->ca_info_blob) &&
+      blobcmp(c1->issuercert_blob, c2->issuercert_blob) &&
++     blobcmp(c1->key_blob, c2->key_blob) &&
+      Curl_safecmp(c1->CApath, c2->CApath) &&
+      Curl_safecmp(c1->CAfile, c2->CAfile) &&
+      Curl_safecmp(c1->issuercert, c2->issuercert) &&
+@@ -217,7 +218,11 @@ static bool match_ssl_primary_config(struct Curl_easy *data,
+      curl_strequal(c1->curves, c2->curves) &&
+      curl_strequal(c1->signature_algorithms, c2->signature_algorithms) &&
+      curl_strequal(c1->CRLfile, c2->CRLfile) &&
+-     curl_strequal(c1->pinned_key, c2->pinned_key))
++     curl_strequal(c1->pinned_key, c2->pinned_key) &&
++     curl_strequal(c1->cert_type, c2->cert_type) &&
++     Curl_safecmp(c1->key, c2->key) &&
++     curl_strequal(c1->key_type, c2->key_type) &&
++     !Curl_timestrcmp(c1->key_passwd, c2->key_passwd))
+     return TRUE;
+
+   return FALSE;
+@@ -252,6 +257,7 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source,
+   CLONE_BLOB(cert_blob);
+   CLONE_BLOB(ca_info_blob);
+   CLONE_BLOB(issuercert_blob);
++  CLONE_BLOB(key_blob);
+   CLONE_STRING(CApath);
+   CLONE_STRING(CAfile);
+   CLONE_STRING(issuercert);
+@@ -262,6 +268,10 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source,
+   CLONE_STRING(curves);
+   CLONE_STRING(signature_algorithms);
+   CLONE_STRING(CRLfile);
++  CLONE_STRING(cert_type);
++  CLONE_STRING(key);
++  CLONE_STRING(key_type);
++  CLONE_STRING(key_passwd);
+ #ifdef USE_TLS_SRP
+   CLONE_STRING(username);
+   CLONE_STRING(password);
+@@ -282,9 +292,14 @@ static void free_primary_ssl_config(struct ssl_primary_config *sslc)
+   Curl_safefree(sslc->cert_blob);
+   Curl_safefree(sslc->ca_info_blob);
+   Curl_safefree(sslc->issuercert_blob);
++  Curl_safefree(sslc->key_blob);
+   Curl_safefree(sslc->curves);
+   Curl_safefree(sslc->signature_algorithms);
+   Curl_safefree(sslc->CRLfile);
++  Curl_safefree(sslc->cert_type);
++  Curl_safefree(sslc->key);
++  Curl_safefree(sslc->key_type);
++  Curl_safefree(sslc->key_passwd);
+ #ifdef USE_TLS_SRP
+   Curl_safefree(sslc->username);
+   Curl_safefree(sslc->password);
+@@ -336,12 +351,12 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data)
+   sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME];
+   sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD];
+ #endif
+-  sslc->cert_type = data->set.str[STRING_CERT_TYPE];
+-  sslc->key = data->set.str[STRING_KEY];
+-  sslc->key_type = data->set.str[STRING_KEY_TYPE];
+-  sslc->key_passwd = data->set.str[STRING_KEY_PASSWD];
++  sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE];
++  sslc->primary.key = data->set.str[STRING_KEY];
++  sslc->primary.key_type = data->set.str[STRING_KEY_TYPE];
++  sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD];
+   sslc->primary.clientcert = data->set.str[STRING_CERT];
+-  sslc->key_blob = data->set.blobs[BLOB_KEY];
++  sslc->primary.key_blob = data->set.blobs[BLOB_KEY];
+
+ #ifndef CURL_DISABLE_PROXY
+   sslc = &data->set.proxy_ssl;
+@@ -377,12 +392,12 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data)
+   sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY];
+   sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY];
+   sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY];
+-  sslc->cert_type = data->set.str[STRING_CERT_TYPE_PROXY];
+-  sslc->key = data->set.str[STRING_KEY_PROXY];
+-  sslc->key_type = data->set.str[STRING_KEY_TYPE_PROXY];
+-  sslc->key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY];
++  sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE_PROXY];
++  sslc->primary.key = data->set.str[STRING_KEY_PROXY];
++  sslc->primary.key_type = data->set.str[STRING_KEY_TYPE_PROXY];
++  sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY];
+   sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY];
+-  sslc->key_blob = data->set.blobs[BLOB_KEY_PROXY];
++  sslc->primary.key_blob = data->set.blobs[BLOB_KEY_PROXY];
+ #ifdef USE_TLS_SRP
+   sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY];
+   sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY];
+diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c
+index 673abcfe0d..75e5b89553 100644
+--- a/lib/vtls/vtls_scache.c
++++ b/lib/vtls/vtls_scache.c
+@@ -50,6 +50,7 @@
+ struct Curl_ssl_scache_peer {
+   char *ssl_peer_key;      /* id for peer + relevant TLS configuration */
+   char *clientcert;
++  char *key_passwd;
+   char *srp_username;
+   char *srp_password;
+   struct Curl_llist sessions;
+@@ -123,6 +124,48 @@ out:
+   return r;
+ }
+
++static CURLcode cf_ssl_peer_key_add_mtls(struct dynbuf *buf,
++                                         struct ssl_primary_config *ssl,
++                                         bool *is_local)
++{
++  CURLcode result = CURLE_OK;
++  if(ssl->clientcert && ssl->clientcert[0]) {
++    result = cf_ssl_peer_key_add_path(buf, "CCERT", ssl->clientcert, is_local);
++    if(result)
++      goto out;
++  }
++  if(ssl->key && ssl->key[0]) {
++    result = cf_ssl_peer_key_add_path(buf, "KEY", ssl->key, is_local);
++    if(result)
++      goto out;
++  }
++  if(ssl->key_blob) {
++    result = cf_ssl_peer_key_add_hash(buf, "KEYBlob", ssl->key_blob);
++    if(result)
++      goto out;
++  }
++  if(ssl->cert_type && ssl->cert_type[0]) {
++    size_t i;
++    result = curlx_dyn_add(buf, ":CT-");
++    for(i = 0; !result && ssl->cert_type[i]; i++) {
++      char c = Curl_raw_toupper(ssl->cert_type[i]);
++      result = curlx_dyn_addn(buf, &c, 1);
++    }
++    if(result)
++      goto out;
++  }
++  if(ssl->key_type && ssl->key_type[0]) {
++    size_t i;
++    result = curlx_dyn_add(buf, ":KT-");
++    for(i = 0; !result && ssl->key_type[i]; i++) {
++      char c = Curl_raw_toupper(ssl->key_type[i]);
++      result = curlx_dyn_addn(buf, &c, 1);
++    }
++  }
++out:
++  return result;
++}
++
+ #define CURL_SSLS_LOCAL_SUFFIX     ":L"
+ #define CURL_SSLS_GLOBAL_SUFFIX    ":G"
+
+@@ -134,12 +177,12 @@ static bool cf_ssl_peer_key_is_global(const char *peer_key)
+          (peer_key[len - 2] == ':');
+ }
+
+-CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
+-                                const struct ssl_peer *peer,
+-                                const char *tls_id,
+-                                char **ppeer_key)
++CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl,
++                                 const struct ssl_peer *peer,
++                                 const struct connectdata *conn,
++                                 const char *tls_id,
++                                 char **ppeer_key)
+ {
+-  struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
+   struct dynbuf buf;
+   size_t key_len;
+   bool is_local = FALSE;
+@@ -187,13 +230,13 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
+       goto out;
+   }
+   if(!ssl->verifypeer || !ssl->verifyhost) {
+-    if(cf->conn->bits.conn_to_host) {
+-      r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
++    if(conn && conn->bits.conn_to_host) {
++      r = curlx_dyn_addf(&buf, ":CHOST-%s", conn->conn_to_host.name);
+       if(r)
+         goto out;
+     }
+-    if(cf->conn->bits.conn_to_port) {
+-      r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
++    if(conn && conn->bits.conn_to_port) {
++      r = curlx_dyn_addf(&buf, ":CPORT-%d", conn->conn_to_port);
+       if(r)
+         goto out;
+     }
+@@ -260,11 +303,9 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
+       goto out;
+   }
+
+-  if(ssl->clientcert && ssl->clientcert[0]) {
+-    r = curlx_dyn_add(&buf, ":CCERT");
+-    if(r)
+-      goto out;
+-  }
++  r = cf_ssl_peer_key_add_mtls(&buf, ssl, &is_local);
++  if(r)
++    goto out;
+ #ifdef USE_TLS_SRP
+   if(ssl->username || ssl->password) {
+     r = curlx_dyn_add(&buf, ":SRP-AUTH");
+@@ -295,6 +336,15 @@ out:
+   return r;
+ }
+
++CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
++                                const struct ssl_peer *peer,
++                                const char *tls_id,
++                                char **ppeer_key)
++{
++  struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
++  return Curl_ssl_peer_key_build(ssl, peer, cf->conn, tls_id, ppeer_key);
++}
++
+ struct Curl_ssl_scache {
+   unsigned int magic;
+   struct Curl_ssl_scache_peer *peers;
+@@ -402,6 +452,7 @@ static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer)
+   }
+   peer->sobj_free = NULL;
+   Curl_safefree(peer->clientcert);
++  Curl_safefree(peer->key_passwd);
+ #ifdef USE_TLS_SRP
+   Curl_safefree(peer->srp_username);
+   Curl_safefree(peer->srp_password);
+@@ -430,8 +481,8 @@ static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer)
+    * - its peer key is not yet known, because sessions were
+    *   imported using only the salt+hmac
+    * - the peer key is global, e.g. carrying no relative paths */
+-  peer->exportable = (!peer->clientcert && !peer->srp_username &&
+-                      !peer->srp_password &&
++  peer->exportable = (!peer->clientcert && !peer->key_passwd &&
++                      !peer->srp_username && !peer->srp_password &&
+                       (!peer->ssl_peer_key ||
+                        cf_ssl_peer_key_is_global(peer->ssl_peer_key)));
+ }
+@@ -440,6 +491,7 @@ static CURLcode
+ cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
+                         const char *ssl_peer_key,
+                         const char *clientcert,
++                        const char *key_passwd,
+                         const char *srp_username,
+                         const char *srp_password,
+                         const unsigned char *salt,
+@@ -468,6 +520,11 @@ cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
+     if(!peer->clientcert)
+       goto out;
+   }
++  if(key_passwd) {
++    peer->key_passwd = curlx_strdup(key_passwd);
++    if(!peer->key_passwd)
++      goto out;
++  }
+   if(srp_username) {
+     peer->srp_username = curlx_strdup(srp_username);
+     if(!peer->srp_username)
+@@ -598,7 +655,7 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
+                                      struct ssl_primary_config *conn_config)
+ {
+   if(!conn_config) {
+-    if(peer->clientcert)
++    if(peer->clientcert || peer->key_passwd)
+       return FALSE;
+ #ifdef USE_TLS_SRP
+     if(peer->srp_username || peer->srp_password)
+@@ -608,6 +665,8 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
+   }
+   else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert))
+     return FALSE;
++  if(Curl_timestrcmp(peer->key_passwd, conn_config->key_passwd))
++    return FALSE;
+ #ifdef USE_TLS_SRP
+   if(Curl_timestrcmp(peer->srp_username, conn_config->username) ||
+      Curl_timestrcmp(peer->srp_password, conn_config->password))
+@@ -734,13 +793,21 @@ static CURLcode cf_ssl_add_peer(struct Curl_easy *data,
+
+   peer = cf_ssl_get_free_peer(scache);
+   if(peer) {
++    char buffer[64];
+     const char *ccert = conn_config ? conn_config->clientcert : NULL;
++    const char *kpasswd = conn_config ? conn_config->key_passwd : NULL;
+     const char *username = NULL, *password = NULL;
+ #ifdef USE_TLS_SRP
+     username = conn_config ? conn_config->username : NULL;
+     password = conn_config ? conn_config->password : NULL;
+ #endif
+-    result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert,
++    if(!ccert && conn_config && conn_config->cert_blob) {
++      /* when using a client cert blob, create a name for it */
++      curl_msnprintf(buffer, sizeof(buffer),
++                     "cert-%p", conn_config->cert_blob->data);
++      ccert = buffer; /* data is strduped by cf_ssl_scache_peer_init */
++    }
++    result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, kpasswd,
+                                      username, password, NULL, NULL);
+     if(result)
+       goto out;
+@@ -1115,7 +1182,7 @@ CURLcode Curl_ssl_session_import(struct Curl_easy *data,
+     if(!peer) {
+       peer = cf_ssl_get_free_peer(scache);
+       if(peer) {
+-        r = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL,
++        r = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, NULL,
+                                     NULL, NULL, salt, hmac);
+         if(r)
+           goto out;
+diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h
+index b9db0f19b3..d33f2bb1a7 100644
+--- a/lib/vtls/vtls_scache.h
++++ b/lib/vtls/vtls_scache.h
+@@ -66,6 +66,22 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
+                                 const char *tls_id,
+                                 char **ppeer_key);
+
++/**
++ * Like Curl_ssl_peer_key_make() but takes the primary config and peer
++ * descriptors directly, without requiring a Curl_cfilter. Exposed for
++ * unit testing.
++ * @param ssl      the primary SSL config to key on
++ * @param peer     the peer the filter wants to talk to
++ * @param conn     connection data for connect-to keying, or NULL
++ * @param tls_id   identifier of TLS implementation for sessions
++ * @param ppeer_key on successful return, the key generated
++ */
++CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl,
++                                 const struct ssl_peer *peer,
++                                 const struct connectdata *conn,
++                                 const char *tls_id,
++                                 char **ppeer_key);
++
+ /* Return if there is a session cache shall be used.
+  * An ssl session might not be configured or not available for
+  * "connect-only" transfers.
+diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c
+index aa841a754a..74bd247e68 100644
+--- a/lib/vtls/wolfssl.c
++++ b/lib/vtls/wolfssl.c
+@@ -919,10 +919,10 @@ static CURLcode client_certificate(struct Curl_easy *data,
+ #ifndef NO_FILESYSTEM
+   if(ssl_config->primary.cert_blob || ssl_config->primary.clientcert) {
+     const char *cert_file = ssl_config->primary.clientcert;
+-    const char *key_file = ssl_config->key;
++    const char *key_file = ssl_config->primary.key;
+     const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
+-    const struct curl_blob *key_blob = ssl_config->key_blob;
+-    int file_type = wssl_do_file_type(ssl_config->cert_type);
++    const struct curl_blob *key_blob = ssl_config->primary.key_blob;
++    int file_type = wssl_do_file_type(ssl_config->primary.cert_type);
+     int rc;
+
+     switch(file_type) {
+@@ -953,7 +953,7 @@ static CURLcode client_certificate(struct Curl_easy *data,
+       key_file = cert_file;
+     }
+     else
+-      file_type = wssl_do_file_type(ssl_config->key_type);
++      file_type = wssl_do_file_type(ssl_config->primary.key_type);
+
+     rc = key_blob ?
+       wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
+@@ -967,8 +967,8 @@ static CURLcode client_certificate(struct Curl_easy *data,
+ #else /* NO_FILESYSTEM */
+   if(ssl_config->primary.cert_blob) {
+     const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
+-    const struct curl_blob *key_blob = ssl_config->key_blob;
+-    int file_type = wssl_do_file_type(ssl_config->cert_type);
++    const struct curl_blob *key_blob = ssl_config->primary.key_blob;
++    int file_type = wssl_do_file_type(ssl_config->primary.cert_type);
+     int rc;
+
+     switch(file_type) {
+@@ -993,7 +993,7 @@ static CURLcode client_certificate(struct Curl_easy *data,
+     if(!key_blob)
+       key_blob = cert_blob;
+     else
+-      file_type = wssl_do_file_type(ssl_config->key_type);
++      file_type = wssl_do_file_type(ssl_config->primary.key_type);
+
+     if(wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
+                                          (long)key_blob->len,
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index 4fd7dfe912..2217666382 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -282,6 +282,9 @@ test3100 test3101 test3102 test3103 test3104 test3105 \
+ \
+ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 test3208 \
+ test3209 test3210 test3211 test3212 test3213 test3214 test3215 test3216 \
++\
++test3303 test3304 \
++\
+ test4000 test4001
+
+ EXTRA_DIST = $(TESTCASES) DISABLED data-xml1 data320.html \
+diff --git a/tests/data/test3303 b/tests/data/test3303
+new file mode 100644
+index 0000000000..697049f013
+--- /dev/null
++++ b/tests/data/test3303
+@@ -0,0 +1,20 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++unittest
++TLS
++mTLS
++</keywords>
++</info>
++
++# Client-side
++<client>
++<features>
++unittest
++</features>
++<name>
++conn-reuse match distinguishes mTLS key, cert_type, key_type and key_passwd fields
++</name>
++</client>
++</testcase>
+diff --git a/tests/data/test3304 b/tests/data/test3304
+new file mode 100644
+index 0000000000..4380c0819f
+--- /dev/null
++++ b/tests/data/test3304
+@@ -0,0 +1,20 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++unittest
++TLS
++mTLS
++</keywords>
++</info>
++
++# Client-side
++<client>
++<features>
++unittest
++</features>
++<name>
++TLS session cache peer key discriminates on mTLS key, key_type and cert_type fields
++</name>
++</client>
++</testcase>
+diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc
+index 149636cbcc..bba30f6706 100644
+--- a/tests/unit/Makefile.inc
++++ b/tests/unit/Makefile.inc
+@@ -43,4 +43,5 @@ TESTS_C = \
+   unit1979.c unit1980.c \
+   unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \
+   unit3200.c                                             unit3205.c \
+-  unit3211.c unit3212.c unit3213.c unit3214.c            unit3216.c
++  unit3211.c unit3212.c unit3213.c unit3214.c            unit3216.c \
++  unit3303.c unit3304.c
+diff --git a/tests/unit/unit3303.c b/tests/unit/unit3303.c
+new file mode 100644
+index 0000000000..41bced542d
+--- /dev/null
++++ b/tests/unit/unit3303.c
+@@ -0,0 +1,127 @@
++/***************************************************************************
++ *                                  _   _ ____  _
++ *  Project                     ___| | | |  _ \| |
++ *                             / __| | | | |_) | |
++ *                            | (__| |_| |  _ <| |___
++ *                             \___|\___/|_| \_\_____|
++ *
++ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
++ *
++ * This software is licensed as described in the file COPYING, which
++ * you should have received as part of this distribution. The terms
++ * are also available at https://curl.se/docs/copyright.html.
++ *
++ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
++ * copies of the Software, and permit persons to whom the Software is
++ * furnished to do so, under the terms of the COPYING file.
++ *
++ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
++ * KIND, either express or implied.
++ *
++ * SPDX-License-Identifier: curl
++ *
++ ***************************************************************************/
++#include "unitcheck.h"
++#include "urldata.h"
++
++#ifdef USE_SSL
++#include "vtls/vtls.h"
++#endif
++
++static CURLcode test_unit3303(const char *arg)
++{
++  UNITTEST_BEGIN_SIMPLE
++
++#ifdef USE_SSL
++  {
++    CURL *curl;
++    struct connectdata *conn;
++    struct ssl_primary_config *primary;
++    char *saved;
++    static char alt_passwd[] = "wrong";
++    static char alt_key[]    = "other.key";
++    static char alt_ktype[]  = "DER";
++    static char alt_ctype[]  = "P12";
++
++    curl_global_init(CURL_GLOBAL_ALL);
++    curl = curl_easy_init();
++    if(!curl) {
++      curl_global_cleanup();
++      goto unit_test_abort;
++    }
++
++    curl_easy_setopt(curl, CURLOPT_SSLCERT, "client.pem");
++    curl_easy_setopt(curl, CURLOPT_SSLKEY, "client.key");
++    curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "secret");
++    curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
++    curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM");
++
++    if(Curl_ssl_easy_config_complete((struct Curl_easy *)curl)) {
++      curl_easy_cleanup(curl);
++      curl_global_cleanup();
++      goto unit_test_abort;
++    }
++
++    conn = curlx_calloc(1, sizeof(*conn));
++    if(!conn || Curl_ssl_conn_config_init((struct Curl_easy *)curl, conn)) {
++      if(conn)
++        Curl_ssl_conn_config_cleanup(conn);
++      curlx_free(conn);
++      curl_easy_cleanup(curl);
++      curl_global_cleanup();
++      goto unit_test_abort;
++    }
++
++    /* Baseline: identical config must match. */
++    fail_unless(Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
++                                           FALSE),
++                "identical mTLS config should match");
++
++    primary = &((struct Curl_easy *)curl)->set.ssl.primary;
++
++    /* Different key_passwd must not match. */
++    saved = primary->key_passwd;
++    primary->key_passwd = alt_passwd;
++    fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
++                                            FALSE),
++                "different key_passwd must not reuse conn");
++    primary->key_passwd = saved;
++
++    /* Different key path must not match. */
++    saved = primary->key;
++    primary->key = alt_key;
++    fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
++                                            FALSE),
++                "different key must not reuse conn");
++    primary->key = saved;
++
++    /* Different key type must not match. */
++    saved = primary->key_type;
++    primary->key_type = alt_ktype;
++    fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
++                                            FALSE),
++                "different key_type must not reuse conn");
++    primary->key_type = saved;
++
++    /* Different cert type must not match. */
++    saved = primary->cert_type;
++    primary->cert_type = alt_ctype;
++    fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
++                                            FALSE),
++                "different cert_type must not reuse conn");
++    primary->cert_type = saved;
++
++    /* All fields restored: must match again. */
++    fail_unless(Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
++                                           FALSE),
++                "restored mTLS config should match");
++
++    Curl_ssl_conn_config_cleanup(conn);
++    curlx_free(conn);
++    curl_easy_cleanup(curl);
++    curl_global_cleanup();
++  }
++#endif /* USE_SSL */
++
++  UNITTEST_END_SIMPLE
++}
+diff --git a/tests/unit/unit3304.c b/tests/unit/unit3304.c
+new file mode 100644
+index 0000000000..4defa1e426
+--- /dev/null
++++ b/tests/unit/unit3304.c
+@@ -0,0 +1,163 @@
++/***************************************************************************
++ *                                  _   _ ____  _
++ *  Project                     ___| | | |  _ \| |
++ *                             / __| | | | |_) | |
++ *                            | (__| |_| |  _ <| |___
++ *                             \___|\___/|_| \_\_____|
++ *
++ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
++ *
++ * This software is licensed as described in the file COPYING, which
++ * you should have received as part of this distribution. The terms
++ * are also available at https://curl.se/docs/copyright.html.
++ *
++ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
++ * copies of the Software, and permit persons to whom the Software is
++ * furnished to do so, under the terms of the COPYING file.
++ *
++ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
++ * KIND, either express or implied.
++ *
++ * SPDX-License-Identifier: curl
++ *
++ ***************************************************************************/
++
++/* Unit tests for TLS session cache peer key discrimination on mTLS fields.
++ * Verifies that Curl_ssl_peer_key_build() produces distinct keys when two
++ * handles differ only on key, key_type or cert_type.  key_passwd is NOT
++ * embedded in the peer key; it is compared separately at session lookup via
++ * cf_ssl_scache_match_auth(), following the same pattern as SRP
++ * credentials. */
++
++#include "unitcheck.h"
++#include "urldata.h"
++
++#ifdef USE_SSL
++#include "vtls/vtls.h"
++#include "vtls/vtls_scache.h"
++#endif
++
++static CURLcode test_unit3304(const char *arg)
++{
++  UNITTEST_BEGIN_SIMPLE
++
++#ifdef USE_SSL
++  {
++    struct ssl_peer peer;
++    struct ssl_primary_config ssl;
++    char *key1 = NULL;
++    char *key2 = NULL;
++    static char base_hostname[] = "example.com";
++    static char base_cert[]     = "client.pem";
++    static char base_key[]      = "client.key";
++    static char base_passwd[]   = "secret";
++    static char base_ctype[]    = "PEM";
++    static char base_ktype[]    = "PEM";
++    static char alt_key[]       = "other.key";
++    static char alt_ktype[]     = "DER";
++    static char alt_ctype[]     = "P12";
++    static char lc_ctype[]      = "pem";
++    static char lc_ktype[]      = "pem";
++
++    memset(&peer, 0, sizeof(peer));
++    peer.hostname = base_hostname;
++    peer.port = 443;
++    peer.transport = TRNSPRT_TCP;
++
++    memset(&ssl, 0, sizeof(ssl));
++    ssl.verifypeer = TRUE;
++    ssl.verifyhost = TRUE;
++    ssl.clientcert = base_cert;
++    ssl.key        = base_key;
++    ssl.key_passwd = base_passwd;
++    ssl.cert_type  = base_ctype;
++    ssl.key_type   = base_ktype;
++
++    /* Baseline: same config produces same key. */
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
++                "peer key build failed");
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
++                "peer key build failed");
++    fail_unless(key1 && key2 && !strcmp(key1, key2),
++                "identical config should produce identical peer key");
++    curlx_free(key1); key1 = NULL;
++    curlx_free(key2); key2 = NULL;
++
++    /* key_passwd is NOT in the peer key: lookup uses timing-safe comparison
++     * via cf_ssl_scache_match_auth(), same as SRP credentials. */
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
++                "peer key build failed");
++    ssl.key_passwd = NULL;
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
++                "peer key build failed");
++    fail_unless(key1 && key2 && !strcmp(key1, key2),
++                "key_passwd must not affect the peer key");
++    curlx_free(key1); key1 = NULL;
++    curlx_free(key2); key2 = NULL;
++    ssl.key_passwd = base_passwd;
++
++    /* Different key path must produce a different peer key. */
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
++                "peer key build failed");
++    ssl.key = alt_key;
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
++                "peer key build failed");
++    fail_unless(key1 && key2 && strcmp(key1, key2),
++                "different key must produce different peer key");
++    curlx_free(key1); key1 = NULL;
++    curlx_free(key2); key2 = NULL;
++    ssl.key = base_key;
++
++    /* Different key_type must produce a different peer key. */
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
++                "peer key build failed");
++    ssl.key_type = alt_ktype;
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
++                "peer key build failed");
++    fail_unless(key1 && key2 && strcmp(key1, key2),
++                "different key_type must produce different peer key");
++    curlx_free(key1); key1 = NULL;
++    curlx_free(key2); key2 = NULL;
++    ssl.key_type = base_ktype;
++
++    /* Different cert_type must produce a different peer key. */
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
++                "peer key build failed");
++    ssl.cert_type = alt_ctype;
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
++                "peer key build failed");
++    fail_unless(key1 && key2 && strcmp(key1, key2),
++                "different cert_type must produce different peer key");
++    curlx_free(key1); key1 = NULL;
++    curlx_free(key2); key2 = NULL;
++    ssl.cert_type = base_ctype;
++
++    /* cert_type is case-insensitive: "PEM" and "pem" must produce the
++     * same peer key, consistent with the conn-reuse comparison. */
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
++                "peer key build failed");
++    ssl.cert_type = lc_ctype;
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
++                "peer key build failed");
++    fail_unless(key1 && key2 && !strcmp(key1, key2),
++                "cert_type case must not affect peer key");
++    curlx_free(key1); key1 = NULL;
++    curlx_free(key2); key2 = NULL;
++    ssl.cert_type = base_ctype;
++
++    /* key_type is case-insensitive: "PEM" and "pem" must produce the
++     * same peer key. */
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
++                "peer key build failed");
++    ssl.key_type = lc_ktype;
++    fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
++                "peer key build failed");
++    fail_unless(key1 && key2 && !strcmp(key1, key2),
++                "key_type case must not affect peer key");
++    curlx_free(key1); key1 = NULL;
++    curlx_free(key2); key2 = NULL;
++  }
++#endif /* USE_SSL */
++
++  UNITTEST_END_SIMPLE
++}
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 193366f657..994b1cff28 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -25,6 +25,8 @@ SRC_URI = " \
     file://CVE-2026-4873.patch \
     file://CVE-2026-8286.patch \
     file://CVE-2026-8927.patch \
+    file://CVE-2026-8932-dependent.patch \
+    file://CVE-2026-8932.patch \
 "
 
 SRC_URI:append:class-nativesdk = " \
-- 
2.35.6



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [OE-core][wrynose][PATCH 5/8] curl: fix CVE-2026-8458
  2026-08-05  8:30 [OE-core][wrynose][PATCH 0/8] curl: Security fixes Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (3 preceding siblings ...)
  2026-08-05  8:30 ` [OE-core][wrynose][PATCH 4/8] curl: fix CVE-2026-8932 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-08-05  8:31 ` Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:31 ` [OE-core][wrynose][PATCH 6/8] curl: fix CVE-2026-11856 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-08-05  8:31 UTC (permalink / raw)
  To: openembedded-core

From: Deepak Rathore <deeratho@cisco.com>

This patch applies the upstream curl security fix backport for
CVE-2026-8458. The upstream fix commit is referenced in [1],
and the public curl advisory is referenced in [2]. The backported
commit link is also recorded in the embedded patch header.

[1] https://github.com/curl/curl/commit/5e99b73cf441d9c369768b9cd48b5389b9a2503d
[2] https://curl.se/docs/CVE-2026-8458.html

Signed-off-by: Deepak Rathore <deeratho@cisco.com>
---
 .../curl/curl/CVE-2026-8458.patch             | 202 ++++++++++++++++++
 meta/recipes-support/curl/curl_8.19.0.bb      |   1 +
 2 files changed, 203 insertions(+)
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-8458.patch

diff --git a/meta/recipes-support/curl/curl/CVE-2026-8458.patch b/meta/recipes-support/curl/curl/CVE-2026-8458.patch
new file mode 100644
index 0000000000..6340f5305f
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-8458.patch
@@ -0,0 +1,202 @@
+From 01ce94b67888e6efa4196247302e59cf68e77b2d Mon Sep 17 00:00:00 2001
+From: Stefan Eissing <stefan@eissing.org>
+Date: Mon, 13 Jul 2026 23:13:37 -0700
+Subject: [PATCH] creds: add sasl service name
+
+The SASL service name, used in authentication, is part of curl's credentials
+when authenticating to a server/proxy. Make it part of `struct Curl_creds`.
+
+Change code to use `creds` to obtain a service name. By tying creds used
+to the connection, connection reuse is also only allowed when the service
+name matches.
+
+Closes #21585
+
+CVE: CVE-2026-8458
+Upstream-Status: Backport [https://github.com/curl/curl/commit/5e99b73cf441d9c369768b9cd48b5389b9a2503d]
+
+Backport Changes:
+- Wrynose curl 8.19.0 does not have upstream struct Curl_creds.
+  This backport stores the optional SASL service name on the existing
+  connectdata/proxy_info structures and compares it during connection
+  reuse for the same security behavior.
+- Omitted the upstream unit1304 Curl_creds_create() signature
+  adjustment because Wrynose 8.19.0 does not contain Curl_creds.
+
+(cherry picked from commit 5e99b73cf441d9c369768b9cd48b5389b9a2503d)
+Signed-off-by: Deepak Rathore <deeratho@cisco.com>
+---
+ lib/url.c     | 61 ++++++++++++++++++++++++++++++++++++++++++++++-----
+ lib/urldata.h |  2 ++
+ 2 files changed, 58 insertions(+), 5 deletions(-)
+
+diff --git a/lib/url.c b/lib/url.c
+index 6c1375f8e3..35f467daf2 100644
+--- a/lib/url.c
++++ b/lib/url.c
+@@ -535,12 +535,15 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
+   Curl_safefree(conn->socks_proxy.user);
+   Curl_safefree(conn->http_proxy.passwd);
+   Curl_safefree(conn->socks_proxy.passwd);
++  Curl_safefree(conn->http_proxy.sasl_service_name);
++  Curl_safefree(conn->socks_proxy.sasl_service_name);
+   Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */
+   Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */
+ #endif
+   Curl_safefree(conn->user);
+   Curl_safefree(conn->passwd);
+   Curl_safefree(conn->sasl_authzid);
++  Curl_safefree(conn->sasl_service_name);
+   Curl_safefree(conn->options);
+   Curl_safefree(conn->oauth_bearer);
+   Curl_safefree(conn->host.rawalloc); /* hostname buffer */
+@@ -593,7 +596,9 @@ static bool proxy_info_matches(const struct proxy_info *data,
+      curl_strequal(data->host.name, needle->host.name)) {
+ 
+     if(Curl_timestrcmp(data->user, needle->user) ||
+-       Curl_timestrcmp(data->passwd, needle->passwd))
++       Curl_timestrcmp(data->passwd, needle->passwd) ||
++       Curl_timestrcmp(data->sasl_service_name,
++                       needle->sasl_service_name))
+       return FALSE;
+     return TRUE;
+   }
+@@ -1035,6 +1040,8 @@ static bool url_match_auth(struct connectdata *conn,
+     if(Curl_timestrcmp(m->needle->user, conn->user) ||
+        Curl_timestrcmp(m->needle->passwd, conn->passwd) ||
+        Curl_timestrcmp(m->needle->sasl_authzid, conn->sasl_authzid) ||
++       Curl_timestrcmp(m->needle->sasl_service_name,
++                       conn->sasl_service_name) ||
+        Curl_timestrcmp(m->needle->oauth_bearer, conn->oauth_bearer)) {
+       /* one of them was different */
+       return FALSE;
+@@ -1116,7 +1123,9 @@ static bool url_match_auth_ntlm(struct connectdata *conn,
+      partway through a handshake!) */
+   if(m->want_ntlm_http) {
+     if(Curl_timestrcmp(m->needle->user, conn->user) ||
+-       Curl_timestrcmp(m->needle->passwd, conn->passwd)) {
++       Curl_timestrcmp(m->needle->passwd, conn->passwd) ||
++       Curl_timestrcmp(m->needle->sasl_service_name,
++                       conn->sasl_service_name)) {
+       /* we prefer a credential match, but this is at least a connection
+          that can be reused and "upgraded" to NTLM if it does
+          not have any auth ongoing. */
+@@ -1147,7 +1156,9 @@ static bool url_match_auth_ntlm(struct connectdata *conn,
+     if(Curl_timestrcmp(m->needle->http_proxy.user,
+                        conn->http_proxy.user) ||
+        Curl_timestrcmp(m->needle->http_proxy.passwd,
+-                       conn->http_proxy.passwd))
++                       conn->http_proxy.passwd) ||
++       Curl_timestrcmp(m->needle->http_proxy.sasl_service_name,
++                       conn->http_proxy.sasl_service_name))
+       return FALSE;
+   }
+   else if(conn->proxy_ntlm_state != NTLMSTATE_NONE) {
+@@ -1188,7 +1199,9 @@ static bool url_match_auth_nego(struct connectdata *conn,
+      so that we can reuse Negotiate connections if possible. */
+   if(m->want_nego_http) {
+     if(Curl_timestrcmp(m->needle->user, conn->user) ||
+-       Curl_timestrcmp(m->needle->passwd, conn->passwd))
++       Curl_timestrcmp(m->needle->passwd, conn->passwd) ||
++       Curl_timestrcmp(m->needle->sasl_service_name,
++                       conn->sasl_service_name))
+       return FALSE;
+   }
+   else if(conn->http_negotiate_state != GSS_AUTHNONE) {
+@@ -1207,7 +1220,9 @@ static bool url_match_auth_nego(struct connectdata *conn,
+     if(Curl_timestrcmp(m->needle->http_proxy.user,
+                        conn->http_proxy.user) ||
+        Curl_timestrcmp(m->needle->http_proxy.passwd,
+-                       conn->http_proxy.passwd))
++                       conn->http_proxy.passwd) ||
++       Curl_timestrcmp(m->needle->http_proxy.sasl_service_name,
++                       conn->http_proxy.sasl_service_name))
+       return FALSE;
+   }
+   else if(conn->proxy_negotiate_state != GSS_AUTHNONE) {
+@@ -3196,6 +3211,11 @@ static void url_conn_reuse_adjust(struct Curl_easy *data,
+     needle->user = NULL;
+     needle->passwd = NULL;
+   }
++  if(needle->sasl_service_name) {
++    curlx_free(conn->sasl_service_name);
++    conn->sasl_service_name = needle->sasl_service_name;
++    needle->sasl_service_name = NULL;
++  }
+ 
+ #ifndef CURL_DISABLE_PROXY
+   conn->bits.proxy_user_passwd = needle->bits.proxy_user_passwd;
+@@ -3214,6 +3234,17 @@ static void url_conn_reuse_adjust(struct Curl_easy *data,
+     needle->http_proxy.passwd = NULL;
+     needle->socks_proxy.passwd = NULL;
+   }
++  if(needle->http_proxy.sasl_service_name ||
++     needle->socks_proxy.sasl_service_name) {
++    curlx_free(conn->http_proxy.sasl_service_name);
++    curlx_free(conn->socks_proxy.sasl_service_name);
++    conn->http_proxy.sasl_service_name =
++      needle->http_proxy.sasl_service_name;
++    conn->socks_proxy.sasl_service_name =
++      needle->socks_proxy.sasl_service_name;
++    needle->http_proxy.sasl_service_name = NULL;
++    needle->socks_proxy.sasl_service_name = NULL;
++  }
+ #endif
+ 
+   /* Finding a connection for reuse in the cpool matches, among other
+@@ -3283,6 +3314,15 @@ static CURLcode url_create_needle(struct Curl_easy *data,
+     }
+   }
+ 
++  if(data->set.str[STRING_SERVICE_NAME]) {
++    needle->sasl_service_name =
++      curlx_strdup(data->set.str[STRING_SERVICE_NAME]);
++    if(!needle->sasl_service_name) {
++      result = CURLE_OUT_OF_MEMORY;
++      goto out;
++    }
++  }
++
+   if(data->set.str[STRING_BEARER]) {
+     needle->oauth_bearer = curlx_strdup(data->set.str[STRING_BEARER]);
+     if(!needle->oauth_bearer) {
+@@ -3310,6 +3350,17 @@ static CURLcode url_create_needle(struct Curl_easy *data,
+   if(result)
+     goto out;
+ 
++  if(data->set.str[STRING_PROXY_SERVICE_NAME]) {
++    result = Curl_setstropt(&needle->http_proxy.sasl_service_name,
++                            data->set.str[STRING_PROXY_SERVICE_NAME]);
++    if(result)
++      goto out;
++    result = Curl_setstropt(&needle->socks_proxy.sasl_service_name,
++                            data->set.str[STRING_PROXY_SERVICE_NAME]);
++    if(result)
++      goto out;
++  }
++
+   /*************************************************************
+    * If the protocol is using SSL and HTTP proxy is used, we set
+    * the tunnel_proxy bit.
+diff --git a/lib/urldata.h b/lib/urldata.h
+index 6c6c83969c..1c369a54f2 100644
+--- a/lib/urldata.h
++++ b/lib/urldata.h
+@@ -586,6 +586,7 @@ struct proxy_info {
+   uint8_t proxytype; /* what kind of proxy that is in use */
+   char *user;    /* proxy username string, allocated */
+   char *passwd;  /* proxy password string, allocated */
++  char *sasl_service_name; /* SASL service name, allocated */
+ };
+ 
+ /*
+@@ -628,6 +629,7 @@ struct connectdata {
+   char *passwd;  /* password string, allocated */
+   char *options; /* options string, allocated */
+   char *sasl_authzid;     /* authorization identity string, allocated */
++  char *sasl_service_name; /* SASL service name, allocated */
+   char *oauth_bearer; /* OAUTH2 bearer, allocated */
+   struct curltime created; /* creation time */
+   struct curltime lastused; /* when returned to the connection poolas idle */
+-- 
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 994b1cff28..33ccb73eb3 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -27,6 +27,7 @@ SRC_URI = " \
     file://CVE-2026-8927.patch \
     file://CVE-2026-8932-dependent.patch \
     file://CVE-2026-8932.patch \
+    file://CVE-2026-8458.patch \
 "
 
 SRC_URI:append:class-nativesdk = " \
-- 
2.35.6



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [OE-core][wrynose][PATCH 6/8] curl: fix CVE-2026-11856
  2026-08-05  8:30 [OE-core][wrynose][PATCH 0/8] curl: Security fixes Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (4 preceding siblings ...)
  2026-08-05  8:31 ` [OE-core][wrynose][PATCH 5/8] curl: fix CVE-2026-8458 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-08-05  8:31 ` Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:31 ` [OE-core][wrynose][PATCH 7/8] curl: set CVE_STATUS for CVE-2026-9547 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:31 ` [OE-core][wrynose][PATCH 8/8] curl: set CVE_STATUS for CVE-2026-12064 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  7 siblings, 0 replies; 9+ messages in thread
From: Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-08-05  8:31 UTC (permalink / raw)
  To: openembedded-core

From: Deepak Rathore <deeratho@cisco.com>

This patch applies the upstream curl security backports for
CVE-2026-11856. The main fix commit is referenced in [1], the
Digest proxy-state follow-up is referenced in [2], and the public
curl advisory is referenced in [3]. The individual backported commit
links are recorded in the embedded patch headers.

[1] https://github.com/curl/curl/commit/5c6b4880357ab3e72967c1c45cae0f96ffabc535
[2] https://github.com/curl/curl/commit/7ec25148c06b049d3252172ff17fae85b19c54c9
[3] https://curl.se/docs/CVE-2026-11856.html

Signed-off-by: Deepak Rathore <deeratho@cisco.com>
---
 .../curl/curl/CVE-2026-11856_p1.patch         | 372 ++++++++++++++++++
 .../curl/curl/CVE-2026-11856_p2.patch         |  72 ++++
 meta/recipes-support/curl/curl_8.19.0.bb      |   2 +
 3 files changed, 446 insertions(+)
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-11856_p1.patch
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-11856_p2.patch

diff --git a/meta/recipes-support/curl/curl/CVE-2026-11856_p1.patch b/meta/recipes-support/curl/curl/CVE-2026-11856_p1.patch
new file mode 100644
index 0000000000..80fb18a7c1
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-11856_p1.patch
@@ -0,0 +1,372 @@
+From 3349fbcb7876456cadc3cc3a8d3e8aff29406906 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel@haxx.se>
+Date: Mon, 13 Jul 2026 23:17:23 -0700
+Subject: [PATCH] digest: flush state on origin or credential change
+
+Verified by test 1686
+
+Closes #21944
+
+CVE: CVE-2026-11856
+Upstream-Status: Backport [https://github.com/curl/curl/commit/5c6b4880357ab3e72967c1c45cae0f96ffabc535]
+
+Backport Changes:
+- Wrynose curl 8.19.0 does not have upstream Curl_peer or
+  struct Curl_creds. This backport stores the host Digest origin
+  and user/password on digestdata, then flushes stale host Digest
+  state before reuse when either value changes.
+- Kept Wrynose test-list ordering and added only the upstream
+  test1686/lib1686 regression coverage.
+
+(cherry picked from commit 5c6b4880357ab3e72967c1c45cae0f96ffabc535)
+Signed-off-by: Deepak Rathore <deeratho@cisco.com>
+---
+ lib/http_digest.c          | 50 ++++++++++++++++++++
+ lib/urldata.h              |  3 ++
+ lib/vauth/digest.c         |  3 ++
+ lib/vauth/digest_sspi.c    |  4 ++
+ tests/data/Makefile.am     |  2 +-
+ tests/data/test1686        | 84 +++++++++++++++++++++++++++++++++
+ tests/libtest/Makefile.inc |  1 +
+ tests/libtest/lib1686.c    | 96 ++++++++++++++++++++++++++++++++++++++
+ 8 files changed, 242 insertions(+), 1 deletion(-)
+ create mode 100644 tests/data/test1686
+ create mode 100644 tests/libtest/lib1686.c
+
+diff --git a/lib/http_digest.c b/lib/http_digest.c
+index f5b20c5e9d..29bfa3b0b3 100644
+--- a/lib/http_digest.c
++++ b/lib/http_digest.c
+@@ -29,6 +29,7 @@
+ #include "strcase.h"
+ #include "vauth/vauth.h"
+ #include "http_digest.h"
++#include "curlx/strdup.h"
+ #include "curlx/strparse.h"
+ 
+ /* Test example headers:
+@@ -38,6 +39,49 @@ Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598"
+ 
+ */
+ 
++static CURLcode digest_update_origin(struct Curl_easy *data,
++                                     struct digestdata *digest,
++                                     const char *userp,
++                                     const char *passwdp)
++{
++  char *origin;
++  char *authuser;
++  char *authpasswd;
++  bool flush = FALSE;
++
++  origin = curl_maprintf("%s:%d:%s", data->conn->scheme->name,
++                         data->conn->remote_port, data->conn->host.name);
++  if(!origin)
++    return CURLE_OUT_OF_MEMORY;
++
++  authuser = curlx_strdup(userp);
++  authpasswd = curlx_strdup(passwdp);
++  if(!authuser || !authpasswd) {
++    curlx_free(origin);
++    curlx_free(authuser);
++    curlx_free(authpasswd);
++    return CURLE_OUT_OF_MEMORY;
++  }
++
++  if(digest->authorigin && Curl_timestrcmp(digest->authorigin, origin))
++    flush = TRUE;
++  else if(digest->authuser && Curl_timestrcmp(digest->authuser, userp))
++    flush = TRUE;
++  else if(digest->authpasswd && Curl_timestrcmp(digest->authpasswd, passwdp))
++    flush = TRUE;
++
++  if(flush)
++    Curl_auth_digest_cleanup(digest);
++
++  Curl_safefree(digest->authorigin);
++  Curl_safefree(digest->authuser);
++  Curl_safefree(digest->authpasswd);
++  digest->authorigin = origin;
++  digest->authuser = authuser;
++  digest->authpasswd = authpasswd;
++  return CURLE_OK;
++}
++
+ CURLcode Curl_input_digest(struct Curl_easy *data,
+                            bool proxy,
+                            const char *header) /* rest of the *-authenticate:
+@@ -114,6 +158,12 @@ CURLcode Curl_output_digest(struct Curl_easy *data,
+   if(!passwdp)
+     passwdp = "";
+ 
++  if(!proxy) {
++    result = digest_update_origin(data, digest, userp, passwdp);
++    if(result)
++      return result;
++  }
++
+ #ifdef USE_WINDOWS_SSPI
+   have_chlg = !!digest->input_token;
+ #else
+diff --git a/lib/urldata.h b/lib/urldata.h
+index 455ed7f996..113f6e7102 100644
+--- a/lib/urldata.h
++++ b/lib/urldata.h
+@@ -283,6 +283,9 @@ struct ssl_general_config {
+ #ifndef CURL_DISABLE_DIGEST_AUTH
+ /* Struct used for Digest challenge-response authentication */
+ struct digestdata {
++  char *authorigin; /* origin used for the host Digest state */
++  char *authuser;   /* user used for the host Digest state */
++  char *authpasswd; /* password used for the host Digest state */
+ #ifdef USE_WINDOWS_SSPI
+   BYTE *input_token;
+   size_t input_token_len;
+diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c
+index 9609390387..1feee07764 100644
+--- a/lib/vauth/digest.c
++++ b/lib/vauth/digest.c
+@@ -1028,6 +1028,9 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
+  */
+ void Curl_auth_digest_cleanup(struct digestdata *digest)
+ {
++  Curl_safefree(digest->authorigin);
++  Curl_safefree(digest->authuser);
++  Curl_safefree(digest->authpasswd);
+   Curl_safefree(digest->nonce);
+   Curl_safefree(digest->cnonce);
+   Curl_safefree(digest->realm);
+diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c
+index f29e569cd1..4688a7b5c5 100644
+--- a/lib/vauth/digest_sspi.c
++++ b/lib/vauth/digest_sspi.c
+@@ -636,6 +636,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
+  */
+ void Curl_auth_digest_cleanup(struct digestdata *digest)
+ {
++  Curl_safefree(digest->authorigin);
++  Curl_safefree(digest->authuser);
++  Curl_safefree(digest->authpasswd);
++
+   /* Free the input token */
+   Curl_safefree(digest->input_token);
+ 
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index f9d20a9cc8..89eae33ca0 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -226,7 +226,7 @@ test1660 test1661 test1662 test1663 test1664 test1665 \
+ \
+ test1670 test1671 \
+ \
+-test1680 test1681 test1682 test1683 \
++test1680 test1681 test1682 test1683 test1686 \
+ \
+ test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \
+ test1708 test1709 test1710 test1711 test1712 \
+diff --git a/tests/data/test1686 b/tests/data/test1686
+new file mode 100644
+index 0000000000..2d419ad608
+--- /dev/null
++++ b/tests/data/test1686
+@@ -0,0 +1,84 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++HTTP
++Digest
++</keywords>
++</info>
++
++<reply>
++<data crlf="headers" nocheck="yes">
++HTTP/1.1 401 Authorization Required
++Server: Apache/1.3.27 (Darwin) PHP/4.1.2
++WWW-Authenticate: Digest realm="my-backyard", nonce="314156295"
++Content-Length: 26
++
++This is not the real page
++</data>
++
++# This is supposed to be returned when the server gets a
++# Authorization: Digest line passed-in from the client
++<data1000 crlf="headers">
++HTTP/1.1 200 OK
++Server: Apache/1.3.27 (Darwin) PHP/4.1.2
++Content-Type: text/html; charset=iso-8859-1
++Content-Length: 23
++
++This IS the real page!
++</data1000>
++
++</reply>
++
++<client>
++<features>
++!SSPI
++crypto
++digest
++</features>
++<server>
++http
++</server>
++<name>
++HTTP Digest to different origins and switching credentials
++</name>
++<tool>
++lib%TESTNUMBER
++</tool>
++<command>
++%HOSTIP %HTTPPORT
++</command>
++</client>
++
++<verify>
++<protocol crlf="headers">
++GET /api HTTP/1.1
++Host: first.test:%HTTPPORT
++Accept: */*
++
++GET /api HTTP/1.1
++Host: first.test:%HTTPPORT
++Authorization: Digest username="alice", realm="my-backyard", nonce="314156295", uri="/api", response="4ecc00e567c37a9d537727890c2e5b32"
++Accept: */*
++
++GET /hook HTTP/1.1
++Host: second.test:%HTTPPORT
++Accept: */*
++
++GET /hook HTTP/1.1
++Host: second.test:%HTTPPORT
++Authorization: Digest username="alice", realm="my-backyard", nonce="314156295", uri="/hook", response="d3a7738fb6a23f5543fb8dacc0f0f253"
++Accept: */*
++
++GET /hook HTTP/1.1
++Host: second.test:%HTTPPORT
++Accept: */*
++
++GET /hook HTTP/1.1
++Host: second.test:%HTTPPORT
++Authorization: Digest username="bob", realm="my-backyard", nonce="314156295", uri="/hook", response="777e68eddb77294d9cbd6134973cbbab"
++Accept: */*
++
++</protocol>
++</verify>
++</testcase>
+diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
+index e938b87bc5..ef23ca4e28 100644
+--- a/tests/libtest/Makefile.inc
++++ b/tests/libtest/Makefile.inc
+@@ -102,6 +102,7 @@ TESTS_C = \
+   lib1598.c lib1599.c \
+   lib1647.c \
+   lib1662.c \
++  lib1686.c \
+   lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \
+   lib1908.c           lib1910.c lib1911.c lib1912.c lib1913.c \
+   lib1915.c lib1916.c           lib1918.c lib1919.c lib1920.c \
+diff --git a/tests/libtest/lib1686.c b/tests/libtest/lib1686.c
+new file mode 100644
+index 0000000000..e457012bb9
+--- /dev/null
++++ b/tests/libtest/lib1686.c
+@@ -0,0 +1,96 @@
++/***************************************************************************
++ *                                  _   _ ____  _
++ *  Project                     ___| | | |  _ \| |
++ *                             / __| | | | |_) | |
++ *                            | (__| |_| |  _ <| |___
++ *                             \___|\___/|_| \_\_____|
++ *
++ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
++ *
++ * This software is licensed as described in the file COPYING, which
++ * you should have received as part of this distribution. The terms
++ * are also available at https://curl.se/docs/copyright.html.
++ *
++ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
++ * copies of the Software, and permit persons to whom the Software is
++ * furnished to do so, under the terms of the COPYING file.
++ *
++ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
++ * KIND, either express or implied.
++ *
++ * SPDX-License-Identifier: curl
++ *
++ ***************************************************************************/
++#include "first.h"
++
++static size_t devnull_1686(char *p, size_t s, size_t n, void *u)
++{
++  (void)p;
++  (void)u;
++  return s * n;
++}
++
++#define FIRSTHOST "first.test"
++#define SECONDHOST "second.test"
++
++static CURLcode test_lib1686(const char *hostip)
++{
++  CURL *curl = NULL;
++  CURLcode result = CURLE_OK;
++  const char *httpport = libtest_arg2;
++  char firsturl[100];
++  char secondurl[100];
++  char firstres[100];
++  char secondres[100];
++  struct curl_slist *host = NULL;
++  struct curl_slist *host2 = NULL;
++
++  if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
++    curl_mfprintf(stderr, "curl_global_init() failed\n");
++    return TEST_ERR_MAJOR_BAD;
++  }
++
++  /* create strings for CURLOPT_RESOLVE */
++  curl_msnprintf(firstres, sizeof(firstres), "%s:%s:%s",
++                 FIRSTHOST, httpport, hostip);
++  curl_msnprintf(secondres, sizeof(secondres), "%s:%s:%s",
++                 SECONDHOST, httpport, hostip);
++
++  /* create URLs */
++  curl_msnprintf(firsturl, sizeof(firsturl), "http://%s:%s/api",
++                 FIRSTHOST, httpport);
++  curl_msnprintf(secondurl, sizeof(secondurl), "http://%s:%s/hook",
++                 SECONDHOST, httpport);
++
++  host = curl_slist_append(NULL, firstres);
++  if(!host)
++    goto test_cleanup;
++  host2 = curl_slist_append(host, secondres);
++  if(!host2)
++    goto test_cleanup;
++  host = host2;
++
++  curl = curl_easy_init();
++  if(curl) {
++    easy_setopt(curl, CURLOPT_RESOLVE, host);
++    easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
++    easy_setopt(curl, CURLOPT_USERPWD, "alice:bond");
++    easy_setopt(curl, CURLOPT_WRITEFUNCTION, devnull_1686);
++
++    easy_setopt(curl, CURLOPT_URL, firsturl);
++    result = curl_easy_perform(curl);
++
++    easy_setopt(curl, CURLOPT_URL, secondurl);
++    result = curl_easy_perform(curl);
++
++    easy_setopt(curl, CURLOPT_USERPWD, "bob:secret");
++    easy_setopt(curl, CURLOPT_URL, secondurl);
++    result = curl_easy_perform(curl);
++  }
++
++test_cleanup:
++  curl_easy_cleanup(curl);
++  curl_global_cleanup();
++  curl_slist_free_all(host);
++  return result;
++}
+-- 
+2.35.6
diff --git a/meta/recipes-support/curl/curl/CVE-2026-11856_p2.patch b/meta/recipes-support/curl/curl/CVE-2026-11856_p2.patch
new file mode 100644
index 0000000000..4e3dc45186
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-11856_p2.patch
@@ -0,0 +1,72 @@
+From 0d7f5b5037e2878ee68ca8317445df1240d117bf Mon Sep 17 00:00:00 2001
+From: alhudz <al.hudz.k@gmail.com>
+Date: Thu, 30 Jul 2026 22:18:13 -0700
+Subject: [PATCH] digest: flush proxy state on proxy or credential change
+
+Closes #21951
+
+CVE: CVE-2026-11856
+Upstream-Status: Backport [https://github.com/curl/curl/commit/7ec25148c06b049d3252172ff17fae85b19c54c9]
+
+Backport Changes:
+- Wrynose curl 8.19.0 does not have upstream Curl_peer or Curl_creds.
+  This backport extends the string-based Digest origin and credential
+  tracking introduced by upstream commit [1] to the proxy path, so
+  proxy Digest state is flushed when the proxy destination or proxy
+  credentials change.
+
+[1] https://github.com/curl/curl/commit/5c6b4880357ab3e72967c1c45cae0f96ffabc535
+
+(cherry picked from commit 7ec25148c06b049d3252172ff17fae85b19c54c9)
+Signed-off-by: Deepak Rathore <deeratho@cisco.com>
+---
+ lib/http_digest.c | 20 +++++++++++++-------
+ 1 file changed, 13 insertions(+), 7 deletions(-)
+
+diff --git a/lib/http_digest.c b/lib/http_digest.c
+index 29bfa3b0b3..06e4028f02 100644
+--- a/lib/http_digest.c
++++ b/lib/http_digest.c
+@@ -41,6 +41,7 @@ Proxy-Authenticate: Digest realm="testrealm", nonce="1053604598"
+ 
+ static CURLcode digest_update_origin(struct Curl_easy *data,
+                                      struct digestdata *digest,
++                                     bool proxy,
+                                      const char *userp,
+                                      const char *passwdp)
+ {
+@@ -49,8 +50,15 @@ static CURLcode digest_update_origin(struct Curl_easy *data,
+   char *authpasswd;
+   bool flush = FALSE;
+ 
+-  origin = curl_maprintf("%s:%d:%s", data->conn->scheme->name,
+-                         data->conn->remote_port, data->conn->host.name);
++#ifndef CURL_DISABLE_PROXY
++  if(proxy)
++    origin = curl_maprintf("%d:%d:%s", data->conn->http_proxy.proxytype,
++                           data->conn->http_proxy.port,
++                           data->conn->http_proxy.host.name);
++  else
++#endif
++    origin = curl_maprintf("%s:%d:%s", data->conn->scheme->name,
++                           data->conn->remote_port, data->conn->host.name);
+   if(!origin)
+     return CURLE_OUT_OF_MEMORY;
+ 
+@@ -158,11 +166,9 @@ CURLcode Curl_output_digest(struct Curl_easy *data,
+   if(!passwdp)
+     passwdp = "";
+ 
+-  if(!proxy) {
+-    result = digest_update_origin(data, digest, userp, passwdp);
+-    if(result)
+-      return result;
+-  }
++  result = digest_update_origin(data, digest, proxy, userp, passwdp);
++  if(result)
++    return result;
+ 
+ #ifdef USE_WINDOWS_SSPI
+   have_chlg = !!digest->input_token;
+-- 
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 33ccb73eb3..b0edc5b960 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -28,6 +28,8 @@ SRC_URI = " \
     file://CVE-2026-8932-dependent.patch \
     file://CVE-2026-8932.patch \
     file://CVE-2026-8458.patch \
+    file://CVE-2026-11856_p1.patch \
+    file://CVE-2026-11856_p2.patch \
 "
 
 SRC_URI:append:class-nativesdk = " \
-- 
2.35.6



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [OE-core][wrynose][PATCH 7/8] curl: set CVE_STATUS for CVE-2026-9547
  2026-08-05  8:30 [OE-core][wrynose][PATCH 0/8] curl: Security fixes Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (5 preceding siblings ...)
  2026-08-05  8:31 ` [OE-core][wrynose][PATCH 6/8] curl: fix CVE-2026-11856 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-08-05  8:31 ` Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  2026-08-05  8:31 ` [OE-core][wrynose][PATCH 8/8] curl: set CVE_STATUS for CVE-2026-12064 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  7 siblings, 0 replies; 9+ messages in thread
From: Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-08-05  8:31 UTC (permalink / raw)
  To: openembedded-core

From: Deepak Rathore <deeratho@cisco.com>

Analysis:
- CVE-2026-9547 is tied to the libssh backend. [1]
- Wrynose curl 8.19.0 does not enable or expose the libssh
  backend in the recipe. [2]
- Hence mark this CVE as not-applicable-config for the default
  Wrynose recipe configuration.

Reference:
[1] https://curl.se/docs/CVE-2026-9547.html
[2] https://git.openembedded.org/openembedded-core/tree/meta/recipes-support/curl/curl_8.19.0.bb?h=wrynose

Signed-off-by: Deepak Rathore <deeratho@cisco.com>
---
 meta/recipes-support/curl/curl_8.19.0.bb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index b0edc5b960..4c3e31d438 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -43,6 +43,7 @@ CVE_PRODUCT = "haxx:curl haxx:libcurl curl:curl curl:libcurl libcurl:libcurl dan
 CVE_STATUS[CVE-2024-32928] = "ignored: CURLOPT_SSL_VERIFYPEER was disabled on google cloud services causing a potential man in the middle attack"
 CVE_STATUS[CVE-2026-8924] = "not-applicable-config: public suffix list support is disabled by the recipe with --without-libpsl"
 CVE_STATUS[CVE-2026-10536] = "${@bb.utils.contains('PACKAGECONFIG', 'nghttp2', 'unpatched', 'not-applicable-config: applicable only with HTTP/2', d)}"
+CVE_STATUS[CVE-2026-9547] = "not-applicable-config: vulnerable libssh backend is not enabled by the recipe"
 
 inherit autotools pkgconfig binconfig multilib_header ptest
 
-- 
2.35.6



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [OE-core][wrynose][PATCH 8/8] curl: set CVE_STATUS for CVE-2026-12064
  2026-08-05  8:30 [OE-core][wrynose][PATCH 0/8] curl: Security fixes Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
                   ` (6 preceding siblings ...)
  2026-08-05  8:31 ` [OE-core][wrynose][PATCH 7/8] curl: set CVE_STATUS for CVE-2026-9547 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-08-05  8:31 ` Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
  7 siblings, 0 replies; 9+ messages in thread
From: Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-08-05  8:31 UTC (permalink / raw)
  To: openembedded-core

From: Deepak Rathore <deeratho@cisco.com>

Analysis:
- CVE-2026-12064 affects the curl command-line tool when
  SCP/SFTP protocol support is available. [1]
- Wrynose exposes SCP/SFTP only through the optional libssh2
  PACKAGECONFIG, which is disabled by default. [2]
- Hence mark this CVE as not-applicable-config for the default
  Wrynose recipe configuration, while reporting unpatched if
  libssh2 is explicitly enabled.

Reference:
[1] https://curl.se/docs/CVE-2026-12064.html
[2] https://git.openembedded.org/openembedded-core/tree/meta/recipes-support/curl/curl_8.19.0.bb?h=wrynose

Signed-off-by: Deepak Rathore <deeratho@cisco.com>
---
 meta/recipes-support/curl/curl_8.19.0.bb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 4c3e31d438..1bf32ffc53 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -44,6 +44,7 @@ CVE_STATUS[CVE-2024-32928] = "ignored: CURLOPT_SSL_VERIFYPEER was disabled on go
 CVE_STATUS[CVE-2026-8924] = "not-applicable-config: public suffix list support is disabled by the recipe with --without-libpsl"
 CVE_STATUS[CVE-2026-10536] = "${@bb.utils.contains('PACKAGECONFIG', 'nghttp2', 'unpatched', 'not-applicable-config: applicable only with HTTP/2', d)}"
 CVE_STATUS[CVE-2026-9547] = "not-applicable-config: vulnerable libssh backend is not enabled by the recipe"
+CVE_STATUS[CVE-2026-12064] = "${@bb.utils.contains('PACKAGECONFIG', 'libssh2', 'unpatched', 'not-applicable-config: SCP/SFTP support is not enabled in PACKAGECONFIG', d)}"
 
 inherit autotools pkgconfig binconfig multilib_header ptest
 
-- 
2.35.6



^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-08-05  8:31 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-05  8:30 [OE-core][wrynose][PATCH 0/8] curl: Security fixes Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-08-05  8:30 ` [OE-core][wrynose][PATCH 1/8] curl: fix CVE-2026-8286 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-08-05  8:30 ` [OE-core][wrynose][PATCH 2/8] curl: set CVE_STATUS for CVE-2026-8924 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-08-05  8:30 ` [OE-core][wrynose][PATCH 3/8] curl: fix CVE-2026-8927 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-08-05  8:30 ` [OE-core][wrynose][PATCH 4/8] curl: fix CVE-2026-8932 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-08-05  8:31 ` [OE-core][wrynose][PATCH 5/8] curl: fix CVE-2026-8458 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-08-05  8:31 ` [OE-core][wrynose][PATCH 6/8] curl: fix CVE-2026-11856 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-08-05  8:31 ` [OE-core][wrynose][PATCH 7/8] curl: set CVE_STATUS for CVE-2026-9547 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-08-05  8:31 ` [OE-core][wrynose][PATCH 8/8] curl: set CVE_STATUS for CVE-2026-12064 Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).