diff --git a/SPECS/cmake/CVE-2026-4873.patch b/SPECS/cmake/CVE-2026-4873.patch new file mode 100644 index 00000000000..77f004652b4 --- /dev/null +++ b/SPECS/cmake/CVE-2026-4873.patch @@ -0,0 +1,35 @@ +From 507e7be573b0a76fca597b75ff7cb27a66e7d865 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Tue, 24 Mar 2026 08:35:08 +0100 +Subject: [PATCH] url: do not reuse a non-tls starttls connection if new + requires TLS + +Reported-by: Arkadi Vainbrand + +Closes #21082 + +Upstream Patch Reference: https://launchpadlibrarian.net/859770355/curl_8.5.0-2ubuntu10.8_8.5.0-2ubuntu10.9.diff.gz +https://github.com/curl/curl/commit/507e7be573b0a76fca597b75ff7cb27a66e7d865.patch +--- + Utilities/cmcurl/lib/url.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c +index 5f3eae4e..b8980f87 100644 +--- a/Utilities/cmcurl/lib/url.c ++++ b/Utilities/cmcurl/lib/url.c +@@ -1188,6 +1188,11 @@ ConnectionExists(struct Curl_easy *data, + (get_protocol_family(check->handler) != + needle->handler->protocol || !check->bits.tls_upgraded)) + continue; ++ else if(!(needle->handler->flags & PROTOPT_SSL) && ++ !check->bits.tls_upgraded && ++ data->set.use_ssl >= CURLUSESSL_CONTROL) ++ /* do not reuse a non-TLS STARTTLS connection if TLS is required */ ++ continue; + + /* If needle has "conn_to_*" set, check must match this */ + if((needle->bits.conn_to_host && !strcasecompare( +-- +2.45.4 + diff --git a/SPECS/cmake/CVE-2026-5545.patch b/SPECS/cmake/CVE-2026-5545.patch new file mode 100644 index 00000000000..5d221e76356 --- /dev/null +++ b/SPECS/cmake/CVE-2026-5545.patch @@ -0,0 +1,41 @@ +From 33e43985b8f3b9e66691d06e70be0395849856cd Mon Sep 17 00:00:00 2001 +From: Stefan Eissing +Date: Thu, 2 Apr 2026 11:33:39 +0200 +Subject: [PATCH] url: improve connection reuse on negotiate + +Check state of negotiate to allow proper connection reuse. + +Closes #21203 + +Upstream Patch Reference: https://launchpadlibrarian.net/859770355/curl_8.5.0-2ubuntu10.8_8.5.0-2ubuntu10.9.diff.gz +https://github.com/curl/curl/commit/33e43985b8f3b9e6669.patch +--- + Utilities/cmcurl/lib/url.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c +index 319ea1ea..47451df2 100644 +--- a/Utilities/cmcurl/lib/url.c ++++ b/Utilities/cmcurl/lib/url.c +@@ -1228,9 +1228,16 @@ ConnectionExists(struct Curl_easy *data, + Curl_timestrcmp(needle->passwd, check->passwd)) { + + /* we prefer a credential match, but this is at least a connection +- that can be reused and "upgraded" to NTLM */ +- if(check->http_ntlm_state == NTLMSTATE_NONE) ++ that can be reused and "upgraded" to NTLM if it does ++ not have any auth ongoing. */ ++#ifdef USE_SPNEGO ++ if((check->http_ntlm_state == NTLMSTATE_NONE) ++ && (check->http_negotiate_state == GSS_AUTHNONE)) { ++#else ++ if(check->http_ntlm_state == NTLMSTATE_NONE) { ++#endif + chosen = check; ++ } + continue; + } + } +-- +2.45.4 + diff --git a/SPECS/cmake/CVE-2026-6253.patch b/SPECS/cmake/CVE-2026-6253.patch new file mode 100644 index 00000000000..87229c6f9e5 --- /dev/null +++ b/SPECS/cmake/CVE-2026-6253.patch @@ -0,0 +1,222 @@ +From 188c2f166a20fa97c2325b2da7d0e5cecc13725f Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Mon, 13 Apr 2026 17:17:23 +0200 +Subject: [PATCH] http: clear the proxy credentials as well on port or scheme + change + +Add tests 2009-2011 to verify switching between proxies with credentials +when the switch is driven by a redirect + +Reported-by: Dwij Mehta + +Upstream Patch Reference: https://launchpadlibrarian.net/859770351/curl_8.14.1-2ubuntu1.2_8.14.1-2ubuntu1.3.diff.gz +https://github.com/curl/curl/commit/188c2f166a20fa97c2325b2da7d0e5cecc13725f.patch +--- + Utilities/cmcurl/lib/cf-h1-proxy.c | 25 ++++++------ + Utilities/cmcurl/lib/cf-h2-proxy.c | 2 +- + Utilities/cmcurl/lib/transfer.c | 61 ++++++++++++++++++++++++------ + Utilities/cmcurl/lib/transfer.h | 2 + + Utilities/cmcurl/lib/urldata.h | 5 +++ + 5 files changed, 70 insertions(+), 25 deletions(-) + +diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c +index 093c33be..2d4a9e93 100644 +--- a/Utilities/cmcurl/lib/cf-h1-proxy.c ++++ b/Utilities/cmcurl/lib/cf-h1-proxy.c +@@ -419,17 +419,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, + + if(ts->keepon == KEEPON_IGNORE) { + /* This means we are currently ignoring a response-body */ +- +- if(ts->cl) { +- /* A Content-Length based body: simply count down the counter +- and make sure to break out of the loop when we're done! */ +- ts->cl--; +- if(ts->cl <= 0) { +- ts->keepon = KEEPON_DONE; +- break; +- } +- } +- else if(ts->chunked_encoding) { ++ if(ts->chunked_encoding) { + /* chunked-encoded body, so we need to do the chunked dance + properly to know when the end of the body is reached */ + size_t consumed = 0; +@@ -445,6 +435,15 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, + ts->keepon = KEEPON_DONE; + } + } ++ else if(ts->cl) { ++ /* A Content-Length based body: count down the counter ++ and make sure to break out of the loop when we are done! */ ++ ts->cl--; ++ if(ts->cl <= 0) { ++ ts->keepon = KEEPON_DONE; ++ break; ++ } ++ } + continue; + } + +@@ -908,6 +907,8 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, + /* read what is there */ + CURL_TRC_CF(data, cf, "CONNECT receive"); + result = recv_CONNECT_resp(cf, data, ts, &done); ++ if(result) ++ CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", result); + if(Curl_pgrsUpdate(data)) { + result = CURLE_ABORTED_BY_CALLBACK; + goto out; +@@ -961,7 +962,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, + streamclose(conn, "proxy CONNECT failure"); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); + failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); +- return CURLE_RECV_ERROR; ++ return CURLE_COULDNT_CONNECT; + } + /* 2xx response, SUCCESS! */ + h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); +diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c +index 0bff15f3..d3cdbe80 100644 +--- a/Utilities/cmcurl/lib/cf-h2-proxy.c ++++ b/Utilities/cmcurl/lib/cf-h2-proxy.c +@@ -1016,7 +1016,7 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, + } + + /* Seems to have failed */ +- return CURLE_RECV_ERROR; ++ return CURLE_COULDNT_CONNECT; + } + + static CURLcode H2_CONNECT(struct Curl_cfilter *cf, +diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c +index 744227eb..15aa9d29 100644 +--- a/Utilities/cmcurl/lib/transfer.c ++++ b/Utilities/cmcurl/lib/transfer.c +@@ -559,6 +559,40 @@ void Curl_init_CONNECT(struct Curl_easy *data) + data->state.upload = (data->state.httpreq == HTTPREQ_PUT); + } + ++/* ++ * Restore the user credentials to those set in options. ++ */ ++CURLcode Curl_reset_userpwd(struct Curl_easy *data) ++{ ++ CURLcode result; ++ if(data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD]) ++ data->state.creds_from = CREDS_OPTION; ++ result = Curl_setstropt(&data->state.aptr.user, ++ data->set.str[STRING_USERNAME]); ++ if(!result) ++ result = Curl_setstropt(&data->state.aptr.passwd, ++ data->set.str[STRING_PASSWORD]); ++ return result; ++} ++ ++/* ++ * Restore the proxy credentials to those set in options. ++ */ ++CURLcode Curl_reset_proxypwd(struct Curl_easy *data) ++{ ++#ifndef CURL_DISABLE_PROXY ++ CURLcode result = Curl_setstropt(&data->state.aptr.proxyuser, ++ data->set.str[STRING_PROXYUSERNAME]); ++ if(!result) ++ result = Curl_setstropt(&data->state.aptr.proxypasswd, ++ data->set.str[STRING_PROXYPASSWORD]); ++ return result; ++#else ++ (void)data; ++ return CURLE_OK; ++#endif ++} ++ + /* + * Curl_pretransfer() is called immediately before a transfer starts, and only + * once for one transfer no matter if it has redirects or do multi-pass +@@ -707,19 +741,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) + } + + if(!result) +- result = Curl_setstropt(&data->state.aptr.user, +- data->set.str[STRING_USERNAME]); +- if(!result) +- result = Curl_setstropt(&data->state.aptr.passwd, +- data->set.str[STRING_PASSWORD]); +-#ifndef CURL_DISABLE_PROXY +- if(!result) +- result = Curl_setstropt(&data->state.aptr.proxyuser, +- data->set.str[STRING_PROXYUSERNAME]); ++ result = Curl_reset_userpwd(data); + if(!result) +- result = Curl_setstropt(&data->state.aptr.proxypasswd, +- data->set.str[STRING_PROXYPASSWORD]); +-#endif ++ result = Curl_reset_proxypwd(data); + + data->req.headerbytecount = 0; + Curl_headers_cleanup(data); +@@ -894,11 +918,24 @@ CURLcode Curl_follow(struct Curl_easy *data, + free(scheme); + } + if(clear) { ++ CURLcode result = Curl_reset_userpwd(data); ++ if(result) { ++ free(newurl); ++ return result; ++ } + Curl_safefree(data->state.aptr.user); + Curl_safefree(data->state.aptr.passwd); + } + } + } ++ DEBUGASSERT(newurl); ++ { ++ CURLcode result = Curl_reset_proxypwd(data); ++ if(result) { ++ free(newurl); ++ return result; ++ } ++ } + + if(type == FOLLOW_FAKE) { + /* we're only figuring out the new url if we would've followed locations +diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h +index ad0f3a20..7a1dc174 100644 +--- a/Utilities/cmcurl/lib/transfer.h ++++ b/Utilities/cmcurl/lib/transfer.h +@@ -31,6 +31,8 @@ char *Curl_checkheaders(const struct Curl_easy *data, + + void Curl_init_CONNECT(struct Curl_easy *data); + ++CURLcode Curl_reset_userpwd(struct Curl_easy *data); ++CURLcode Curl_reset_proxypwd(struct Curl_easy *data); + CURLcode Curl_pretransfer(struct Curl_easy *data); + CURLcode Curl_posttransfer(struct Curl_easy *data); + +diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h +index fffac9f5..95ce93f7 100644 +--- a/Utilities/cmcurl/lib/urldata.h ++++ b/Utilities/cmcurl/lib/urldata.h +@@ -1222,6 +1222,8 @@ struct urlpieces { + char *query; + }; + ++#define CREDS_OPTION 2 /* set with a CURLOPT_ */ ++ + struct UrlState { + /* Points to the connection cache */ + struct conncache *conn_cache; +@@ -1372,6 +1374,9 @@ struct UrlState { + unsigned char select_bits; /* != 0 -> bitmask of socket events for this + transfer overriding anything the socket may + report */ ++ unsigned int creds_from:2; /* where is the server credentials originating ++ from, see the CREDS_* defines above */ ++ + #ifdef CURLDEBUG + BIT(conncache_lock); + #endif +-- +2.45.4 + diff --git a/SPECS/cmake/CVE-2026-6276.patch b/SPECS/cmake/CVE-2026-6276.patch new file mode 100644 index 00000000000..51d820f7ff0 --- /dev/null +++ b/SPECS/cmake/CVE-2026-6276.patch @@ -0,0 +1,125 @@ +From 3a19987a87f393d9394fe5acc7643f6c263c92db Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Tue, 14 Apr 2026 08:51:44 +0200 +Subject: [PATCH] urldata: move cookiehost to struct SingleRequest + +To make it scoped for the single request appropriately. + +Reported-by: Muhamad Arga Reksapati + +Verify with libtest 2504: a custom Host *disabled* on reused handle + +Closes #21312 +Upstream Patch Reference: https://github.com/curl/curl/commit/3a19987a87f393d9394fe5ac.patch +--- + Utilities/cmcurl/lib/http.c | 14 ++++++++------ + Utilities/cmcurl/lib/request.c | 3 +++ + Utilities/cmcurl/lib/request.h | 3 +++ + Utilities/cmcurl/lib/url.c | 2 +- + Utilities/cmcurl/lib/urldata.h | 3 --- + 5 files changed, 15 insertions(+), 10 deletions(-) + +diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c +index 2a41f807..ba05658c 100644 +--- a/Utilities/cmcurl/lib/http.c ++++ b/Utilities/cmcurl/lib/http.c +@@ -1714,6 +1714,9 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) + data->state.first_remote_protocol = conn->handler->protocol; + } + Curl_safefree(aptr->host); ++#ifndef CURL_DISABLE_COOKIES ++ Curl_safefree(data->req.cookiehost); ++#endif + + ptr = Curl_checkheaders(data, STRCONST("Host")); + if(ptr && (!data->state.this_is_a_follow || +@@ -1748,8 +1751,7 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn) + if(colon) + *colon = 0; /* The host must not include an embedded port number */ + } +- Curl_safefree(aptr->cookiehost); +- aptr->cookiehost = cookiehost; ++ data->req.cookiehost = cookiehost; + } + #endif + +@@ -2270,8 +2272,8 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, + int count = 0; + + if(data->cookies && data->state.cookie_engine) { +- const char *host = data->state.aptr.cookiehost ? +- data->state.aptr.cookiehost : conn->host.name; ++ const char *host = data->req.cookiehost ? ++ data->req.cookiehost : conn->host.name; + const bool secure_context = + conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || + strcasecompare("localhost", host) || +@@ -3096,8 +3098,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, + if(v) { + /* If there is a custom-set Host: name, use it here, or else use + * real peer host name. */ +- const char *host = data->state.aptr.cookiehost? +- data->state.aptr.cookiehost:conn->host.name; ++ const char *host = data->req.cookiehost ? ++ data->req.cookiehost : conn->host.name; + const bool secure_context = + conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || + strcasecompare("localhost", host) || +diff --git a/Utilities/cmcurl/lib/request.c b/Utilities/cmcurl/lib/request.c +index 26741fa6..3307c746 100644 +--- a/Utilities/cmcurl/lib/request.c ++++ b/Utilities/cmcurl/lib/request.c +@@ -110,6 +110,9 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) + * free this safely without leaks. */ + Curl_safefree(req->p.http); + Curl_safefree(req->newurl); ++#ifndef CURL_DISABLE_COOKIES ++ Curl_safefree(req->cookiehost); ++#endif + Curl_client_reset(data); + if(req->sendbuf_init) + Curl_bufq_reset(&req->sendbuf); +diff --git a/Utilities/cmcurl/lib/request.h b/Utilities/cmcurl/lib/request.h +index f9be6f29..2247c297 100644 +--- a/Utilities/cmcurl/lib/request.h ++++ b/Utilities/cmcurl/lib/request.h +@@ -118,6 +118,9 @@ struct SingleRequest { + #ifndef CURL_DISABLE_DOH + struct dohdata *doh; /* DoH specific data for this request */ + #endif ++#ifndef CURL_DISABLE_COOKIES ++ char *cookiehost; ++#endif + #ifndef CURL_DISABLE_COOKIES + unsigned char setcookies; + #endif +diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c +index b8980f87..319ea1ea 100644 +--- a/Utilities/cmcurl/lib/url.c ++++ b/Utilities/cmcurl/lib/url.c +@@ -318,7 +318,7 @@ CURLcode Curl_close(struct Curl_easy **datap) + Curl_safefree(data->state.aptr.ref); + Curl_safefree(data->state.aptr.host); + #ifndef CURL_DISABLE_COOKIES +- Curl_safefree(data->state.aptr.cookiehost); ++ Curl_safefree(data->req.cookiehost); + #endif + #ifndef CURL_DISABLE_RTSP + Curl_safefree(data->state.aptr.rtsp_transport); +diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h +index 8b1bd65d..fffac9f5 100644 +--- a/Utilities/cmcurl/lib/urldata.h ++++ b/Utilities/cmcurl/lib/urldata.h +@@ -1348,9 +1348,6 @@ struct UrlState { + char *rangeline; + char *ref; + char *host; +-#ifndef CURL_DISABLE_COOKIES +- char *cookiehost; +-#endif + #ifndef CURL_DISABLE_RTSP + char *rtsp_transport; + #endif +-- +2.45.4 + diff --git a/SPECS/cmake/CVE-2026-6429.patch b/SPECS/cmake/CVE-2026-6429.patch new file mode 100644 index 00000000000..6eba68706e7 --- /dev/null +++ b/SPECS/cmake/CVE-2026-6429.patch @@ -0,0 +1,232 @@ +From b4024bf808bd558026fdc6096e8457f199ace306 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +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 +Upstream Patch Reference: https://launchpadlibrarian.net/859770351/curl_8.14.1-2ubuntu1.2_8.14.1-2ubuntu1.3.diff.gz +https://github.com/curl/curl/commit/b4024bf808bd558026fdc6.patch +--- + Utilities/cmcurl/lib/http2.c | 22 ++++++--- + Utilities/cmcurl/lib/transfer.c | 82 ++++++++++--------------------- + Utilities/cmcurl/lib/urlapi-int.h | 2 + + Utilities/cmcurl/lib/urlapi.c | 33 +++++++++++++ + 4 files changed, 76 insertions(+), 63 deletions(-) + +diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c +index f0f7b566..19531f07 100644 +--- a/Utilities/cmcurl/lib/http2.c ++++ b/Utilities/cmcurl/lib/http2.c +@@ -810,8 +810,9 @@ static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, + return second; + } + +-static int set_transfer_url(struct Curl_easy *data, +- struct curl_pushheaders *hp) ++static int set_transfer_url(struct Curl_easy *newhandle, ++ struct curl_pushheaders *hp, ++ struct Curl_easy *data) + { + const char *v; + CURLUcode uc; +@@ -849,6 +850,13 @@ static int set_transfer_url(struct Curl_easy *data, + } + } + ++ /* We can only allow PUSH of resource from the same origin, e.g. ++ * scheme + hostname + port */ ++ if(!Curl_url_same_origin(data->state.uh, u)) { ++ rc = 1; ++ goto fail; ++ } ++ + uc = curl_url_get(u, CURLUPART_URL, &url, 0); + if(uc) + rc = 4; +@@ -857,10 +865,10 @@ fail: + if(rc) + return rc; + +- if(data->state.url_alloc) +- free(data->state.url); +- data->state.url_alloc = TRUE; +- data->state.url = url; ++ if(newhandle->state.url_alloc) ++ free(newhandle->state.url); ++ newhandle->state.url_alloc = TRUE; ++ newhandle->state.url = url; + return 0; + } + +@@ -911,7 +919,7 @@ static int push_promise(struct Curl_cfilter *cf, + heads.stream = stream; + heads.frame = frame; + +- rv = set_transfer_url(newhandle, &heads); ++ rv = set_transfer_url(newhandle, &heads, data); + if(rv) { + discard_newhandle(cf, newhandle); + rv = CURL_PUSH_DENY; +diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c +index 15aa9d29..21f7b7b1 100644 +--- a/Utilities/cmcurl/lib/transfer.c ++++ b/Utilities/cmcurl/lib/transfer.c +@@ -871,71 +871,41 @@ CURLcode Curl_follow(struct Curl_easy *data, + return CURLE_OUT_OF_MEMORY; + } + else { +- uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); +- if(uc) ++ bool same_origin; ++ CURLcode result; ++ CURLU *u = curl_url(); ++ if(!u) ++ return CURLE_OUT_OF_MEMORY; ++ uc = curl_url_set(u, CURLUPART_URL, ++ data->state.url, ++ CURLU_URLENCODE | CURLU_ALLOW_SPACE); ++ if(!uc) ++ uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); ++ if(uc) { ++ curl_url_cleanup(u); + 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)) { +- char *portnum; +- int port; +- bool clear = FALSE; +- +- if(data->set.use_port && data->state.allow_port) +- /* a custom port is used */ +- port = (int)data->set.use_port; +- else { +- uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, +- CURLU_DEFAULT_PORT); +- if(uc) { +- free(newurl); +- return Curl_uc_to_curlcode(uc); +- } +- port = atoi(portnum); +- 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_handler *p; +- uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); +- if(uc) { +- free(newurl); +- return Curl_uc_to_curlcode(uc); +- } +- +- p = Curl_get_scheme_handler(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; +- } +- free(scheme); +- } +- if(clear) { +- CURLcode result = Curl_reset_userpwd(data); +- if(result) { +- free(newurl); +- return result; +- } +- Curl_safefree(data->state.aptr.user); +- Curl_safefree(data->state.aptr.passwd); ++ same_origin = Curl_url_same_origin(u, data->state.uh); ++ curl_url_cleanup(u); ++ ++ if((!same_origin && !data->set.allow_auth_to_other_hosts) || ++ !data->set.str[STRING_USERNAME]) { ++ result = Curl_reset_userpwd(data); ++ if(result) { ++ free(newurl); ++ return result; + } ++ Curl_safefree(data->state.aptr.user); ++ Curl_safefree(data->state.aptr.passwd); + } +- } +- DEBUGASSERT(newurl); +- { +- CURLcode result = Curl_reset_proxypwd(data); ++ result = Curl_reset_proxypwd(data); + if(result) { + free(newurl); + return result; + } + } ++ DEBUGASSERT(newurl); + + if(type == FOLLOW_FAKE) { + /* we're only figuring out the new url if we would've followed locations +diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h +index c40281a8..9d038a5d 100644 +--- a/Utilities/cmcurl/lib/urlapi-int.h ++++ b/Utilities/cmcurl/lib/urlapi-int.h +@@ -35,4 +35,6 @@ CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, + bool has_scheme); + #endif + ++bool Curl_url_same_origin(CURLU *base, CURLU *href); ++ + #endif /* HEADER_CURL_URLAPI_INT_H */ +diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c +index eb039668..65d18b16 100644 +--- a/Utilities/cmcurl/lib/urlapi.c ++++ b/Utilities/cmcurl/lib/urlapi.c +@@ -1993,3 +1993,36 @@ nomem: + } + return CURLUE_OK; + } ++ ++bool Curl_url_same_origin(CURLU *base, CURLU *href) ++{ ++ const struct Curl_handler *s = NULL; ++ ++ /* base must be an absolute URL */ ++ if(!base->scheme || !base->host) ++ return FALSE; ++ if(href->scheme && !curl_strequal(base->scheme, href->scheme)) ++ return FALSE; ++ if(href->host) { ++ if(!curl_strequal(base->host, href->host)) ++ return FALSE; ++ if(!curl_strequal(base->port, href->port)) { ++ /* This may still match if only one has an explicit port ++ * and it is the default for the scheme. */ ++ if(base->port && href->port) ++ return FALSE; ++ ++ s = Curl_get_scheme_handler(base->scheme); ++ if(!s) /* Cannot match default port for unknown scheme */ ++ return FALSE; ++ ++ /* The port which is set must be the default one */ ++ if((base->port && (base->portnum != s->defport)) || ++ (href->port && (href->portnum != s->defport))) ++ return FALSE; ++ } ++ } ++ else if(href->port) /* no host in href, then there must be no port */ ++ return FALSE; ++ return TRUE; ++} +-- +2.45.4 + diff --git a/SPECS/cmake/cmake.spec b/SPECS/cmake/cmake.spec index a003bd43418..44e3a7d2519 100644 --- a/SPECS/cmake/cmake.spec +++ b/SPECS/cmake/cmake.spec @@ -2,7 +2,7 @@ Summary: Cmake Name: cmake Version: 3.30.3 -Release: 13%{?dist} +Release: 14%{?dist} License: BSD AND LGPLv2+ Vendor: Microsoft Corporation Distribution: Azure Linux @@ -25,17 +25,22 @@ Patch7: CVE-2023-44487.patch # Utilities/cmnghttp2/lib/includes/nghttp2/nghttp2ver.h. Manual inspection is # required to determine what upstream patches are included. Patch8: CVE-2023-35945.patch -Patch9: CVE-2024-48615.patch -Patch10: CVE-2025-4947.patch -Patch11: CVE-2025-5916.patch -Patch12: CVE-2025-5917.patch -Patch13: CVE-2025-5918.patch -Patch14: CVE-2025-9301.patch -Patch15: CVE-2025-10148.patch -Patch16: CVE-2025-14017.patch -Patch17: CVE-2025-10966.patch -Patch18: CVE-2025-14524.patch -Patch19: CVE-2026-27135.patch +Patch9: CVE-2024-48615.patch +Patch10: CVE-2025-4947.patch +Patch11: CVE-2025-5916.patch +Patch12: CVE-2025-5917.patch +Patch13: CVE-2025-5918.patch +Patch14: CVE-2025-9301.patch +Patch15: CVE-2025-10148.patch +Patch16: CVE-2025-14017.patch +Patch17: CVE-2025-10966.patch +Patch18: CVE-2025-14524.patch +Patch19: CVE-2026-27135.patch +Patch20: CVE-2026-4873.patch +Patch21: CVE-2026-6276.patch +Patch22: CVE-2026-6253.patch +Patch23: CVE-2026-6429.patch +Patch24: CVE-2026-5545.patch BuildRequires: bzip2 BuildRequires: bzip2-devel @@ -116,6 +121,9 @@ bin/ctest --force-new-ctest-process --rerun-failed --output-on-failure %{_libdir}/rpm/macros.d/macros.cmake %changelog +* Wed May 27 2026 Jyoti Kanase - 3.30.3-14 +- Patch for CVE-2026-4873, CVE-2026-6276, CVE-2026-6253, CVE-2026-6429, CVE-2026-5545 + * Fri Mar 20 2026 Azure Linux Security Servicing Account - 3.30.3-13 - Patch for CVE-2026-27135 diff --git a/SPECS/curl/CVE-2026-5545.patch b/SPECS/curl/CVE-2026-5545.patch new file mode 100644 index 00000000000..5a4541f5b6b --- /dev/null +++ b/SPECS/curl/CVE-2026-5545.patch @@ -0,0 +1,42 @@ +From 33e43985b8f3b9e66691d06e70be0395849856cd Mon Sep 17 00:00:00 2001 +From: Stefan Eissing +Date: Thu, 2 Apr 2026 11:33:39 +0200 +Subject: [PATCH] url: improve connection reuse on negotiate + +Check state of negotiate to allow proper connection reuse. + +Closes #21203 +Upstream Patch Reference: https://github.com/curl/curl/commit/33e43985b8f3b9e66691d06e70be0395849856cd.patch +https://launchpadlibrarian.net/859770351/curl_8.14.1-2ubuntu1.2_8.14.1-2ubuntu1.3.diff.gz +--- + lib/url.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/lib/url.c b/lib/url.c +index 6ea7b30..984b8db 100644 +--- a/lib/url.c ++++ b/lib/url.c +@@ -1166,11 +1166,17 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) + if(match->want_ntlm_http) { + if(Curl_timestrcmp(needle->user, conn->user) || + Curl_timestrcmp(needle->passwd, conn->passwd)) { +- + /* we prefer a credential match, but this is at least a connection +- that can be reused and "upgraded" to NTLM */ +- if(conn->http_ntlm_state == NTLMSTATE_NONE) ++ that can be reused and "upgraded" to NTLM if it does ++ not have any auth ongoing. */ ++#ifdef USE_SPNEGO ++ if((conn->http_ntlm_state == NTLMSTATE_NONE) ++ && (conn->http_negotiate_state == GSS_AUTHNONE)) { ++#else ++ if(conn->http_ntlm_state == NTLMSTATE_NONE) { ++#endif + match->found = conn; ++ } + return FALSE; + } + } +-- +2.45.4 + diff --git a/SPECS/curl/CVE-2026-6253.patch b/SPECS/curl/CVE-2026-6253.patch new file mode 100644 index 00000000000..71d514f4543 --- /dev/null +++ b/SPECS/curl/CVE-2026-6253.patch @@ -0,0 +1,384 @@ +From 188c2f166a20fa97c2325b2da7d0e5cecc13725f Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Mon, 13 Apr 2026 17:17:23 +0200 +Subject: [PATCH] http: clear the proxy credentials as well on port or scheme + change + +Add tests 2009-2011 to verify switching between proxies with credentials +when the switch is driven by a redirect + +Reported-by: Dwij Mehta + +Upstream Patch Reference: https://launchpadlibrarian.net/859770351/curl_8.14.1-2ubuntu1.2_8.14.1-2ubuntu1.3.diff.gz +https://github.com/curl/curl/commit/188c2f166a20fa97c2325b2da7d0e5cecc13725f.patch +--- + lib/multi.c | 13 ++++++++ + lib/transfer.c | 51 +++++++++++++++++++++--------- + lib/transfer.h | 2 ++ + tests/data/Makefile.am | 2 +- + tests/data/test2009 | 70 +++++++++++++++++++++++++++++++++++++++++ + tests/data/test2010 | 71 ++++++++++++++++++++++++++++++++++++++++++ + tests/data/test2011 | 70 +++++++++++++++++++++++++++++++++++++++++ + 7 files changed, 263 insertions(+), 16 deletions(-) + create mode 100644 tests/data/test2009 + create mode 100644 tests/data/test2010 + create mode 100644 tests/data/test2011 + +diff --git a/lib/multi.c b/lib/multi.c +index 1851dc7..53ab80e 100644 +--- a/lib/multi.c ++++ b/lib/multi.c +@@ -1979,11 +1979,24 @@ static CURLcode multi_follow(struct Curl_easy *data, + free(scheme); + } + if(clear) { ++ CURLcode result = Curl_reset_userpwd(data); ++ if(result) { ++ free(newurl); ++ return result; ++ } + Curl_safefree(data->state.aptr.user); + Curl_safefree(data->state.aptr.passwd); + } + } + } ++ DEBUGASSERT(newurl); ++ { ++ CURLcode result = Curl_reset_proxypwd(data); ++ if(result) { ++ free(newurl); ++ return result; ++ } ++ } + + if(type == FOLLOW_FAKE) { + /* we are only figuring out the new URL if we would have followed locations +diff --git a/lib/transfer.c b/lib/transfer.c +index d7d3d16..0b688a7 100644 +--- a/lib/transfer.c ++++ b/lib/transfer.c +@@ -531,6 +531,40 @@ void Curl_init_CONNECT(struct Curl_easy *data) + data->state.upload = (data->state.httpreq == HTTPREQ_PUT); + } + ++/* ++ * Restore the user credentials to those set in options. ++ */ ++CURLcode Curl_reset_userpwd(struct Curl_easy *data) ++{ ++ CURLcode result; ++ if(data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD]) ++ data->state.creds_from = CREDS_OPTION; ++ result = Curl_setstropt(&data->state.aptr.user, ++ data->set.str[STRING_USERNAME]); ++ if(!result) ++ result = Curl_setstropt(&data->state.aptr.passwd, ++ data->set.str[STRING_PASSWORD]); ++ return result; ++} ++ ++/* ++ * Restore the proxy credentials to those set in options. ++ */ ++CURLcode Curl_reset_proxypwd(struct Curl_easy *data) ++{ ++#ifndef CURL_DISABLE_PROXY ++ CURLcode result = Curl_setstropt(&data->state.aptr.proxyuser, ++ data->set.str[STRING_PROXYUSERNAME]); ++ if(!result) ++ result = Curl_setstropt(&data->state.aptr.proxypasswd, ++ data->set.str[STRING_PROXYPASSWORD]); ++ return result; ++#else ++ (void)data; ++ return CURLE_OK; ++#endif ++} ++ + /* + * Curl_pretransfer() is called immediately before a transfer starts, and only + * once for one transfer no matter if it has redirects or do multi-pass +@@ -678,23 +712,10 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) + return CURLE_OUT_OF_MEMORY; + } + +- if(data->set.str[STRING_USERNAME] || +- data->set.str[STRING_PASSWORD]) +- data->state.creds_from = CREDS_OPTION; + if(!result) +- result = Curl_setstropt(&data->state.aptr.user, +- data->set.str[STRING_USERNAME]); ++ result = Curl_reset_userpwd(data); + if(!result) +- result = Curl_setstropt(&data->state.aptr.passwd, +- data->set.str[STRING_PASSWORD]); +-#ifndef CURL_DISABLE_PROXY +- if(!result) +- result = Curl_setstropt(&data->state.aptr.proxyuser, +- data->set.str[STRING_PROXYUSERNAME]); +- if(!result) +- result = Curl_setstropt(&data->state.aptr.proxypasswd, +- data->set.str[STRING_PROXYPASSWORD]); +-#endif ++ result = Curl_reset_proxypwd(data); + + data->req.headerbytecount = 0; + Curl_headers_cleanup(data); +diff --git a/lib/transfer.h b/lib/transfer.h +index 8c9b88c..8e28c01 100644 +--- a/lib/transfer.h ++++ b/lib/transfer.h +@@ -31,6 +31,8 @@ char *Curl_checkheaders(const struct Curl_easy *data, + + void Curl_init_CONNECT(struct Curl_easy *data); + ++CURLcode Curl_reset_userpwd(struct Curl_easy *data); ++CURLcode Curl_reset_proxypwd(struct Curl_easy *data); + CURLcode Curl_pretransfer(struct Curl_easy *data); + + typedef enum { +diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am +index 66dbfb7..2516681 100644 +--- a/tests/data/Makefile.am ++++ b/tests/data/Makefile.am +@@ -239,7 +239,7 @@ test1955 test1956 test1957 test1958 test1959 test1960 test1964 \ + test1970 test1971 test1972 test1973 test1974 test1975 test1976 \ + \ + test2000 test2001 test2002 test2003 test2004 test2006 \ +-\ ++test2009 test2010 test2011 \ + test2023 \ + test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 \ + test2032 test2033 test2034 test2035 test2037 test2038 test2039 \ +diff --git a/tests/data/test2009 b/tests/data/test2009 +new file mode 100644 +index 0000000..d2fd79e +--- /dev/null ++++ b/tests/data/test2009 +@@ -0,0 +1,70 @@ ++ ++ ++ ++ ++HTTP ++HTTP proxy ++http_proxy ++ ++ ++# Server-side ++ ++ ++HTTP/1.1 407 Denied ++ ++ ++ ++HTTP/1.1 301 redirect ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Content-Length: 4 ++Content-Type: text/html ++Location: https://another.example/%TESTNUMBER0002 ++ ++boo ++ ++ ++ ++# Client-side ++ ++ ++proxy ++ ++ ++http ++https ++ ++ ++proxy credentials via env variables, redirect from http to https ++ ++ ++ ++http_proxy=http://user:secret@%HOSTIP:%HTTPPORT ++https_proxy=https://%HOSTIP:%HTTPSPORT/ ++ ++ ++http://somewhere.example/ --follow --proxy-insecure ++ ++ ++ ++# Verify data after the test has been "shot" ++ ++ ++GET http://somewhere.example/ HTTP/1.1 ++Host: somewhere.example ++Proxy-Authorization: Basic %b64[user:secret]b64% ++User-Agent: curl/%VERSION ++Accept: */* ++Proxy-Connection: Keep-Alive ++ ++CONNECT another.example:443 HTTP/1.1 ++Host: another.example:443 ++User-Agent: curl/%VERSION ++Proxy-Connection: Keep-Alive ++ ++ ++ ++7 ++ ++ ++ +diff --git a/tests/data/test2010 b/tests/data/test2010 +new file mode 100644 +index 0000000..443ae9d +--- /dev/null ++++ b/tests/data/test2010 +@@ -0,0 +1,71 @@ ++ ++ ++ ++ ++HTTP ++HTTP proxy ++http_proxy ++ ++ ++# Server-side ++ ++ ++HTTP/1.1 407 Denied ++ ++ ++ ++HTTP/1.1 301 redirect ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Content-Length: 4 ++Content-Type: text/html ++Location: https://another.example/%TESTNUMBER0002 ++ ++boo ++ ++ ++ ++# Client-side ++ ++ ++proxy ++ ++ ++http ++https ++ ++ ++proxy credentials via options for two proxies, redirect from http to https ++ ++ ++ ++http_proxy=http://%HOSTIP:%HTTPPORT ++https_proxy=https://%HOSTIP:%HTTPSPORT/ ++ ++ ++--proxy-user batman:robin http://somewhere.example/ --follow --proxy-insecure ++ ++ ++ ++# Verify data after the test has been "shot" ++ ++ ++GET http://somewhere.example/ HTTP/1.1 ++Host: somewhere.example ++Proxy-Authorization: Basic %b64[batman:robin]b64% ++User-Agent: curl/%VERSION ++Accept: */* ++Proxy-Connection: Keep-Alive ++ ++CONNECT another.example:443 HTTP/1.1 ++Host: another.example:443 ++Proxy-Authorization: Basic %b64[batman:robin]b64% ++User-Agent: curl/%VERSION ++Proxy-Connection: Keep-Alive ++ ++ ++ ++7 ++ ++ ++ +diff --git a/tests/data/test2011 b/tests/data/test2011 +new file mode 100644 +index 0000000..dd4e534 +--- /dev/null ++++ b/tests/data/test2011 +@@ -0,0 +1,70 @@ ++ ++ ++ ++ ++HTTP ++HTTP proxy ++http_proxy ++ ++ ++# Server-side ++ ++ ++HTTP/1.1 407 Denied ++ ++ ++ ++HTTP/1.1 301 redirect ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Server: test-server/fake ++Content-Length: 4 ++Content-Type: text/html ++Location: https://another.example/%TESTNUMBER0002 ++ ++boo ++ ++ ++ ++# Client-side ++ ++ ++proxy ++ ++ ++http ++https ++ ++ ++proxy creds via env, cross-scheme redirect, --location-trusted ++ ++ ++ ++http_proxy=http://user:secret@%HOSTIP:%HTTPPORT ++https_proxy=https://%HOSTIP:%HTTPSPORT/ ++ ++ ++http://somewhere.example/ --location-trusted --proxy-insecure ++ ++ ++ ++# Verify data after the test has been "shot" ++ ++ ++GET http://somewhere.example/ HTTP/1.1 ++Host: somewhere.example ++Proxy-Authorization: Basic %b64[user:secret]b64% ++User-Agent: curl/%VERSION ++Accept: */* ++Proxy-Connection: Keep-Alive ++ ++CONNECT another.example:443 HTTP/1.1 ++Host: another.example:443 ++User-Agent: curl/%VERSION ++Proxy-Connection: Keep-Alive ++ ++ ++ ++7 ++ ++ ++ +-- +2.45.4 + diff --git a/SPECS/curl/CVE-2026-6429.patch b/SPECS/curl/CVE-2026-6429.patch new file mode 100644 index 00000000000..16365a7672f --- /dev/null +++ b/SPECS/curl/CVE-2026-6429.patch @@ -0,0 +1,453 @@ +From 2e8c922a89153083227a8f6b737f15bbe9fe1446 Mon Sep 17 00:00:00 2001 +From: Stefan Eissing +Date: Fri, 27 Mar 2026 09:19:34 +0100 +Subject: [PATCH 1/2] http2: prevent secure schemes pushed over insecure & + urlapi: same origin tests + +When assembling the URL of a pushed resource over a not-secured +connection, require the scheme to be known and not secure. + +Add new internal `curl_url_same_origin()` to check if a href has the +same origin as a base URL. Add test cases in test1675 and use this in +http2 push handling. + +Upstream Patch Reference: https://launchpadlibrarian.net/859770351/curl_8.14.1-2ubuntu1.2_8.14.1-2ubuntu1.3.diff.gz +https://github.com/curl/curl/commit/b4024bf808bd558026fdc6.patch +--- + lib/http2.c | 22 +++++++++++++++------- + lib/urlapi-int.h | 2 ++ + lib/urlapi.c | 33 +++++++++++++++++++++++++++++++++ + 3 files changed, 50 insertions(+), 7 deletions(-) + +diff --git a/lib/http2.c b/lib/http2.c +index dbe6f1a..ef29686 100644 +--- a/lib/http2.c ++++ b/lib/http2.c +@@ -883,8 +883,9 @@ static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, + return second; + } + +-static int set_transfer_url(struct Curl_easy *data, +- struct curl_pushheaders *hp) ++static int set_transfer_url(struct Curl_easy *newhandle, ++ struct curl_pushheaders *hp, ++ struct Curl_easy *data) + { + const char *v; + CURLUcode uc; +@@ -922,6 +923,13 @@ static int set_transfer_url(struct Curl_easy *data, + } + } + ++ /* We can only allow PUSH of resource from the same origin, e.g. ++ * scheme + hostname + port */ ++ if(!Curl_url_same_origin(data->state.uh, u)) { ++ rc = 1; ++ goto fail; ++ } ++ + uc = curl_url_get(u, CURLUPART_URL, &url, 0); + if(uc) + rc = 4; +@@ -930,10 +938,10 @@ fail: + if(rc) + return rc; + +- if(data->state.url_alloc) +- free(data->state.url); +- data->state.url_alloc = TRUE; +- data->state.url = url; ++ if(newhandle->state.url_alloc) ++ free(newhandle->state.url); ++ newhandle->state.url_alloc = TRUE; ++ newhandle->state.url = url; + return 0; + } + +@@ -982,7 +990,7 @@ static int push_promise(struct Curl_cfilter *cf, + heads.stream = stream; + heads.frame = frame; + +- rv = set_transfer_url(newhandle, &heads); ++ rv = set_transfer_url(newhandle, &heads, data); + if(rv) { + discard_newhandle(cf, newhandle); + rv = CURL_PUSH_DENY; +diff --git a/lib/urlapi-int.h b/lib/urlapi-int.h +index fcffab2..467f405 100644 +--- a/lib/urlapi-int.h ++++ b/lib/urlapi-int.h +@@ -35,4 +35,6 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, + bool has_scheme); + #endif + ++bool Curl_url_same_origin(CURLU *base, CURLU *href); ++ + #endif /* HEADER_CURL_URLAPI_INT_H */ +diff --git a/lib/urlapi.c b/lib/urlapi.c +index 98c8f6f..6533e8a 100644 +--- a/lib/urlapi.c ++++ b/lib/urlapi.c +@@ -2013,3 +2013,36 @@ nomem: + } + return CURLUE_OK; + } ++ ++bool Curl_url_same_origin(CURLU *base, CURLU *href) ++{ ++ const struct Curl_handler *s = NULL; ++ ++ /* base must be an absolute URL */ ++ if(!base->scheme || !base->host) ++ return FALSE; ++ if(href->scheme && !curl_strequal(base->scheme, href->scheme)) ++ return FALSE; ++ if(href->host) { ++ if(!curl_strequal(base->host, href->host)) ++ return FALSE; ++ if(!curl_strequal(base->port, href->port)) { ++ /* This may still match if only one has an explicit port ++ * and it is the default for the scheme. */ ++ if(base->port && href->port) ++ return FALSE; ++ ++ s = Curl_get_scheme_handler(base->scheme); ++ if(!s) /* Cannot match default port for unknown scheme */ ++ return FALSE; ++ ++ /* The port which is set must be the default one */ ++ if((base->port && (base->portnum != s->defport)) || ++ (href->port && (href->portnum != s->defport))) ++ return FALSE; ++ } ++ } ++ else if(href->port) /* no host in href, then there must be no port */ ++ return FALSE; ++ return TRUE; ++} +-- +2.45.4 + +From b4024bf808bd558026fdc6096e8457f199ace306 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg +Date: Thu, 16 Apr 2026 14:26:20 +0200 +Subject: [PATCH 2/2] 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 + +--- + lib/multi.c | 80 ++++++++++++-------------------------- + tests/data/Makefile.am | 2 +- + tests/data/test2506 | 64 ++++++++++++++++++++++++++++++ + tests/data/test998 | 1 - + tests/libtest/Makefile.inc | 5 ++- + tests/libtest/lib2506.c | 71 +++++++++++++++++++++++++++++++++ + 6 files changed, 165 insertions(+), 58 deletions(-) + create mode 100644 tests/data/test2506 + create mode 100644 tests/libtest/lib2506.c + +diff --git a/lib/multi.c b/lib/multi.c +index 53ab80e..57f7649 100644 +--- a/lib/multi.c ++++ b/lib/multi.c +@@ -1932,71 +1932,41 @@ static CURLcode multi_follow(struct Curl_easy *data, + return CURLE_OUT_OF_MEMORY; + } + else { +- uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); +- if(uc) ++ bool same_origin; ++ CURLcode result; ++ CURLU *u = curl_url(); ++ if(!u) ++ return CURLE_OUT_OF_MEMORY; ++ uc = curl_url_set(u, CURLUPART_URL, ++ data->state.url, ++ CURLU_URLENCODE | CURLU_ALLOW_SPACE); ++ if(!uc) ++ uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); ++ if(uc) { ++ curl_url_cleanup(u); + 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)) { +- char *portnum; +- int port; +- bool clear = FALSE; +- +- if(data->set.use_port && data->state.allow_port) +- /* a custom port is used */ +- port = (int)data->set.use_port; +- else { +- uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, +- CURLU_DEFAULT_PORT); +- if(uc) { +- free(newurl); +- return Curl_uc_to_curlcode(uc); +- } +- port = atoi(portnum); +- 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_handler *p; +- uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); +- if(uc) { +- free(newurl); +- return Curl_uc_to_curlcode(uc); +- } ++ same_origin = Curl_url_same_origin(u, data->state.uh); ++ curl_url_cleanup(u); + +- p = Curl_get_scheme_handler(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; +- } +- free(scheme); +- } +- if(clear) { +- CURLcode result = Curl_reset_userpwd(data); +- if(result) { +- free(newurl); +- return result; +- } +- Curl_safefree(data->state.aptr.user); +- Curl_safefree(data->state.aptr.passwd); ++ if((!same_origin && !data->set.allow_auth_to_other_hosts) || ++ !data->set.str[STRING_USERNAME]) { ++ result = Curl_reset_userpwd(data); ++ if(result) { ++ free(newurl); ++ return result; + } ++ Curl_safefree(data->state.aptr.user); ++ Curl_safefree(data->state.aptr.passwd); + } +- } +- DEBUGASSERT(newurl); +- { +- CURLcode result = Curl_reset_proxypwd(data); ++ result = Curl_reset_proxypwd(data); + if(result) { + free(newurl); + return result; + } + } ++ DEBUGASSERT(newurl); + + 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 2516681..fd69983 100644 +--- a/tests/data/Makefile.am ++++ b/tests/data/Makefile.am +@@ -259,7 +259,7 @@ test2308 test2309 \ + \ + test2400 test2401 test2402 test2403 test2404 test2405 test2406 \ + \ +-test2500 test2501 test2502 test2503 test2504 \ ++test2500 test2501 test2502 test2503 test2504 test2506 \ + \ + test2600 test2601 test2602 test2603 test2604 \ + \ +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 @@ ++ ++ ++ ++ ++HTTP ++cookies ++ ++ ++ ++ ++ ++HTTP/1.1 301 redirect ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Content-Length: 3 ++Location: http://numbertwo.example/%TESTNUMBER0002 ++ ++ok ++ ++ ++HTTP/1.1 200 OK ++Date: Tue, 09 Nov 2010 14:49:00 GMT ++Content-Length: 4 ++ ++yes ++ ++ ++ ++ ++ ++http ++ ++ ++proxy ++ ++ ++lib%TESTNUMBER ++ ++ ++netrc with redirect using proxy ++ ++ ++machine site.example login batman password robin ++ ++ ++http://%HOSTIP:%HTTPPORT http://site.example/ %LOGDIR/netrc2506 ++ ++ ++ ++ ++ ++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 ++ ++ ++ ++ +diff --git a/tests/data/test998 b/tests/data/test998 +index c3a8f51..091c235 100644 +--- a/tests/data/test998 ++++ b/tests/data/test998 +@@ -82,7 +82,6 @@ Proxy-Connection: Keep-Alive + + GET http://somewhere.else.example/a/path/9980002 HTTP/1.1 + Host: somewhere.else.example +-Authorization: Basic YWxiZXJ0bzplaW5zdGVpbg== + User-Agent: curl/%VERSION + Accept: */* + Proxy-Connection: Keep-Alive +diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc +index 99cbc18..49e568e 100644 +--- a/tests/libtest/Makefile.inc ++++ b/tests/libtest/Makefile.inc +@@ -79,7 +79,7 @@ LIBTESTPROGS = libauthretry libntlmconnect libprereq \ + lib1970 lib1971 lib1972 lib1973 lib1974 lib1975 \ + lib2301 lib2302 lib2304 lib2305 lib2306 lib2308 lib2309 \ + lib2402 lib2404 lib2405 \ +- lib2502 lib2504 \ ++ lib2502 lib2504 lib2506 \ + lib3010 lib3025 lib3026 lib3027 \ + lib3100 lib3101 lib3102 lib3103 lib3207 + +@@ -704,6 +704,9 @@ lib2502_LDADD = $(TESTUTIL_LIBS) + lib2504_SOURCES = lib2504.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) + lib2504_LDADD = $(TESTUTIL_LIBS) + ++lib2506_SOURCES = lib2506.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) ++lib2506_LDADD = $(TESTUTIL_LIBS) ++ + lib3010_SOURCES = lib3010.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) + lib3010_LDADD = $(TESTUTIL_LIBS) + +diff --git a/tests/libtest/lib2506.c b/tests/libtest/lib2506.c +new file mode 100644 +index 0000000..a09e16a +--- /dev/null ++++ b/tests/libtest/lib2506.c +@@ -0,0 +1,71 @@ ++/*************************************************************************** ++ * _ _ ____ _ ++ * Project ___| | | | _ \| | ++ * / __| | | | |_) | | ++ * | (__| |_| | _ <| |___ ++ * \___|\___/|_| \_\_____| ++ * ++ * Copyright (C) Linus Nielsen Feltzing ++ * ++ * 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 "test.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; ++} ++ ++CURLcode test(char *URL) ++{ ++ CURL *curl; ++ CURLcode res = 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); ++ ++ res = curl_easy_perform(curl); ++ ++test_cleanup: ++ curl_easy_cleanup(curl); ++ curl_global_cleanup(); ++ ++ return res; ++} +-- +2.45.4 + diff --git a/SPECS/curl/curl.spec b/SPECS/curl/curl.spec index f164a0bb01a..3d7b38581ee 100644 --- a/SPECS/curl/curl.spec +++ b/SPECS/curl/curl.spec @@ -1,7 +1,7 @@ Summary: An URL retrieval utility and library Name: curl Version: 8.11.1 -Release: 7%{?dist} +Release: 8%{?dist} License: curl Vendor: Microsoft Corporation Distribution: Azure Linux @@ -19,6 +19,9 @@ Patch7: CVE-2026-3784.patch Patch8: CVE-2026-4873.patch Patch9: CVE-2026-6276.patch Patch10: CVE-2026-7168.patch +Patch11: CVE-2026-5545.patch +Patch12: CVE-2026-6253.patch +Patch13: CVE-2026-6429.patch BuildRequires: cmake BuildRequires: krb5-devel BuildRequires: libnghttp2-devel @@ -109,6 +112,9 @@ find %{buildroot} -type f -name "*.la" -delete -print %{_libdir}/libcurl.so.* %changelog +* Tue May 26 2026 Jyoti Kanase - 8.11.1-8 +- Patch for CVE-2026-6253, CVE-2026-6429, CVE-2026-5545 + * Thu May 14 2026 Azure Linux Security Servicing Account - 8.11.1-7 - Patch for CVE-2026-7168, CVE-2026-6276, CVE-2026-4873 diff --git a/SPECS/openssh/CVE-2026-35414.patch b/SPECS/openssh/CVE-2026-35414.patch new file mode 100644 index 00000000000..19398879179 --- /dev/null +++ b/SPECS/openssh/CVE-2026-35414.patch @@ -0,0 +1,457 @@ +From 5166b6cbf2b6103117a79f90a68068e89e02bf66 Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Mon, 22 Dec 2025 01:49:03 +0000 +Subject: [PATCH] upstream: When certificate support was added to OpenSSH, + +certificates were originally specified to represent any principal if the +principals list was empty. + +This was, in retrospect, a mistake as it created a fail-open +situation if a CA could be convinced to accidentally sign a +certificate with no principals. This actually happened in a 3rd- +party CA product (CVE-2024-7594). + +Somewhat fortunately, the main pathway for using certificates in +sshd (TrustedUserCAKeys) never supported empty-principals +certificates, so the blast radius of such mistakes was +substantially reduced. + +This change removes this footcannon and requires all certificates +include principals sections. It also fixes interpretation of +wildcard principals, and properly enables them for host +certificates only. + +This is a behaviour change that will permanently break uses of +certificates with empty principals sections. + +ok markus@ + +OpenBSD-Commit-ID: 0a901f03c567c100724a492cf91e02939904712e +-------------------- +Additional patches from upstream are added to fix ptest. +Since the CVE fix is a behaviour change, the ptest changes are included in the same patch to avoid breaking the test suite on the old behaviour. +Before patch: A certificate with no principals = valid for everyone (wildcard behaviour) +After patch: A certificate with no principals = rejected immediately, no exceptions, no config to override it + +Upstream Patch references: +For CVE fix: https://github.com/openssh/openssh-portable/commit/5166b6cbf2b6103117a79f90a68068e89e02bf66.patch +For ptest fix: +1. https://github.com/openssh/openssh-portable/commit/ecdf9b9f8e89aae65d4a12fe5a25c560eea08393.patch +2. https://github.com/openssh/openssh-portable/commit/09daf2ac5f248dc5d60a6f3a703b479d67da14b4.patch +--- + auth2-hostbased.c | 4 +-- + auth2-pubkey.c | 2 +- + auth2-pubkeyfile.c | 2 +- + regress/cert-hostkey.sh | 43 ++++++++++++++++++++++---------- + regress/cert-userkey.sh | 7 +++--- + ssh-agent.c | 2 +- + ssh-keygen.1 | 32 +++++++++++++++++------- + ssh-keygen.c | 9 +++++++ + sshconnect.c | 2 +- + sshkey.c | 55 +++++++++++++++++++---------------------- + sshkey.h | 6 ++--- + sshsig.c | 6 ++--- + 12 files changed, 103 insertions(+), 67 deletions(-) + +diff --git a/auth2-hostbased.c b/auth2-hostbased.c +index eb21479..4022d92 100644 +--- a/auth2-hostbased.c ++++ b/auth2-hostbased.c +@@ -211,8 +211,8 @@ hostbased_key_allowed(struct ssh *ssh, struct passwd *pw, + } + debug2_f("access allowed by auth_rhosts2"); + +- if (sshkey_is_cert(key) && +- sshkey_cert_check_authority_now(key, 1, 0, 0, lookup, &reason)) { ++ if (sshkey_is_cert(key) && sshkey_cert_check_host(key, lookup, ++ options.ca_sign_algorithms, &reason) != 0) { + error("%s", reason); + auth_debug_add("%s", reason); + return 0; +diff --git a/auth2-pubkey.c b/auth2-pubkey.c +index 7580db7..2a108ea 100644 +--- a/auth2-pubkey.c ++++ b/auth2-pubkey.c +@@ -528,7 +528,7 @@ user_cert_trusted_ca(struct passwd *pw, struct sshkey *key, + } + if (use_authorized_principals && principals_opts == NULL) + fatal_f("internal error: missing principals_opts"); +- if (sshkey_cert_check_authority_now(key, 0, 1, 0, ++ if (sshkey_cert_check_authority_now(key, 0, 0, + use_authorized_principals ? NULL : pw->pw_name, &reason) != 0) + goto fail_reason; + +diff --git a/auth2-pubkeyfile.c b/auth2-pubkeyfile.c +index 31e7481..556550a 100644 +--- a/auth2-pubkeyfile.c ++++ b/auth2-pubkeyfile.c +@@ -364,7 +364,7 @@ auth_check_authkey_line(struct passwd *pw, struct sshkey *key, + reason = "Certificate does not contain an authorized principal"; + goto fail_reason; + } +- if (sshkey_cert_check_authority_now(key, 0, 0, 0, ++ if (sshkey_cert_check_authority_now(key, 0, 0, + keyopts->cert_principals == NULL ? pw->pw_name : NULL, + &reason) != 0) + goto fail_reason; +diff --git a/regress/cert-hostkey.sh b/regress/cert-hostkey.sh +index a3414e1..5db1ccb 100644 +--- a/regress/cert-hostkey.sh ++++ b/regress/cert-hostkey.sh +@@ -208,18 +208,32 @@ kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig + cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert + + test_one() { +- ident=$1 +- result=$2 +- sign_opts=$3 ++ ident="$1" ++ result="$2" ++ hosts="$3" ++ sign_opts="$4" ++ ++ test -z "$hosts" || sign_opts="$sign_opts -n $hosts" + + for kt in $PLAIN_TYPES; do + case $ktype in + rsa-sha2-*) tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;; + *) tflag=""; ca="$OBJ/host_ca_key" ;; + esac +- ${SSHKEYGEN} -q -s $ca $tflag -I "regress host key for $USER" \ +- $sign_opts $OBJ/cert_host_key_${kt} || +- fatal "couldn't sign cert_host_key_${kt}" ++ if test -z "$hosts" ; then ++ # Empty principals section. ++ ${SSHKEYGEN} -q -s $ca $tflag $sign_opts \ ++ -I "regress host key for $USER" \ ++ $OBJ/cert_host_key_${kt} 2>/dev/null || ++ fatal "couldn't sign cert_host_key_${kt}" ++ else ++ # Be careful with quoting principals, which may contain ++ # wilcards. ++ ${SSHKEYGEN} -q -s $ca $tflag $sign_opts \ ++ -I "regress host key for $USER" -n "$hosts" \ ++ $OBJ/cert_host_key_${kt} || ++ fatal "couldn't sign cert_host_key_${kt}" ++ fi + ( + cat $OBJ/sshd_proxy_bak + echo HostKey $OBJ/cert_host_key_${kt} +@@ -243,13 +257,16 @@ test_one() { + done + } + +-test_one "user-certificate" failure "-n $HOSTS" +-test_one "empty principals" success "-h" +-test_one "wrong principals" failure "-h -n foo" +-test_one "cert not yet valid" failure "-h -V20300101:20320101" +-test_one "cert expired" failure "-h -V19800101:19900101" +-test_one "cert valid interval" success "-h -V-1w:+2w" +-test_one "cert has constraints" failure "-h -Oforce-command=false" ++test_one "simple" success $HOSTS "-h" ++test_one "wildcard" success "loc*" "-h" ++test_one "user-certificate" failure $HOSTS ++test_one "wildcard user" failure "local*" ++test_one "empty principals" failure "" "-h" ++test_one "wrong principals" failure foo "-h" ++test_one "cert not yet valid" failure $HOSTS "-h -V20300101:20320101" ++test_one "cert expired" failure $HOSTS "-h -V19800101:19900101" ++test_one "cert valid interval" success $HOSTS "-h -V-1w:+2w" ++test_one "cert has constraints" failure $HOSTS "-h -Oforce-command=false" + + # Check downgrade of cert to raw key when no CA found + for ktype in $PLAIN_TYPES ; do +diff --git a/regress/cert-userkey.sh b/regress/cert-userkey.sh +index 4ea29b7..69b58eb 100644 +--- a/regress/cert-userkey.sh ++++ b/regress/cert-userkey.sh +@@ -330,16 +330,15 @@ test_one() { + } + + test_one "correct principal" success "-n ${USER}" ++test_one "correct principal" success "-n ${USER},*" + test_one "host-certificate" failure "-n ${USER} -h" +-test_one "wrong principals" failure "-n foo" ++test_one "wrong principals" failure "-n foo,*" + test_one "cert not yet valid" failure "-n ${USER} -V20300101:20320101" + test_one "cert expired" failure "-n ${USER} -V19800101:19900101" + test_one "cert valid interval" success "-n ${USER} -V-1w:+2w" + test_one "wrong source-address" failure "-n ${USER} -Osource-address=10.0.0.0/8" + test_one "force-command" failure "-n ${USER} -Oforce-command=false" +- +-# Behaviour is different here: TrustedUserCAKeys doesn't allow empty principals +-test_one "empty principals" success "" authorized_keys ++test_one "empty principals" failure "" authorized_keys + test_one "empty principals" failure "" TrustedUserCAKeys + + # Check explicitly-specified principals: an empty principals list in the cert +diff --git a/ssh-agent.c b/ssh-agent.c +index fc8e459..6e1f8d8 100644 +--- a/ssh-agent.c ++++ b/ssh-agent.c +@@ -397,7 +397,7 @@ match_key_hop(const char *tag, const struct sshkey *key, + return -1; /* shouldn't happen */ + if (!sshkey_equal(key->cert->signature_key, dch->keys[i])) + continue; +- if (sshkey_cert_check_host(key, hostname, 1, ++ if (sshkey_cert_check_host(key, hostname, + SSH_ALLOWED_CA_SIGALGS, &reason) != 0) { + debug_f("cert %s / hostname %s rejected: %s", + key->cert->key_id, hostname, reason); +diff --git a/ssh-keygen.1 b/ssh-keygen.1 +index df6803f..7e1fdf4 100644 +--- a/ssh-keygen.1 ++++ b/ssh-keygen.1 +@@ -35,7 +35,7 @@ + .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + .\" +-.Dd $Mdocdate: June 17 2024 $ ++.Dd $Mdocdate: December 22 2025 $ + .Dt SSH-KEYGEN 1 + .Os + .Sh NAME +@@ -905,15 +905,29 @@ User certificates authenticate users to servers, whereas host certificates + authenticate server hosts to users. + To generate a user certificate: + .Pp +-.Dl $ ssh-keygen -s /path/to/ca_key -I key_id /path/to/user_key.pub ++.Dl $ ssh-keygen -s /path/to/ca_key -I id -n user \e ++.Dl \ \ \ \ \ \ /path/to/user_key.pub + .Pp + The resultant certificate will be placed in + .Pa /path/to/user_key-cert.pub . ++The argument to ++.Fl I ++is a key identifier that will be used in logs and may be used to revoke ++keys. ++The argument to ++.Fl n ++is one or more (comma-separated) principals, typically usernames, that ++the certificate represents. + A host certificate requires the + .Fl h + option: + .Pp +-.Dl $ ssh-keygen -s /path/to/ca_key -I key_id -h /path/to/host_key.pub ++.Dl $ ssh-keygen -s /path/to/ca_key -I id -h -n foo.example.org \e ++.Dl \ \ \ \ \ \ /path/to/host_key.pub ++.Pp ++For host certificates, the principals specified using the ++.Fl n ++argument are hostnames and may contain wildcard characters. + .Pp + The host certificate will be output to + .Pa /path/to/host_key-cert.pub . +@@ -925,7 +939,8 @@ and identifying the CA key by providing its public half as an argument + to + .Fl s : + .Pp +-.Dl $ ssh-keygen -s ca_key.pub -D libpkcs11.so -I key_id user_key.pub ++.Dl $ ssh-keygen -s ca_key.pub -D libpkcs11.so -I id -n user \e ++.Dl \ \ \ \ \ \ user_key.pub + .Pp + Similarly, it is possible for the CA key to be hosted in a + .Xr ssh-agent 1 . +@@ -933,20 +948,19 @@ This is indicated by the + .Fl U + flag and, again, the CA key must be identified by its public half. + .Pp +-.Dl $ ssh-keygen -Us ca_key.pub -I key_id user_key.pub ++.Dl $ ssh-keygen -Us ca_key.pub -I id -n user user_key.pub + .Pp + In all cases, + .Ar key_id + is a "key identifier" that is logged by the server when the certificate + is used for authentication. + .Pp +-Certificates may be limited to be valid for a set of principal (user/host) ++Certificates are limited to be valid for a set of principal (user/host) + names. +-By default, generated certificates are valid for all users or hosts. + To generate a certificate for a specified set of principals: + .Pp +-.Dl $ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub +-.Dl "$ ssh-keygen -s ca_key -I key_id -h -n host.domain host_key.pub" ++.Dl $ ssh-keygen -s ca_key -I id -n user1,user2 user_key.pub ++.Dl $ ssh-keygen -s ca_key -I id -h -n host.domain host_key.pub + .Pp + Additional limitations on the validity and use of user certificates may + be specified through certificate options. +diff --git a/ssh-keygen.c b/ssh-keygen.c +index 97c6d13..f7a8e55 100644 +--- a/ssh-keygen.c ++++ b/ssh-keygen.c +@@ -3713,6 +3713,15 @@ main(int argc, char **argv) + if (ca_key_path != NULL) { + if (cert_key_id == NULL) + fatal("Must specify key id (-I) when certifying"); ++ if (cert_principals == NULL) { ++ /* ++ * Ideally this would be a fatal(), but we need to ++ * be able to generate such certificates for testing ++ * even though they will be rejected. ++ */ ++ error("Warning: certificate will contain no " ++ "principals (-n)"); ++ } + for (i = 0; i < nopts; i++) + add_cert_option(opts[i]); + do_ca_sign(pw, ca_key_path, prefer_agent, +diff --git a/sshconnect.c b/sshconnect.c +index 7cf6b63..2f5266c 100644 +--- a/sshconnect.c ++++ b/sshconnect.c +@@ -1092,7 +1092,7 @@ check_host_key(char *hostname, const struct ssh_conn_info *cinfo, + if (want_cert) { + if (sshkey_cert_check_host(host_key, + options.host_key_alias == NULL ? +- hostname : options.host_key_alias, 0, ++ hostname : options.host_key_alias, + options.ca_sign_algorithms, &fail_reason) != 0) { + error("%s", fail_reason); + goto fail; +diff --git a/sshkey.c b/sshkey.c +index d4356e7..23fa340 100644 +--- a/sshkey.c ++++ b/sshkey.c +@@ -2302,8 +2302,8 @@ sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg, + + int + sshkey_cert_check_authority(const struct sshkey *k, +- int want_host, int require_principal, int wildcard_pattern, +- uint64_t verify_time, const char *name, const char **reason) ++ int want_host, int wildcard_pattern, uint64_t verify_time, ++ const char *name, const char **reason) + { + u_int i, principal_matches; + +@@ -2333,37 +2333,36 @@ sshkey_cert_check_authority(const struct sshkey *k, + return SSH_ERR_KEY_CERT_INVALID; + } + if (k->cert->nprincipals == 0) { +- if (require_principal) { +- *reason = "Certificate lacks principal list"; +- return SSH_ERR_KEY_CERT_INVALID; +- } +- } else if (name != NULL) { +- principal_matches = 0; +- for (i = 0; i < k->cert->nprincipals; i++) { +- if (wildcard_pattern) { +- if (match_pattern(k->cert->principals[i], +- name)) { +- principal_matches = 1; +- break; +- } +- } else if (strcmp(name, k->cert->principals[i]) == 0) { ++ *reason = "Certificate lacks principal list"; ++ return SSH_ERR_KEY_CERT_INVALID; ++ } ++ if (name == NULL) ++ return 0; /* principal matching not requested */ ++ ++ principal_matches = 0; ++ for (i = 0; i < k->cert->nprincipals; i++) { ++ if (wildcard_pattern) { ++ if (match_pattern(name, k->cert->principals[i])) { + principal_matches = 1; + break; + } ++ } else if (strcmp(name, k->cert->principals[i]) == 0) { ++ principal_matches = 1; ++ break; + } +- if (!principal_matches) { +- *reason = "Certificate invalid: name is not a listed " +- "principal"; +- return SSH_ERR_KEY_CERT_INVALID; +- } ++ } ++ if (!principal_matches) { ++ *reason = "Certificate invalid: name is not a listed " ++ "principal"; ++ return SSH_ERR_KEY_CERT_INVALID; + } + return 0; + } + + int + sshkey_cert_check_authority_now(const struct sshkey *k, +- int want_host, int require_principal, int wildcard_pattern, +- const char *name, const char **reason) ++ int want_host, int wildcard_pattern, const char *name, ++ const char **reason) + { + time_t now; + +@@ -2372,19 +2371,17 @@ sshkey_cert_check_authority_now(const struct sshkey *k, + *reason = "Certificate invalid: not yet valid"; + return SSH_ERR_KEY_CERT_INVALID; + } +- return sshkey_cert_check_authority(k, want_host, require_principal, +- wildcard_pattern, (uint64_t)now, name, reason); ++ return sshkey_cert_check_authority(k, want_host, wildcard_pattern, ++ (uint64_t)now, name, reason); + } + + int + sshkey_cert_check_host(const struct sshkey *key, const char *host, +- int wildcard_principals, const char *ca_sign_algorithms, +- const char **reason) ++ const char *ca_sign_algorithms, const char **reason) + { + int r; + +- if ((r = sshkey_cert_check_authority_now(key, 1, 0, wildcard_principals, +- host, reason)) != 0) ++ if ((r = sshkey_cert_check_authority_now(key, 1, 1, host, reason)) != 0) + return r; + if (sshbuf_len(key->cert->critical) != 0) { + *reason = "Certificate contains unsupported critical options"; +diff --git a/sshkey.h b/sshkey.h +index 32933bb..6004853 100644 +--- a/sshkey.h ++++ b/sshkey.h +@@ -234,12 +234,12 @@ int sshkey_match_keyname_to_sigalgs(const char *, const char *); + int sshkey_to_certified(struct sshkey *); + int sshkey_drop_cert(struct sshkey *); + int sshkey_cert_copy(const struct sshkey *, struct sshkey *); +-int sshkey_cert_check_authority(const struct sshkey *, int, int, int, ++int sshkey_cert_check_authority(const struct sshkey *, int, int, + uint64_t, const char *, const char **); +-int sshkey_cert_check_authority_now(const struct sshkey *, int, int, int, ++int sshkey_cert_check_authority_now(const struct sshkey *, int, int, + const char *, const char **); + int sshkey_cert_check_host(const struct sshkey *, const char *, +- int , const char *, const char **); ++ const char *, const char **); + size_t sshkey_format_cert_validity(const struct sshkey_cert *, + char *, size_t) __attribute__((__bounded__(__string__, 2, 3))); + int sshkey_check_cert_sigtype(const struct sshkey *, const char *); +diff --git a/sshsig.c b/sshsig.c +index 057e1df..aa32cf3 100644 +--- a/sshsig.c ++++ b/sshsig.c +@@ -849,8 +849,8 @@ cert_filter_principals(const char *path, u_long linenum, + + while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') { + /* Check certificate validity */ +- if ((r = sshkey_cert_check_authority(cert, 0, 1, 0, +- verify_time, NULL, &reason)) != 0) { ++ if ((r = sshkey_cert_check_authority(cert, 0, 0, verify_time, ++ NULL, &reason)) != 0) { + debug("%s:%lu: principal \"%s\" not authorized: %s", + path, linenum, cp, reason); + continue; +@@ -915,7 +915,7 @@ check_allowed_keys_line(const char *path, u_long linenum, char *line, + sshkey_equal_public(sign_key->cert->signature_key, found_key)) { + if (principal) { + /* Match certificate CA key with specified principal */ +- if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0, ++ if ((r = sshkey_cert_check_authority(sign_key, 0, 0, + verify_time, principal, &reason)) != 0) { + error("%s:%lu: certificate not authorized: %s", + path, linenum, reason); +-- +2.43.0 + diff --git a/SPECS/openssh/openssh.spec b/SPECS/openssh/openssh.spec index 5fb163fbc0e..9572637cbb6 100644 --- a/SPECS/openssh/openssh.spec +++ b/SPECS/openssh/openssh.spec @@ -3,7 +3,7 @@ Summary: Free version of the SSH connectivity tools Name: openssh Version: %{openssh_ver} -Release: 6%{?dist} +Release: 7%{?dist} License: BSD Vendor: Microsoft Corporation Distribution: Azure Linux @@ -38,13 +38,15 @@ Patch400: CVE-2025-26465.patch Patch401: CVE-2025-32728.patch Patch402: CVE-2025-61984.patch Patch403: CVE-2025-61985.patch +Patch404: CVE-2026-35385.patch +Patch405: CVE-2026-35386.patch +Patch406: CVE-2026-35388.patch +Patch407: CVE-2026-35414.patch + # sk-dummy.so built with -fvisibility=hidden does not work # The tests fail with the following error: # dlsym(sk_api_version) failed: (...)/sk-dummy.so: undefined symbol: sk_api_version -Patch965: openssh-8.2p1-visibility.patch -Patch966: CVE-2026-35385.patch -Patch967: CVE-2026-35386.patch -Patch968: CVE-2026-35388.patch +Patch965: openssh-8.2p1-visibility.patch BuildRequires: audit-devel BuildRequires: autoconf @@ -106,9 +108,6 @@ The module is most useful for su and sudo service stacks. %prep %setup -q -a 3 -%patch 966 -p1 -%patch 967 -p1 -%patch 968 -p1 pushd pam_ssh_agent_auth-%{pam_ssh_agent_ver} %patch -P 300 -p2 -b .psaa-build @@ -127,6 +126,10 @@ popd %patch -P 965 -p1 -b .visibility %patch -P 402 -p1 -b .CVE-2025-61984.patch %patch -P 403 -p1 -b .CVE-2025-61985.patch +%patch -P 404 -p1 -b .CVE-2026-35385.patch +%patch -P 405 -p1 -b .CVE-2026-35386.patch +%patch -P 406 -p1 -b .CVE-2026-35388.patch +%patch -P 407 -p1 -b .CVE-2026-35414.patch %build # The -fvisibility=hidden is needed for clean build of the pam_ssh_agent_auth. @@ -285,6 +288,9 @@ fi %{_mandir}/man8/ssh-sk-helper.8.gz %changelog +* Wed Apr 22 2026 Azure Linux Security Servicing Account - 9.8p1-7 +- Patch CVE-2026-35414 + * Mon Apr 06 2026 Azure Linux Security Servicing Account - 9.8p1-6 - Patch CVE-2026-35385, CVE-2026-35386, CVE-2026-35388 diff --git a/toolkit/resources/manifests/package/pkggen_core_aarch64.txt b/toolkit/resources/manifests/package/pkggen_core_aarch64.txt index f3a12e5625b..05bae71819a 100644 --- a/toolkit/resources/manifests/package/pkggen_core_aarch64.txt +++ b/toolkit/resources/manifests/package/pkggen_core_aarch64.txt @@ -199,9 +199,9 @@ krb5-1.21.3-4.azl3.aarch64.rpm krb5-devel-1.21.3-4.azl3.aarch64.rpm nghttp2-1.61.0-3.azl3.aarch64.rpm nghttp2-devel-1.61.0-3.azl3.aarch64.rpm -curl-8.11.1-7.azl3.aarch64.rpm -curl-devel-8.11.1-7.azl3.aarch64.rpm -curl-libs-8.11.1-7.azl3.aarch64.rpm +curl-8.11.1-8.azl3.aarch64.rpm +curl-devel-8.11.1-8.azl3.aarch64.rpm +curl-libs-8.11.1-8.azl3.aarch64.rpm createrepo_c-1.0.3-1.azl3.aarch64.rpm libxml2-2.11.5-9.azl3.aarch64.rpm libxml2-devel-2.11.5-9.azl3.aarch64.rpm diff --git a/toolkit/resources/manifests/package/pkggen_core_x86_64.txt b/toolkit/resources/manifests/package/pkggen_core_x86_64.txt index 292763b52ee..877f5febed7 100644 --- a/toolkit/resources/manifests/package/pkggen_core_x86_64.txt +++ b/toolkit/resources/manifests/package/pkggen_core_x86_64.txt @@ -199,9 +199,9 @@ krb5-1.21.3-4.azl3.x86_64.rpm krb5-devel-1.21.3-4.azl3.x86_64.rpm nghttp2-1.61.0-3.azl3.x86_64.rpm nghttp2-devel-1.61.0-3.azl3.x86_64.rpm -curl-8.11.1-7.azl3.x86_64.rpm -curl-devel-8.11.1-7.azl3.x86_64.rpm -curl-libs-8.11.1-7.azl3.x86_64.rpm +curl-8.11.1-8.azl3.x86_64.rpm +curl-devel-8.11.1-8.azl3.x86_64.rpm +curl-libs-8.11.1-8.azl3.x86_64.rpm createrepo_c-1.0.3-1.azl3.x86_64.rpm libxml2-2.11.5-9.azl3.x86_64.rpm libxml2-devel-2.11.5-9.azl3.x86_64.rpm diff --git a/toolkit/resources/manifests/package/toolchain_aarch64.txt b/toolkit/resources/manifests/package/toolchain_aarch64.txt index c0a94fb915d..2506b9a6a28 100644 --- a/toolkit/resources/manifests/package/toolchain_aarch64.txt +++ b/toolkit/resources/manifests/package/toolchain_aarch64.txt @@ -51,8 +51,8 @@ check-debuginfo-0.15.2-1.azl3.aarch64.rpm chkconfig-1.25-1.azl3.aarch64.rpm chkconfig-debuginfo-1.25-1.azl3.aarch64.rpm chkconfig-lang-1.25-1.azl3.aarch64.rpm -cmake-3.30.3-13.azl3.aarch64.rpm -cmake-debuginfo-3.30.3-13.azl3.aarch64.rpm +cmake-3.30.3-14.azl3.aarch64.rpm +cmake-debuginfo-3.30.3-14.azl3.aarch64.rpm coreutils-9.4-6.azl3.aarch64.rpm coreutils-debuginfo-9.4-6.azl3.aarch64.rpm coreutils-lang-9.4-6.azl3.aarch64.rpm @@ -67,10 +67,10 @@ cracklib-lang-2.9.11-1.azl3.aarch64.rpm createrepo_c-1.0.3-1.azl3.aarch64.rpm createrepo_c-debuginfo-1.0.3-1.azl3.aarch64.rpm createrepo_c-devel-1.0.3-1.azl3.aarch64.rpm -curl-8.11.1-7.azl3.aarch64.rpm -curl-debuginfo-8.11.1-7.azl3.aarch64.rpm -curl-devel-8.11.1-7.azl3.aarch64.rpm -curl-libs-8.11.1-7.azl3.aarch64.rpm +curl-8.11.1-8.azl3.aarch64.rpm +curl-debuginfo-8.11.1-8.azl3.aarch64.rpm +curl-devel-8.11.1-8.azl3.aarch64.rpm +curl-libs-8.11.1-8.azl3.aarch64.rpm Cython-debuginfo-3.0.5-3.azl3.aarch64.rpm debugedit-5.0-2.azl3.aarch64.rpm debugedit-debuginfo-5.0-2.azl3.aarch64.rpm diff --git a/toolkit/resources/manifests/package/toolchain_x86_64.txt b/toolkit/resources/manifests/package/toolchain_x86_64.txt index 5dce4dc188e..ac399c72758 100644 --- a/toolkit/resources/manifests/package/toolchain_x86_64.txt +++ b/toolkit/resources/manifests/package/toolchain_x86_64.txt @@ -54,8 +54,8 @@ check-debuginfo-0.15.2-1.azl3.x86_64.rpm chkconfig-1.25-1.azl3.x86_64.rpm chkconfig-debuginfo-1.25-1.azl3.x86_64.rpm chkconfig-lang-1.25-1.azl3.x86_64.rpm -cmake-3.30.3-13.azl3.x86_64.rpm -cmake-debuginfo-3.30.3-13.azl3.x86_64.rpm +cmake-3.30.3-14.azl3.x86_64.rpm +cmake-debuginfo-3.30.3-14.azl3.x86_64.rpm coreutils-9.4-6.azl3.x86_64.rpm coreutils-debuginfo-9.4-6.azl3.x86_64.rpm coreutils-lang-9.4-6.azl3.x86_64.rpm @@ -72,10 +72,10 @@ createrepo_c-debuginfo-1.0.3-1.azl3.x86_64.rpm createrepo_c-devel-1.0.3-1.azl3.x86_64.rpm cross-binutils-common-2.41-13.azl3.noarch.rpm cross-gcc-common-13.2.0-7.azl3.noarch.rpm -curl-8.11.1-7.azl3.x86_64.rpm -curl-debuginfo-8.11.1-7.azl3.x86_64.rpm -curl-devel-8.11.1-7.azl3.x86_64.rpm -curl-libs-8.11.1-7.azl3.x86_64.rpm +curl-8.11.1-8.azl3.x86_64.rpm +curl-debuginfo-8.11.1-8.azl3.x86_64.rpm +curl-devel-8.11.1-8.azl3.x86_64.rpm +curl-libs-8.11.1-8.azl3.x86_64.rpm Cython-debuginfo-3.0.5-3.azl3.x86_64.rpm debugedit-5.0-2.azl3.x86_64.rpm debugedit-debuginfo-5.0-2.azl3.x86_64.rpm