diff mbox series

[wrynose,5/6] curl: fix CVE-2026-6429

Message ID 20260629131453.1077612-5-adongare@cisco.com
State New
Headers show
Series [wrynose,1/6] curl: ignore CVE-2026-4873 | expand

Commit Message

From: Anil Dongare <adongare@cisco.com>

Backport the upstream fix [1] for the netrc credential leak on redirect
described in [2] and tracked by [3].

[1] https://github.com/curl/curl/commit/b4024bf808bd558026fdc6096e8457f199ace306
[2] https://curl.se/docs/CVE-2026-6429.html
[3] https://nvd.nist.gov/vuln/detail/CVE-2026-6429

Signed-off-by: Anil Dongare <adongare@cisco.com>
---
 .../curl/curl/CVE-2026-6429.patch             | 362 ++++++++++++++++++
 meta/recipes-support/curl/curl_8.19.0.bb      |   1 +
 2 files changed, 363 insertions(+)
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-6429.patch
diff mbox series

Patch

diff --git a/meta/recipes-support/curl/curl/CVE-2026-6429.patch b/meta/recipes-support/curl/curl/CVE-2026-6429.patch
new file mode 100644
index 0000000000..f4398e00f6
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-6429.patch
@@ -0,0 +1,362 @@ 
+From b4024bf808bd558026fdc6096e8457f199ace306 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel@haxx.se>
+Date: Thu, 16 Apr 2026 14:26:20 +0200
+Subject: [PATCH] http: clear credentials better on redirect
+
+Verify with test 2506: netrc with redirect using proxy
+
+Updated test 998 which was wrong.
+
+Reported-by: Muhamad Arga Reksapati
+
+Closes #21345
+
+CVE: CVE-2026-6429
+Upstream-Status: Backport [https://github.com/curl/curl/commit/b4024bf808bd558026fdc6096e8457f199ace306]
+
+Backport Changes:
+- curl 8.19.0 does not have Curl_url_same_origin(), so the same-origin comparison is kept inline in Curl_http_follow().
+- Adapted tests/data/Makefile.am and tests/libtest/Makefile.inc placement for the wrynose test lists.
+
+(cherry picked from commit b4024bf808bd558026fdc6096e8457f199ace306)
+Signed-off-by: Anil Dongare <adongare@cisco.com>
+---
+ lib/http.c                 | 121 +++++++++++++++++++------------------
+ tests/data/Makefile.am     |   2 +-
+ tests/data/test2506        |  64 ++++++++++++++++++++
+ tests/data/test998         |   1 -
+ tests/libtest/Makefile.inc |   2 +-
+ tests/libtest/lib2506.c    |  71 ++++++++++++++++++++++
+ 6 files changed, 198 insertions(+), 63 deletions(-)
+ create mode 100644 tests/data/test2506
+ create mode 100644 tests/libtest/lib2506.c
+
+diff --git a/lib/http.c b/lib/http.c
+index b960d79..9ac96ad 100644
+--- a/lib/http.c
++++ b/lib/http.c
+@@ -1201,75 +1201,76 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
+       return CURLE_OUT_OF_MEMORY;
+   }
+   else {
+-    uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0);
+-    if(uc)
+-      return Curl_uc_to_curlcode(uc);
+-
+-    /* Clear auth if this redirects to a different port number or protocol,
+-       unless permitted */
+-    if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) {
+-      int port;
+-      bool clear = FALSE;
++    bool same_origin;
++    CURLcode result;
++    CURLU *u = curl_url();
++    char *oldscheme = NULL;
++    char *oldhost = NULL;
++    char *oldport = NULL;
++    char *newscheme = NULL;
++    char *newhost = NULL;
++    char *newport = NULL;
++    if(!u)
++      return CURLE_OUT_OF_MEMORY;
+ 
+-      if(data->set.use_port && data->state.allow_port)
+-        /* a custom port is used */
+-        port = (int)data->set.use_port;
+-      else {
+-        curl_off_t value;
+-        char *portnum;
+-        const char *p;
+-        uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum,
+-                          CURLU_DEFAULT_PORT);
+-        if(uc) {
+-          curlx_free(follow_url);
+-          return Curl_uc_to_curlcode(uc);
+-        }
+-        p = portnum;
+-        curlx_str_number(&p, &value, 0xffff);
+-        port = (int)value;
+-        curlx_free(portnum);
+-      }
+-      if(port != data->info.conn_remote_port) {
+-        infof(data, "Clear auth, redirects to port from %u to %u",
+-              data->info.conn_remote_port, port);
+-        clear = TRUE;
+-      }
+-      else {
+-        char *scheme;
+-        const struct Curl_scheme *p;
+-        uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0);
+-        if(uc) {
+-          curlx_free(follow_url);
+-          return Curl_uc_to_curlcode(uc);
+-        }
++    uc = curl_url_set(u, CURLUPART_URL, Curl_bufref_ptr(&data->state.url),
++                      CURLU_URLENCODE | CURLU_ALLOW_SPACE);
++    if(!uc)
++      uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0);
++    if(!uc)
++      uc = curl_url_get(u, CURLUPART_SCHEME, &oldscheme, 0);
++    if(!uc)
++      uc = curl_url_get(u, CURLUPART_HOST, &oldhost, 0);
++    if(!uc)
++      uc = curl_url_get(u, CURLUPART_PORT, &oldport, CURLU_DEFAULT_PORT);
++    if(!uc)
++      uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &newscheme, 0);
++    if(!uc)
++      uc = curl_url_get(data->state.uh, CURLUPART_HOST, &newhost, 0);
++    if(!uc)
++      uc = curl_url_get(data->state.uh, CURLUPART_PORT, &newport,
++                        CURLU_DEFAULT_PORT);
++    if(uc) {
++      curl_url_cleanup(u);
++      curlx_free(oldscheme);
++      curlx_free(oldhost);
++      curlx_free(oldport);
++      curlx_free(newscheme);
++      curlx_free(newhost);
++      curlx_free(newport);
++      curlx_free(follow_url);
++      return Curl_uc_to_curlcode(uc);
++    }
+ 
+-        p = Curl_get_scheme(scheme);
+-        if(p && (p->protocol != data->info.conn_protocol)) {
+-          infof(data, "Clear auth, redirects scheme from %s to %s",
+-                data->info.conn_scheme, scheme);
+-          clear = TRUE;
+-        }
+-        curlx_free(scheme);
+-      }
+-      if(clear) {
+-        CURLcode result = Curl_reset_userpwd(data);
+-        if(result) {
+-          curlx_free(follow_url);
+-          return result;
+-        }
+-        Curl_safefree(data->state.aptr.user);
+-        Curl_safefree(data->state.aptr.passwd);
++    same_origin = strcasecompare(oldscheme, newscheme) &&
++                  strcasecompare(oldhost, newhost) &&
++                  !strcmp(oldport, newport);
++
++    curl_url_cleanup(u);
++    curlx_free(oldscheme);
++    curlx_free(oldhost);
++    curlx_free(oldport);
++    curlx_free(newscheme);
++    curlx_free(newhost);
++    curlx_free(newport);
++
++    if((!same_origin && !data->set.allow_auth_to_other_hosts) ||
++       !data->set.str[STRING_USERNAME]) {
++      result = Curl_reset_userpwd(data);
++      if(result) {
++        curlx_free(follow_url);
++        return result;
+       }
++      Curl_safefree(data->state.aptr.user);
++      Curl_safefree(data->state.aptr.passwd);
+     }
+-  }
+-  DEBUGASSERT(follow_url);
+-  {
+-    CURLcode result = Curl_reset_proxypwd(data);
++    result = Curl_reset_proxypwd(data);
+     if(result) {
+       curlx_free(follow_url);
+       return result;
+     }
+   }
++  DEBUGASSERT(follow_url);
+ 
+   if(type == FOLLOW_FAKE) {
+     /* we are only figuring out the new URL if we would have followed locations
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index 00a5221..1b76b01 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -265,7 +265,7 @@ test2309 \
+ \
+ test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \
+ \
+-test2500 test2501 test2502 test2503 test2504 \
++test2500 test2501 test2502 test2503 test2504 test2506 \
+ \
+ test2600 test2601 test2602 test2603 test2604 test2605 \
+ \
+diff --git a/tests/data/test2506 b/tests/data/test2506
+new file mode 100644
+index 0000000..9c65002
+--- /dev/null
++++ b/tests/data/test2506
+@@ -0,0 +1,64 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++HTTP
++cookies
++</keywords>
++</info>
++
++<reply>
++<data crlf="headers" nocheck="yes">
++HTTP/1.1 301 redirect
++Date: Tue, 09 Nov 2010 14:49:00 GMT
++Content-Length: 3
++Location: http://numbertwo.example/%TESTNUMBER0002
++
++ok
++</data>
++<data2 crlf="headers" nocheck="yes">
++HTTP/1.1 200 OK
++Date: Tue, 09 Nov 2010 14:49:00 GMT
++Content-Length: 4
++
++yes
++</data2>
++</reply>
++
++<client>
++<server>
++http
++</server>
++<features>
++proxy
++</features>
++<tool>
++lib%TESTNUMBER
++</tool>
++<name>
++netrc with redirect using proxy
++</name>
++<file name="%LOGDIR/netrc2506">
++machine site.example login batman password robin
++</file>
++<command>
++http://%HOSTIP:%HTTPPORT http://site.example/ %LOGDIR/netrc2506
++</command>
++</client>
++
++<verify>
++<protocol crlf="headers">
++GET http://site.example/ HTTP/1.1
++Host: site.example
++Authorization: Basic %b64[batman:robin]b64%
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++GET http://numbertwo.example/25060002 HTTP/1.1
++Host: numbertwo.example
++Accept: */*
++Proxy-Connection: Keep-Alive
++
++</protocol>
++</verify>
++</testcase>
+diff --git a/tests/data/test998 b/tests/data/test998
+index 24d1d3d..56dbc0c 100644
+--- a/tests/data/test998
++++ b/tests/data/test998
+@@ -77,7 +77,6 @@ Proxy-Connection: Keep-Alive
+ 
+ GET http://somewhere.else.example/a/path/9980002 HTTP/1.1
+ Host: somewhere.else.example
+-Authorization: Basic %b64[alberto:einstein]b64%
+ User-Agent: curl/%VERSION
+ Accept: */*
+ Proxy-Connection: Keep-Alive
+diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
+index 2319baf..2f77c16 100644
+--- a/tests/libtest/Makefile.inc
++++ b/tests/libtest/Makefile.inc
+@@ -113,7 +113,7 @@ TESTS_C = \
+   lib2023.c lib2032.c lib2082.c \
+   lib2301.c lib2302.c lib2304.c           lib2306.c lib2308.c lib2309.c \
+   lib2402.c           lib2404.c lib2405.c \
+-  lib2502.c lib2504.c \
++  lib2502.c lib2504.c lib2506.c \
+   lib2700.c \
+   lib3010.c lib3025.c lib3026.c lib3027.c lib3033.c lib3034.c \
+   lib3100.c lib3101.c lib3102.c lib3103.c lib3104.c lib3105.c \
+diff --git a/tests/libtest/lib2506.c b/tests/libtest/lib2506.c
+new file mode 100644
+index 0000000..8b3b342
+--- /dev/null
++++ b/tests/libtest/lib2506.c
+@@ -0,0 +1,71 @@
++/***************************************************************************
++ *                                  _   _ ____  _
++ *  Project                     ___| | | |  _ \| |
++ *                             / __| | | | |_) | |
++ *                            | (__| |_| |  _ <| |___
++ *                             \___|\___/|_| \_\_____|
++ *
++ * Copyright (C) Linus Nielsen Feltzing <linus@haxx.se>
++ *
++ * 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"
++
++#include "testtrace.h"
++
++static size_t sink2506(char *ptr, size_t size, size_t nmemb, void *ud)
++{
++  (void)ptr;
++  (void)ud;
++  return size * nmemb;
++}
++
++static CURLcode test_lib2506(const char *URL)
++{
++  CURL *curl;
++  CURLcode result = CURLE_OUT_OF_MEMORY;
++
++  if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
++    curl_mfprintf(stderr, "curl_global_init() failed\n");
++    return TEST_ERR_MAJOR_BAD;
++  }
++
++  curl = curl_easy_init();
++  if(!curl) {
++    curl_mfprintf(stderr, "curl_easy_init() failed\n");
++    curl_global_cleanup();
++    return TEST_ERR_MAJOR_BAD;
++  }
++
++  test_setopt(curl, CURLOPT_WRITEFUNCTION, sink2506);
++  test_setopt(curl, CURLOPT_PROXY, URL);
++  test_setopt(curl, CURLOPT_URL, libtest_arg2);
++  test_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
++  test_setopt(curl, CURLOPT_NETRC_FILE, libtest_arg3);
++  test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
++  test_setopt(curl, CURLOPT_VERBOSE, 1L);
++
++  /* CURLOPT_UNRESTRICTED_AUTH should not make a difference because the
++     credentials come from netrc */
++  test_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1L);
++
++  result = curl_easy_perform(curl);
++
++test_cleanup:
++  curl_easy_cleanup(curl);
++  curl_global_cleanup();
++
++  return result;
++}
+-- 
+2.43.7
+
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 09e93c8ce5..6c31978519 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -17,6 +17,7 @@  SRC_URI = " \
     file://CVE-2026-6276.patch \
     file://CVE-2026-5545.patch \
     file://CVE-2026-6253.patch \
+    file://CVE-2026-6429.patch \
     file://mbedtls.patch \
 "