import UBI nghttp2-1.43.0-6.el9_8.2
This commit is contained in:
parent
8176746a9e
commit
c5e0409db7
298
SOURCES/0004-nghttp2-1.43.0-CVE-2026-58055.patch
Normal file
298
SOURCES/0004-nghttp2-1.43.0-CVE-2026-58055.patch
Normal file
@ -0,0 +1,298 @@
|
||||
From 0d7b33e45e6559f6b12932b5b71a4cb18dbb8fcb Mon Sep 17 00:00:00 2001
|
||||
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
|
||||
Date: Fri, 22 May 2026 21:26:44 +0900
|
||||
Subject: [PATCH] nghttpx: Tighten up CONNECT and HTTP Upgrade handling
|
||||
|
||||
(cherry picked from commit ab28105c4a0197da24f8bfc414bc116055249e1e)
|
||||
---
|
||||
src/shrpx_downstream.cc | 3 +-
|
||||
src/shrpx_downstream.h | 7 ++-
|
||||
src/shrpx_http2_upstream.cc | 8 +++
|
||||
src/shrpx_http_downstream_connection.cc | 68 +++++++++++++++++++++----
|
||||
src/shrpx_http_downstream_connection.h | 5 ++
|
||||
src/shrpx_https_upstream.cc | 36 +++++++++++--
|
||||
6 files changed, 111 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc
|
||||
index dc7762d0..51810297 100644
|
||||
--- a/src/shrpx_downstream.cc
|
||||
+++ b/src/shrpx_downstream.cc
|
||||
@@ -1108,7 +1108,8 @@ bool Downstream::can_detach_downstream_connection() const {
|
||||
// state, especially for HTTP/1.1
|
||||
return dconn_ && response_state_ == DownstreamState::MSG_COMPLETE &&
|
||||
request_state_ == DownstreamState::MSG_COMPLETE && !upgraded_ &&
|
||||
- !resp_.connection_close && request_buf_.rleft() == 0;
|
||||
+ !resp_.connection_close && blocked_request_buf_.rleft() == 0 &&
|
||||
+ request_buf_.rleft() == 0;
|
||||
}
|
||||
|
||||
DefaultMemchunks Downstream::pop_response_buf() {
|
||||
diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h
|
||||
index fff49a1a..0a479519 100644
|
||||
--- a/src/shrpx_downstream.h
|
||||
+++ b/src/shrpx_downstream.h
|
||||
@@ -160,7 +160,8 @@ struct Request {
|
||||
connection_close(false),
|
||||
http2_expect_body(false),
|
||||
no_authority(false),
|
||||
- forwarded_once(false) {}
|
||||
+ forwarded_once(false),
|
||||
+ http1_msg_complete(false) {}
|
||||
|
||||
void consume(size_t len) {
|
||||
assert(unconsumed_body_length >= len);
|
||||
@@ -226,6 +227,10 @@ struct Request {
|
||||
// orig_authority and orig_path have the authority and path which
|
||||
// are used for the first backend selection.
|
||||
bool forwarded_once;
|
||||
+ // true if HTTP/1 request message has been completed. This field is
|
||||
+ // added because Downstream::get_request_state() might be altered
|
||||
+ // from DownstreamState::MSG_COMPLETE.
|
||||
+ bool http1_msg_complete{};
|
||||
};
|
||||
|
||||
struct Response {
|
||||
diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc
|
||||
index 9b9e5523..406c713c 100644
|
||||
--- a/src/shrpx_http2_upstream.cc
|
||||
+++ b/src/shrpx_http2_upstream.cc
|
||||
@@ -359,6 +359,14 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
+ if (method_token == HTTP_CONNECT && content_length) {
|
||||
+ if (LOG_ENABLED(INFO)) {
|
||||
+ ULOG(INFO, this) << "content-length are not allowed in CONNECT request";
|
||||
+ }
|
||||
+
|
||||
+ return error_reply(downstream, 400);
|
||||
+ }
|
||||
+
|
||||
auto faddr = handler_->get_upstream_addr();
|
||||
|
||||
// For HTTP/2 proxy, we require :authority.
|
||||
diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc
|
||||
index f68cfcf5..0cecc1ce 100644
|
||||
--- a/src/shrpx_http_downstream_connection.cc
|
||||
+++ b/src/shrpx_http_downstream_connection.cc
|
||||
@@ -709,6 +709,34 @@ int HttpDownstreamConnection::push_request_headers() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
+bool HttpDownstreamConnection::should_block_request_body() const {
|
||||
+ const auto &req = downstream_->request();
|
||||
+
|
||||
+ return !downstream_->get_request_header_sent() ||
|
||||
+ (req.upgrade_request && !downstream_->get_upgraded());
|
||||
+}
|
||||
+
|
||||
+bool HttpDownstreamConnection::should_unblock_request_body_before_response()
|
||||
+ const {
|
||||
+ const auto &req = downstream_->request();
|
||||
+
|
||||
+ return !req.upgrade_request;
|
||||
+}
|
||||
+
|
||||
+void HttpDownstreamConnection::process_blocked_request_buf_on_response() {
|
||||
+ if (blocked_request_buf_processed_) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ process_blocked_request_buf();
|
||||
+
|
||||
+ auto buf = downstream_->get_blocked_request_buf();
|
||||
+ buf->reset();
|
||||
+ blocked_request_buf_processed_ = true;
|
||||
+
|
||||
+ signal_write();
|
||||
+}
|
||||
+
|
||||
int HttpDownstreamConnection::process_blocked_request_buf() {
|
||||
auto src = downstream_->get_blocked_request_buf();
|
||||
|
||||
@@ -738,7 +766,7 @@ int HttpDownstreamConnection::process_blocked_request_buf() {
|
||||
|
||||
int HttpDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
|
||||
size_t datalen) {
|
||||
- if (!downstream_->get_request_header_sent()) {
|
||||
+ if (should_block_request_body()) {
|
||||
auto output = downstream_->get_blocked_request_buf();
|
||||
auto &req = downstream_->request();
|
||||
output->append(data, datalen);
|
||||
@@ -770,7 +798,7 @@ int HttpDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
|
||||
}
|
||||
|
||||
int HttpDownstreamConnection::end_upload_data() {
|
||||
- if (!downstream_->get_request_header_sent()) {
|
||||
+ if (should_block_request_body()) {
|
||||
downstream_->set_blocked_request_data_eof(true);
|
||||
if (request_header_written_) {
|
||||
signal_write();
|
||||
@@ -961,6 +989,11 @@ int htp_hdrs_completecb(llhttp_t *htp) {
|
||||
// upgrade succeeded, 101 response is treated as final in nghttpx.
|
||||
downstream->check_upgrade_fulfilled_http1();
|
||||
|
||||
+ if (req.method == HTTP_CONNECT && resp.http_status / 100 == 2 &&
|
||||
+ !downstream->get_upgraded()) {
|
||||
+ resp.http_status = 502;
|
||||
+ }
|
||||
+
|
||||
if (downstream->get_non_final_response()) {
|
||||
// Reset content-length because we reuse same Downstream for the
|
||||
// next response.
|
||||
@@ -987,13 +1020,22 @@ int htp_hdrs_completecb(llhttp_t *htp) {
|
||||
resp.connection_close = true;
|
||||
// transfer-encoding not applied to upgraded connection
|
||||
downstream->set_chunked_response(false);
|
||||
- } else if (http2::legacy_http1(req.http_major, req.http_minor)) {
|
||||
- if (resp.fs.content_length == -1) {
|
||||
+
|
||||
+ static_cast<HttpDownstreamConnection *>(dconn)
|
||||
+ ->process_blocked_request_buf_on_response();
|
||||
+ } else {
|
||||
+ if (req.upgrade_request) {
|
||||
resp.connection_close = true;
|
||||
}
|
||||
- downstream->set_chunked_response(false);
|
||||
- } else if (!downstream->expect_response_body()) {
|
||||
- downstream->set_chunked_response(false);
|
||||
+
|
||||
+ if (http2::legacy_http1(req.http_major, req.http_minor)) {
|
||||
+ if (resp.fs.content_length == -1) {
|
||||
+ resp.connection_close = true;
|
||||
+ }
|
||||
+ downstream->set_chunked_response(false);
|
||||
+ } else if (!downstream->expect_response_body()) {
|
||||
+ downstream->set_chunked_response(false);
|
||||
+ }
|
||||
}
|
||||
|
||||
if (loggingconf.access.write_early && downstream->accesslog_ready()) {
|
||||
@@ -1161,7 +1203,10 @@ int htp_msg_completecb(llhttp_t *htp) {
|
||||
int HttpDownstreamConnection::write_first() {
|
||||
int rv;
|
||||
|
||||
- process_blocked_request_buf();
|
||||
+ auto should_unblock_req_body = should_unblock_request_body_before_response();
|
||||
+ if (should_unblock_req_body) {
|
||||
+ process_blocked_request_buf();
|
||||
+ }
|
||||
|
||||
if (conn_.tls.ssl) {
|
||||
rv = write_tls();
|
||||
@@ -1182,8 +1227,11 @@ int HttpDownstreamConnection::write_first() {
|
||||
first_write_done_ = true;
|
||||
downstream_->set_request_header_sent(true);
|
||||
|
||||
- auto buf = downstream_->get_blocked_request_buf();
|
||||
- buf->reset();
|
||||
+ if (should_unblock_req_body) {
|
||||
+ auto buf = downstream_->get_blocked_request_buf();
|
||||
+ buf->reset();
|
||||
+ blocked_request_buf_processed_ = true;
|
||||
+ }
|
||||
|
||||
// upstream->resume_read() might be called in
|
||||
// write_tls()/write_clear(), but before blocked_request_buf_ is
|
||||
diff --git a/src/shrpx_http_downstream_connection.h b/src/shrpx_http_downstream_connection.h
|
||||
index a453f0dc..f5fcb21f 100644
|
||||
--- a/src/shrpx_http_downstream_connection.h
|
||||
+++ b/src/shrpx_http_downstream_connection.h
|
||||
@@ -91,6 +91,9 @@ public:
|
||||
int noop();
|
||||
|
||||
int process_blocked_request_buf();
|
||||
+ void process_blocked_request_buf_on_response();
|
||||
+ bool should_unblock_request_body_before_response() const;
|
||||
+ bool should_block_request_body() const;
|
||||
|
||||
private:
|
||||
Connection conn_;
|
||||
@@ -117,6 +120,8 @@ private:
|
||||
bool reusable_;
|
||||
// true if request header is written to request buffer.
|
||||
bool request_header_written_;
|
||||
+ // true if blocked request buffer has been processed.
|
||||
+ bool blocked_request_buf_processed_{};
|
||||
};
|
||||
|
||||
} // namespace shrpx
|
||||
diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc
|
||||
index 34a5504b..f69c57cd 100644
|
||||
--- a/src/shrpx_https_upstream.cc
|
||||
+++ b/src/shrpx_https_upstream.cc
|
||||
@@ -396,6 +396,17 @@ int htp_hdrs_completecb(llhttp_t *htp) {
|
||||
|
||||
downstream->inspect_http1_request();
|
||||
|
||||
+ if ((req.upgrade_request || htp->upgrade) &&
|
||||
+ (req.fs.header(http2::HD_TRANSFER_ENCODING) ||
|
||||
+ req.fs.header(http2::HD_CONTENT_LENGTH))) {
|
||||
+ if (LOG_ENABLED(INFO)) {
|
||||
+ ULOG(INFO, upstream) << "transfer-encoding and content-length are not "
|
||||
+ "allowed in CONNECT or upgrade request";
|
||||
+ }
|
||||
+
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
auto faddr = handler->get_upstream_addr();
|
||||
auto &balloc = downstream->get_block_allocator();
|
||||
auto config = get_config();
|
||||
@@ -522,6 +533,16 @@ int htp_bodycb(llhttp_t *htp, const char *data, size_t len) {
|
||||
int rv;
|
||||
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
||||
auto downstream = upstream->get_downstream();
|
||||
+ const auto &req = downstream->request();
|
||||
+
|
||||
+ if (req.upgrade_request || htp->upgrade) {
|
||||
+ if (LOG_ENABLED(INFO)) {
|
||||
+ ULOG(INFO, upstream) << "Request body for Upgrade request is not allowed";
|
||||
+ }
|
||||
+
|
||||
+ return HPE_USER;
|
||||
+ }
|
||||
+
|
||||
rv = downstream->push_upload_data_chunk(
|
||||
reinterpret_cast<const uint8_t *>(data), len);
|
||||
if (rv != 0) {
|
||||
@@ -547,7 +568,9 @@ int htp_msg_completecb(llhttp_t *htp) {
|
||||
}
|
||||
auto handler = upstream->get_client_handler();
|
||||
auto downstream = upstream->get_downstream();
|
||||
+ auto &req = downstream->request();
|
||||
downstream->set_request_state(DownstreamState::MSG_COMPLETE);
|
||||
+ req.http1_msg_complete = true;
|
||||
rv = downstream->end_upload_data();
|
||||
if (rv != 0) {
|
||||
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
|
||||
@@ -589,7 +612,8 @@ int HttpsUpstream::on_read() {
|
||||
|
||||
// downstream can be nullptr here, because it is initialized in the
|
||||
// callback chain called by llhttp_execute()
|
||||
- if (downstream && downstream->get_upgraded()) {
|
||||
+ if (downstream && downstream->request().http1_msg_complete &&
|
||||
+ downstream->get_upgraded()) {
|
||||
|
||||
auto rv = downstream->push_upload_data_chunk(rb->pos(), rb->rleft());
|
||||
|
||||
@@ -655,9 +679,13 @@ int HttpsUpstream::on_read() {
|
||||
|
||||
if (htperr != HPE_OK) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
- ULOG(INFO, this) << "HTTP parse failure: "
|
||||
- << "(" << llhttp_errno_name(htperr) << ") "
|
||||
- << llhttp_get_error_reason(&htp_);
|
||||
+ if (htperr == HPE_USER) {
|
||||
+ ULOG(INFO, this) << "HTTP callback error";
|
||||
+ } else {
|
||||
+ ULOG(INFO, this) << "HTTP parse failure: "
|
||||
+ << "(" << llhttp_errno_name(htperr) << ") "
|
||||
+ << llhttp_get_error_reason(&htp_);
|
||||
+ }
|
||||
}
|
||||
|
||||
if (downstream &&
|
||||
--
|
||||
2.55.0
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
Summary: Experimental HTTP/2 client, server and proxy
|
||||
Name: nghttp2
|
||||
Version: 1.43.0
|
||||
Release: 6%{?dist}.1
|
||||
Release: 6%{?dist}.2
|
||||
License: MIT
|
||||
URL: https://nghttp2.org/
|
||||
Source0: https://github.com/tatsuhiro-t/nghttp2/releases/download/v%{version}/nghttp2-%{version}.tar.xz
|
||||
@ -15,6 +15,9 @@ Patch2: 0002-nghttp2-1.43.0-CVE-2024-28182-CVE-2024-27316.patch
|
||||
# fix Denial of service: Assertion failure due to the missing state validation (CVE-2026-27135)
|
||||
Patch3: 0003-nghttp2-1.43.0-CVE-2026-27135.patch
|
||||
|
||||
# fix HTTP Request/Response Smuggling and Response-Queue Poisoning via ambiguous HTTP/1.1 Upgrade requests (CVE-2026-58055)
|
||||
Patch4: 0004-nghttp2-1.43.0-CVE-2026-58055.patch
|
||||
|
||||
BuildRequires: automake
|
||||
BuildRequires: libtool
|
||||
|
||||
@ -128,6 +131,9 @@ export "LD_LIBRARY_PATH=$RPM_BUILD_ROOT%{_libdir}:$LD_LIBRARY_PATH"
|
||||
|
||||
|
||||
%changelog
|
||||
* Wed Aug 12 2026 Jan Macku <jamacku@redhat.com> - 1.43.0-6.2
|
||||
- fix HTTP Request/Response Smuggling and Response-Queue Poisoning via ambiguous HTTP/1.1 Upgrade requests (CVE-2026-58055)
|
||||
|
||||
* Wed Apr 01 2026 Jan Macku <jamacku@redhat.com> - 1.43.0-6.1
|
||||
- fix Denial of service: Assertion failure due to the missing state validation (CVE-2026-27135)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user