Compare commits

...

11 Commits

Author SHA1 Message Date
Kamil Dudka d4baf69027 Resolves: CVE-2020-11080 - prevent DoS caused by overly large SETTINGS frames 2020-06-08 11:40:51 +02:00
Kamil Dudka bbbcefe6b7 sources: reflect the update to nghttp2-1.33 2020-06-08 11:32:01 +02:00
Kamil Dudka 7cb7846cee update to nghttp2-1.33
This upstream release is currently used by RHEL-8.
2020-06-08 11:08:29 +02:00
Kamil Dudka 62a83efa63 add forgotten BR for libtool
... to avoid the following build failure:

+ autoreconf -fiv
autoreconf: Entering directory `.'
autoreconf: configure.ac: not using Gettext
autoreconf: running: aclocal --force -I m4
autoreconf: configure.ac: tracing
autoreconf: running: libtoolize --copy --force
Can't exec "libtoolize": No such file or directory at /usr/share/autoconf/Autom4te/FileUtils.pm line 345, <GEN3> line 5.
autoreconf: failed to run libtoolize: No such file or directory
autoreconf: libtoolize is needed because this package uses Libtool
error: Bad exit status from /var/tmp/rpm-tmp.x1zIDi (%prep)
    Bad exit status from /var/tmp/rpm-tmp.x1zIDi (%prep)
2019-08-19 16:03:19 +02:00
Kamil Dudka 761a0da806 backport security fixes from nghttp2-1.39.2
Resolves: CVE-2019-9511
Resolves: CVE-2019-9513
2019-08-19 15:53:07 +02:00
Kamil Dudka 6d2415785a oops, forgot to update 'sources' file 2018-04-14 15:26:58 +02:00
Kamil Dudka 35d7cae6d1 Resolves: CVE-2018-1000168 - update to the latest upstream release (1.32.0) 2018-04-14 15:14:50 +02:00
Kamil Dudka 8f8a5245bf update to the latest upstream release (1.21.1) 2017-04-10 09:18:11 +02:00
Kamil Dudka 91bb700bc7 Resolves: #1438364 - update sources 2017-04-03 12:04:30 +02:00
Kamil Dudka f4f870fdf7 Resolves: #1426929 - package systemd unit file 2017-04-03 11:58:53 +02:00
Kamil Dudka 17c44c8758 Resolves: #1438364 - update to the latest upstream release 2017-04-03 11:58:53 +02:00
5 changed files with 839 additions and 41 deletions

View File

@ -0,0 +1,454 @@
From 4b7aefd8fd1612d455f2f128c09230335ed0cee6 Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Tue, 6 Aug 2019 20:48:50 +0900
Subject: [PATCH 1/3] nghttpx: Fix request stall
Fix request stall if backend connection is reused and buffer is full.
Upstream-commit: db2f612a30d54aa152ce5530fa1d683738baa4d1
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
integration-tests/nghttpx_http1_test.go | 29 +++++++++++++++++++++++++
integration-tests/server_tester.go | 4 +++-
src/shrpx_downstream.cc | 12 +++++++++-
src/shrpx_downstream.h | 4 ++++
src/shrpx_http_downstream_connection.cc | 16 +++++++++++++-
src/shrpx_https_upstream.cc | 4 +---
6 files changed, 63 insertions(+), 6 deletions(-)
diff --git a/integration-tests/nghttpx_http1_test.go b/integration-tests/nghttpx_http1_test.go
index a765333..3d41677 100644
--- a/integration-tests/nghttpx_http1_test.go
+++ b/integration-tests/nghttpx_http1_test.go
@@ -625,6 +625,35 @@ func TestH1H1HTTPSRedirectPort(t *testing.T) {
}
}
+// TestH1H1POSTRequests tests that server can handle 2 requests with
+// request body.
+func TestH1H1POSTRequests(t *testing.T) {
+ st := newServerTester(nil, t, noopHandler)
+ defer st.Close()
+
+ res, err := st.http1(requestParam{
+ name: "TestH1H1POSTRequestsNo1",
+ body: make([]byte, 1),
+ })
+ if err != nil {
+ t.Fatalf("Error st.http1() = %v", err)
+ }
+ if got, want := res.status, 200; got != want {
+ t.Errorf("res.status: %v; want %v", got, want)
+ }
+
+ res, err = st.http1(requestParam{
+ name: "TestH1H1POSTRequestsNo2",
+ body: make([]byte, 65536),
+ })
+ if err != nil {
+ t.Fatalf("Error st.http1() = %v", err)
+ }
+ if got, want := res.status, 200; got != want {
+ t.Errorf("res.status: %v; want %v", got, want)
+ }
+}
+
// // TestH1H2ConnectFailure tests that server handles the situation that
// // connection attempt to HTTP/2 backend failed.
// func TestH1H2ConnectFailure(t *testing.T) {
diff --git a/integration-tests/server_tester.go b/integration-tests/server_tester.go
index d145519..1156986 100644
--- a/integration-tests/server_tester.go
+++ b/integration-tests/server_tester.go
@@ -662,7 +662,9 @@ func cloneHeader(h http.Header) http.Header {
return h2
}
-func noopHandler(w http.ResponseWriter, r *http.Request) {}
+func noopHandler(w http.ResponseWriter, r *http.Request) {
+ ioutil.ReadAll(r.Body)
+}
type APIResponse struct {
Status string `json:"status,omitempty"`
diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc
index 360a9a9..48db65b 100644
--- a/src/shrpx_downstream.cc
+++ b/src/shrpx_downstream.cc
@@ -144,7 +144,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
request_header_sent_(false),
accesslog_written_(false),
new_affinity_cookie_(false),
- blocked_request_data_eof_(false) {
+ blocked_request_data_eof_(false),
+ expect_100_continue_(false) {
auto &timeoutconf = get_config()->http2.timeout;
@@ -807,6 +808,11 @@ void Downstream::inspect_http1_request() {
chunked_request_ = true;
}
}
+
+ auto expect = req_.fs.header(http2::HD_EXPECT);
+ expect_100_continue_ =
+ expect &&
+ util::strieq(expect->value, StringRef::from_lit("100-continue"));
}
void Downstream::inspect_http1_response() {
@@ -1103,4 +1109,8 @@ bool Downstream::get_blocked_request_data_eof() const {
return blocked_request_data_eof_;
}
+bool Downstream::get_expect_100_continue() const {
+ return expect_100_continue_;
+}
+
} // namespace shrpx
diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h
index c81fcf6..b9a851f 100644
--- a/src/shrpx_downstream.h
+++ b/src/shrpx_downstream.h
@@ -466,6 +466,8 @@ public:
EVENT_TIMEOUT = 0x2,
};
+ bool get_expect_100_continue() const;
+
enum {
DISPATCH_NONE,
DISPATCH_PENDING,
@@ -556,6 +558,8 @@ private:
// true if eof is received from client before sending header fields
// to backend.
bool blocked_request_data_eof_;
+ // true if request contains "expect: 100-continue" header field.
+ bool expect_100_continue_;
};
} // namespace shrpx
diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc
index f50c0f4..85ca947 100644
--- a/src/shrpx_http_downstream_connection.cc
+++ b/src/shrpx_http_downstream_connection.cc
@@ -698,7 +698,8 @@ int HttpDownstreamConnection::push_request_headers() {
// signal_write() when we received request body chunk, and it
// enables us to send headers and data in one writev system call.
if (connect_method || downstream_->get_blocked_request_buf()->rleft() ||
- (!req.http2_expect_body && req.fs.content_length == 0)) {
+ (!req.http2_expect_body && req.fs.content_length == 0) ||
+ downstream_->get_expect_100_continue()) {
signal_write();
}
@@ -1172,6 +1173,19 @@ int HttpDownstreamConnection::write_reuse_first() {
reuse_first_write_done_ = true;
+ // upstream->resume_read() might be called in
+ // write_tls()/write_clear(), but before blocked_request_buf_ is
+ // reset. So upstream read might still be blocked. Let's do it
+ // again here.
+ auto input = downstream_->get_request_buf();
+ if (input->rleft() == 0) {
+ auto upstream = downstream_->get_upstream();
+ auto &req = downstream_->request();
+
+ upstream->resume_read(SHRPX_NO_BUFFER, downstream_,
+ req.unconsumed_body_length);
+ }
+
return 0;
}
diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc
index 452ec90..96ca2cd 100644
--- a/src/shrpx_https_upstream.cc
+++ b/src/shrpx_https_upstream.cc
@@ -467,9 +467,7 @@ int htp_hdrs_completecb(http_parser *htp) {
// and let them decide whether responds with 100 Continue or not.
// For alternative mode, we have no backend, so just send 100
// Continue here to make the client happy.
- auto expect = req.fs.header(http2::HD_EXPECT);
- if (expect &&
- util::strieq(expect->value, StringRef::from_lit("100-continue"))) {
+ if (downstream->get_expect_100_continue()) {
auto output = downstream->get_response_buf();
constexpr auto res = StringRef::from_lit("HTTP/1.1 100 Continue\r\n\r\n");
output->append(res);
--
2.20.1
From 589a98eba0b3c7a4dbb2262c60b609cac2b1f838 Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Tue, 25 Jun 2019 22:33:35 +0900
Subject: [PATCH 2/3] Add nghttp2_option_set_max_outbound_ack
Upstream-commit: a76d0723b5f52902139ff453e0ec840673e86e75
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
doc/Makefile.am | 1 +
lib/includes/nghttp2/nghttp2.h | 11 +++++++++++
lib/nghttp2_option.c | 5 +++++
lib/nghttp2_option.h | 5 +++++
lib/nghttp2_session.c | 9 +++++++--
lib/nghttp2_session.h | 8 ++++++--
tests/nghttp2_session_test.c | 4 ++--
7 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 07cd34e..66e5ba3 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -67,6 +67,7 @@ APIDOCS= \
nghttp2_option_set_no_recv_client_magic.rst \
nghttp2_option_set_peer_max_concurrent_streams.rst \
nghttp2_option_set_user_recv_extension_type.rst \
+ nghttp2_option_set_max_outbound_ack.rst \
nghttp2_pack_settings_payload.rst \
nghttp2_priority_spec_check_default.rst \
nghttp2_priority_spec_default_init.rst \
diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h
index 14f8950..137a675 100644
--- a/lib/includes/nghttp2/nghttp2.h
+++ b/lib/includes/nghttp2/nghttp2.h
@@ -2637,6 +2637,17 @@ nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option,
NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option,
int val);
+/**
+ * @function
+ *
+ * This function sets the maximum number of outgoing SETTINGS ACK and
+ * PING ACK frames retained in :type:`nghttp2_session` object. If
+ * more than those frames are retained, the peer is considered to be
+ * misbehaving and session will be closed. The default value is 1000.
+ */
+NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option,
+ size_t val);
+
/**
* @function
*
diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c
index aec5dcf..ae22493 100644
--- a/lib/nghttp2_option.c
+++ b/lib/nghttp2_option.c
@@ -116,3 +116,8 @@ void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val) {
option->opt_set_mask |= NGHTTP2_OPT_NO_CLOSED_STREAMS;
option->no_closed_streams = val;
}
+
+void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) {
+ option->opt_set_mask |= NGHTTP2_OPT_MAX_OUTBOUND_ACK;
+ option->max_outbound_ack = val;
+}
diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h
index c743e33..86d31f7 100644
--- a/lib/nghttp2_option.h
+++ b/lib/nghttp2_option.h
@@ -66,6 +66,7 @@ typedef enum {
NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH = 1 << 8,
NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 1 << 9,
NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10,
+ NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11,
} nghttp2_option_flag;
/**
@@ -80,6 +81,10 @@ struct nghttp2_option {
* NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE
*/
size_t max_deflate_dynamic_table_size;
+ /**
+ * NGHTTP2_OPT_MAX_OUTBOUND_ACK
+ */
+ size_t max_outbound_ack;
/**
* Bitwise OR of nghttp2_option_flag to determine that which fields
* are specified.
diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c
index c58f059..8628cc7 100644
--- a/lib/nghttp2_session.c
+++ b/lib/nghttp2_session.c
@@ -457,6 +457,7 @@ static int session_new(nghttp2_session **session_ptr,
(*session_ptr)->remote_settings.max_concurrent_streams = 100;
(*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN;
+ (*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM;
if (option) {
if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
@@ -516,6 +517,10 @@ static int session_new(nghttp2_session **session_ptr,
option->no_closed_streams) {
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS;
}
+
+ if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) {
+ (*session_ptr)->max_outbound_ack = option->max_outbound_ack;
+ }
}
rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
@@ -6831,7 +6836,7 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
mem = &session->mem;
if ((flags & NGHTTP2_FLAG_ACK) &&
- session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
+ session->obq_flood_counter_ >= session->max_outbound_ack) {
return NGHTTP2_ERR_FLOODED;
}
@@ -6976,7 +6981,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
- if (session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
+ if (session->obq_flood_counter_ >= session->max_outbound_ack) {
return NGHTTP2_ERR_FLOODED;
}
}
diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h
index c7cb27d..d9e2846 100644
--- a/lib/nghttp2_session.h
+++ b/lib/nghttp2_session.h
@@ -97,7 +97,7 @@ typedef struct {
response frames are stacked up, which leads to memory exhaustion.
The value selected here is arbitrary, but safe value and if we have
these frames in this number, it is considered suspicious. */
-#define NGHTTP2_MAX_OBQ_FLOOD_ITEM 10000
+#define NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM 1000
/* The default value of maximum number of concurrent streams. */
#define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu
@@ -260,8 +260,12 @@ struct nghttp2_session {
size_t num_idle_streams;
/* The number of bytes allocated for nvbuf */
size_t nvbuflen;
- /* Counter for detecting flooding in outbound queue */
+ /* Counter for detecting flooding in outbound queue. If it exceeds
+ max_outbound_ack, session will be closed. */
size_t obq_flood_counter_;
+ /* The maximum number of outgoing SETTINGS ACK and PING ACK in
+ outbound queue. */
+ size_t max_outbound_ack;
/* The maximum length of header block to send. Calculated by the
same way as nghttp2_hd_deflate_bound() does. */
size_t max_send_header_block_length;
diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c
index 783b0ed..debec59 100644
--- a/tests/nghttp2_session_test.c
+++ b/tests/nghttp2_session_test.c
@@ -9894,7 +9894,7 @@ void test_nghttp2_session_flooding(void) {
buf = &bufs.head->buf;
- for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
+ for (i = 0; i < NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; ++i) {
CU_ASSERT(
(ssize_t)nghttp2_buf_len(buf) ==
nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
@@ -9916,7 +9916,7 @@ void test_nghttp2_session_flooding(void) {
buf = &bufs.head->buf;
- for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
+ for (i = 0; i < NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; ++i) {
CU_ASSERT(
(ssize_t)nghttp2_buf_len(buf) ==
nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
--
2.20.1
From e32b3e4c9df4abb83ca1c1c41901fadbae44699b Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Tue, 25 Jun 2019 22:38:43 +0900
Subject: [PATCH 3/3] Don't read too greedily
Upstream-commit: 83d362c6d21f76599b86e7b94cd1992288a1d43c
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/HttpServer.cc | 2 ++
src/shrpx_client_handler.cc | 9 +++++++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/HttpServer.cc b/src/HttpServer.cc
index b3e35ef..a75cee4 100644
--- a/src/HttpServer.cc
+++ b/src/HttpServer.cc
@@ -650,6 +650,7 @@ int Http2Handler::read_clear() {
}
return -1;
}
+ break;
}
return write_(*this);
@@ -775,6 +776,7 @@ int Http2Handler::read_tls() {
}
return -1;
}
+ break;
}
fin:
diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc
index 21430dd..fa1fc87 100644
--- a/src/shrpx_client_handler.cc
+++ b/src/shrpx_client_handler.cc
@@ -111,6 +111,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
int ClientHandler::noop() { return 0; }
int ClientHandler::read_clear() {
+ auto should_break = false;
rb_.ensure_chunk();
for (;;) {
if (rb_.rleft() && on_read() != 0) {
@@ -123,7 +124,7 @@ int ClientHandler::read_clear() {
return 0;
}
- if (!ev_is_active(&conn_.rev)) {
+ if (!ev_is_active(&conn_.rev) || should_break) {
return 0;
}
@@ -141,6 +142,7 @@ int ClientHandler::read_clear() {
}
rb_.write(nread);
+ should_break = true;
}
}
@@ -205,6 +207,8 @@ int ClientHandler::tls_handshake() {
}
int ClientHandler::read_tls() {
+ auto should_break = false;
+
ERR_clear_error();
rb_.ensure_chunk();
@@ -221,7 +225,7 @@ int ClientHandler::read_tls() {
return 0;
}
- if (!ev_is_active(&conn_.rev)) {
+ if (!ev_is_active(&conn_.rev) || should_break) {
return 0;
}
@@ -239,6 +243,7 @@ int ClientHandler::read_tls() {
}
rb_.write(nread);
+ should_break = true;
}
}
--
2.20.1

View File

@ -0,0 +1,340 @@
From 34670cfbc56f1c63ec046c38b9ad518010b5c84d Mon Sep 17 00:00:00 2001
From: James M Snell <jasnell@gmail.com>
Date: Fri, 17 Apr 2020 16:53:51 -0700
Subject: [PATCH 1/2] Implement max settings option
Upstream-commit: 336a98feb0d56b9ac54e12736b18785c27f75090
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
doc/CMakeLists.txt | 1 +
doc/Makefile.am | 1 +
lib/includes/nghttp2/nghttp2.h | 23 +++++++++++++
lib/nghttp2_helper.c | 2 ++
lib/nghttp2_option.c | 5 +++
lib/nghttp2_option.h | 5 +++
lib/nghttp2_session.c | 21 ++++++++++++
lib/nghttp2_session.h | 2 ++
tests/main.c | 2 ++
tests/nghttp2_session_test.c | 61 ++++++++++++++++++++++++++++++++++
tests/nghttp2_session_test.h | 1 +
11 files changed, 124 insertions(+)
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index 34c0279..f3aec84 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -42,6 +42,7 @@ set(APIDOCS
nghttp2_option_set_no_recv_client_magic.rst
nghttp2_option_set_peer_max_concurrent_streams.rst
nghttp2_option_set_user_recv_extension_type.rst
+ nghttp2_option_set_max_settings.rst
nghttp2_pack_settings_payload.rst
nghttp2_priority_spec_check_default.rst
nghttp2_priority_spec_default_init.rst
diff --git a/doc/Makefile.am b/doc/Makefile.am
index c17d933..5a58f8e 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -68,6 +68,7 @@ APIDOCS= \
nghttp2_option_set_peer_max_concurrent_streams.rst \
nghttp2_option_set_user_recv_extension_type.rst \
nghttp2_option_set_max_outbound_ack.rst \
+ nghttp2_option_set_max_settings.rst \
nghttp2_pack_settings_payload.rst \
nghttp2_priority_spec_check_default.rst \
nghttp2_priority_spec_default_init.rst \
diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h
index d79bf48..b0ff6c5 100644
--- a/lib/includes/nghttp2/nghttp2.h
+++ b/lib/includes/nghttp2/nghttp2.h
@@ -222,6 +222,13 @@ typedef struct {
*/
#define NGHTTP2_CLIENT_MAGIC_LEN 24
+/**
+ * @macro
+ *
+ * The default max number of settings per SETTINGS frame
+ */
+#define NGHTTP2_DEFAULT_MAX_SETTINGS 32
+
/**
* @enum
*
@@ -392,6 +399,11 @@ typedef enum {
* receives an other type of frame.
*/
NGHTTP2_ERR_SETTINGS_EXPECTED = -536,
+ /**
+ * When a local endpoint receives too many settings entries
+ * in a single SETTINGS frame.
+ */
+ NGHTTP2_ERR_TOO_MANY_SETTINGS = -537,
/**
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
* under unexpected condition and processing was terminated (e.g.,
@@ -2648,6 +2660,17 @@ NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option,
NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option,
size_t val);
+/**
+ * @function
+ *
+ * This function sets the maximum number of SETTINGS entries per
+ * SETTINGS frame that will be accepted. If more than those entries
+ * are received, the peer is considered to be misbehaving and session
+ * will be closed. The default value is 32.
+ */
+NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option,
+ size_t val);
+
/**
* @function
*
diff --git a/lib/nghttp2_helper.c b/lib/nghttp2_helper.c
index 3b282c7..49bbf07 100644
--- a/lib/nghttp2_helper.c
+++ b/lib/nghttp2_helper.c
@@ -334,6 +334,8 @@ const char *nghttp2_strerror(int error_code) {
case NGHTTP2_ERR_FLOODED:
return "Flooding was detected in this HTTP/2 session, and it must be "
"closed";
+ case NGHTTP2_ERR_TOO_MANY_SETTINGS:
+ return "SETTINGS frame contained more than the maximum allowed entries";
default:
return "Unknown error code";
}
diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c
index e53f22d..34348e6 100644
--- a/lib/nghttp2_option.c
+++ b/lib/nghttp2_option.c
@@ -121,3 +121,8 @@ void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) {
option->opt_set_mask |= NGHTTP2_OPT_MAX_OUTBOUND_ACK;
option->max_outbound_ack = val;
}
+
+void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val) {
+ option->opt_set_mask |= NGHTTP2_OPT_MAX_SETTINGS;
+ option->max_settings = val;
+}
diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h
index 1f740aa..939729f 100644
--- a/lib/nghttp2_option.h
+++ b/lib/nghttp2_option.h
@@ -67,6 +67,7 @@ typedef enum {
NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 1 << 9,
NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10,
NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11,
+ NGHTTP2_OPT_MAX_SETTINGS = 1 << 12,
} nghttp2_option_flag;
/**
@@ -85,6 +86,10 @@ struct nghttp2_option {
* NGHTTP2_OPT_MAX_OUTBOUND_ACK
*/
size_t max_outbound_ack;
+ /**
+ * NGHTTP2_OPT_MAX_SETTINGS
+ */
+ size_t max_settings;
/**
* Bitwise OR of nghttp2_option_flag to determine that which fields
* are specified.
diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c
index 670f83f..7638823 100644
--- a/lib/nghttp2_session.c
+++ b/lib/nghttp2_session.c
@@ -458,6 +458,7 @@ static int session_new(nghttp2_session **session_ptr,
(*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN;
(*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM;
+ (*session_ptr)->max_settings = NGHTTP2_DEFAULT_MAX_SETTINGS;
if (option) {
if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
@@ -521,6 +522,11 @@ static int session_new(nghttp2_session **session_ptr,
if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) {
(*session_ptr)->max_outbound_ack = option->max_outbound_ack;
}
+
+ if ((option->opt_set_mask & NGHTTP2_OPT_MAX_SETTINGS) &&
+ option->max_settings) {
+ (*session_ptr)->max_settings = option->max_settings;
+ }
}
rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
@@ -5658,6 +5664,16 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
iframe->max_niv =
iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
+ if (iframe->max_niv - 1 > session->max_settings) {
+ rv = nghttp2_session_terminate_session_with_reason(
+ session, NGHTTP2_ENHANCE_YOUR_CALM,
+ "SETTINGS: too many setting entries");
+ if (nghttp2_is_fatal(rv)) {
+ return rv;
+ }
+ return (ssize_t)inlen;
+ }
+
iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) *
iframe->max_niv);
@@ -7413,6 +7429,11 @@ static int nghttp2_session_upgrade_internal(nghttp2_session *session,
if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
+ /* SETTINGS frame contains too many settings */
+ if (settings_payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH
+ > session->max_settings) {
+ return NGHTTP2_ERR_TOO_MANY_SETTINGS;
+ }
rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload,
settings_payloadlen, mem);
if (rv != 0) {
diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h
index 2969364..e62a3bb 100644
--- a/lib/nghttp2_session.h
+++ b/lib/nghttp2_session.h
@@ -269,6 +269,8 @@ struct nghttp2_session {
/* The maximum length of header block to send. Calculated by the
same way as nghttp2_hd_deflate_bound() does. */
size_t max_send_header_block_length;
+ /* The maximum number of settings accepted per SETTINGS frame. */
+ size_t max_settings;
/* Next Stream ID. Made unsigned int to detect >= (1 << 31). */
uint32_t next_stream_id;
/* The last stream ID this session initiated. For client session,
diff --git a/tests/main.c b/tests/main.c
index 13865de..1f795cd 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -313,6 +313,8 @@ int main() {
test_nghttp2_session_set_local_window_size) ||
!CU_add_test(pSuite, "session_cancel_from_before_frame_send",
test_nghttp2_session_cancel_from_before_frame_send) ||
+ !CU_add_test(pSuite, "session_too_many_settings",
+ test_nghttp2_session_too_many_settings) ||
!CU_add_test(pSuite, "session_removed_closed_stream",
test_nghttp2_session_removed_closed_stream) ||
!CU_add_test(pSuite, "session_pause_data",
diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c
index 0013e92..ab76ab4 100644
--- a/tests/nghttp2_session_test.c
+++ b/tests/nghttp2_session_test.c
@@ -10450,6 +10450,67 @@ void test_nghttp2_session_cancel_from_before_frame_send(void) {
nghttp2_session_del(session);
}
+void test_nghttp2_session_too_many_settings(void) {
+ nghttp2_session *session;
+ nghttp2_option *option;
+ nghttp2_session_callbacks callbacks;
+ nghttp2_frame frame;
+ nghttp2_bufs bufs;
+ nghttp2_buf *buf;
+ ssize_t rv;
+ my_user_data ud;
+ nghttp2_settings_entry iv[3];
+ nghttp2_mem *mem;
+ nghttp2_outbound_item *item;
+
+ mem = nghttp2_mem_default();
+ frame_pack_bufs_init(&bufs);
+
+ memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
+ callbacks.on_frame_recv_callback = on_frame_recv_callback;
+ callbacks.send_callback = null_send_callback;
+
+ nghttp2_option_new(&option);
+ nghttp2_option_set_max_settings(option, 1);
+
+ nghttp2_session_client_new2(&session, &callbacks, &ud, option);
+
+ CU_ASSERT(1 == session->max_settings);
+
+ nghttp2_option_del(option);
+
+ iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
+ iv[0].value = 3000;
+
+ iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
+ iv[1].value = 16384;
+
+ nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 2),
+ 2);
+
+ rv = nghttp2_frame_pack_settings(&bufs, &frame.settings);
+
+ CU_ASSERT(0 == rv);
+ CU_ASSERT(nghttp2_bufs_len(&bufs) > 0);
+
+ nghttp2_frame_settings_free(&frame.settings, mem);
+
+ buf = &bufs.head->buf;
+ assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf));
+
+ ud.frame_recv_cb_called = 0;
+
+ rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
+ CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
+
+ item = nghttp2_session_get_next_ob_item(session);
+ CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
+
+ nghttp2_bufs_reset(&bufs);
+ nghttp2_bufs_free(&bufs);
+ nghttp2_session_del(session);
+}
+
static void
prepare_session_removed_closed_stream(nghttp2_session *session,
nghttp2_hd_deflater *deflater) {
diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h
index 35a48b8..c5095c2 100644
--- a/tests/nghttp2_session_test.h
+++ b/tests/nghttp2_session_test.h
@@ -155,6 +155,7 @@ void test_nghttp2_session_repeated_priority_change(void);
void test_nghttp2_session_repeated_priority_submission(void);
void test_nghttp2_session_set_local_window_size(void);
void test_nghttp2_session_cancel_from_before_frame_send(void);
+void test_nghttp2_session_too_many_settings(void);
void test_nghttp2_session_removed_closed_stream(void);
void test_nghttp2_session_pause_data(void);
void test_nghttp2_session_no_closed_streams(void);
--
2.21.3
From da3f4a5730ffa015a9e2d62e6e876a02f1dced20 Mon Sep 17 00:00:00 2001
From: James M Snell <jasnell@gmail.com>
Date: Sun, 19 Apr 2020 09:12:24 -0700
Subject: [PATCH 2/2] Earlier check for settings flood
Upstream-commit: f8da73bd042f810f34d19f9eae02b46d870af394
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
lib/nghttp2_session.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c
index 7638823..8271198 100644
--- a/lib/nghttp2_session.c
+++ b/lib/nghttp2_session.c
@@ -5654,6 +5654,12 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
break;
}
+ /* Check the settings flood counter early to be safe */
+ if (session->obq_flood_counter_ >= session->max_outbound_ack &&
+ !(iframe->frame.hd.flags & NGHTTP2_FLAG_ACK)) {
+ return NGHTTP2_ERR_FLOODED;
+ }
+
iframe->state = NGHTTP2_IB_READ_SETTINGS;
if (iframe->payloadleft) {
--
2.21.3

View File

@ -1,36 +0,0 @@
From 74bbd628eb904b8aa4d6258692d581edfe3865e5 Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Sat, 30 Jan 2016 18:41:27 +0900
Subject: [PATCH] Fix compile error with gcc-6 which enables C++14 by default
Upstream-commit: 4e44fccdcf1d0fea6a8cd88916040e06fc75d9db
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/template.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/template.h b/src/template.h
index 0346034..b5862d5 100644
--- a/src/template.h
+++ b/src/template.h
@@ -38,6 +38,9 @@
namespace nghttp2 {
+#if __cplusplus > 201103L
+using std::make_unique;
+#else // __cplusplus <= 201103L
template <typename T, typename... U>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(U &&... u) {
@@ -49,6 +52,7 @@ typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(size_t size) {
return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
}
+#endif // __cplusplus <= 201103L
// std::forward is constexpr since C++14
template <typename... T>
--
2.5.0

View File

@ -1,21 +1,31 @@
Summary: Experimental HTTP/2 client, server and proxy
Name: nghttp2
Version: 1.7.1
Release: 1%{?dist}
Version: 1.33.0
Release: 1.1%{?dist}
License: MIT
Group: Applications/Internet
URL: https://nghttp2.org/
Source0: https://github.com/tatsuhiro-t/nghttp2/releases/download/v%{version}/nghttp2-%{version}.tar.xz
# make the package compile with gcc-6
Patch1: nghttp2-1.7.0-gcc6.patch
# backport security fixes from nghttp2-1.39.2 (CVE-2019-9511 and CVE-2019-9513)
Patch1: nghttp2-1.31.1-CVE-2019-9511-and-CVE-2019-9513.patch
# prevent DoS caused by overly large SETTINGS frames (CVE-2020-11080)
Patch2: nghttp2-1.33.0-CVE-2020-11080.patch
BuildRequires: automake
BuildRequires: libtool
BuildRequires: CUnit-devel
BuildRequires: c-ares-devel
BuildRequires: gcc-c++
BuildRequires: libev-devel
BuildRequires: openssl-devel
BuildRequires: systemd-devel
BuildRequires: zlib-devel
Requires: libnghttp2%{?_isa} = %{version}-%{release}
%{?systemd_requires}
%description
This package contains the HTTP/2 client, server and proxy programs.
@ -44,6 +54,8 @@ for building applications with libnghttp2.
%prep
%setup -q
%patch1 -p1
%patch2 -p1
autoreconf -fiv
%build
@ -63,6 +75,8 @@ make %{?_smp_mflags} V=1
%install
%make_install
install -D -m0444 -p contrib/nghttpx.service \
"$RPM_BUILD_ROOT%{_unitdir}/nghttpx.service"
# not needed on Fedora/RHEL
rm -f "$RPM_BUILD_ROOT%{_libdir}/libnghttp2.la"
@ -74,6 +88,12 @@ rm -f "$RPM_BUILD_ROOT%{_datadir}/doc/nghttp2/README.rst"
%postun -n libnghttp2 -p /sbin/ldconfig
%post
%systemd_post nghttpx.service
%postun
%systemd_postun nghttpx.service
%check
# test the just built library instead of the system one, without using rpath
@ -91,6 +111,7 @@ make %{?_smp_mflags} check
%{_mandir}/man1/nghttp.1*
%{_mandir}/man1/nghttpd.1*
%{_mandir}/man1/nghttpx.1*
%{_unitdir}/nghttpx.service
%files -n libnghttp2
%{_libdir}/libnghttp2.so.*
@ -105,6 +126,25 @@ make %{?_smp_mflags} check
%changelog
* Mon Jun 08 2020 Kamil Dudka <kdudka@redhat.com> 1.33.0-1.1
- prevent DoS caused by overly large SETTINGS frames (CVE-2020-11080)
* Mon Jun 08 2020 Kamil Dudka <kdudka@redhat.com> 1.33.0-1
- update to nghttp2-1.33
* Mon Aug 19 2019 Kamil Dudka <kdudka@redhat.com> 1.31.1-2
- backport security fixes from nghttp2-1.39.2 (CVE-2019-9511 and CVE-2019-9513)
* Fri Apr 13 2018 Kamil Dudka <kdudka@redhat.com> 1.31.1-1
- update to the latest upstream release (fixes CVE-2018-1000168)
* Mon Apr 10 2017 Kamil Dudka <kdudka@redhat.com> 1.21.1-1
- update to the latest upstream release
* Mon Apr 03 2017 Kamil Dudka <kdudka@redhat.com> 1.21.0-1
- update to the latest upstream release (#1438364)
- package systemd unit file (#1426929)
* Thu Feb 11 2016 Kamil Dudka <kdudka@redhat.com> 1.7.1-1
- update to the latest upstream release (fixes CVE-2016-1544)

View File

@ -1 +1 @@
0c25a963e4d1023da7f3b1a325d57737 nghttp2-1.7.1.tar.xz
SHA512 (nghttp2-1.33.0.tar.xz) = eeb0bf64fea115444c685c8a01c1017ce96be18adf88ffcdecad067de7012ca61c6b2b6a627b18e2572bba7bd77ec56a3ca4b1109f7a4b21220e8e28687b5b74