diff --git a/SOURCES/nghttp2-1.7.1-CVE-2020-11080.patch b/SOURCES/nghttp2-1.7.1-CVE-2020-11080.patch new file mode 100644 index 0000000..bdcbb61 --- /dev/null +++ b/SOURCES/nghttp2-1.7.1-CVE-2020-11080.patch @@ -0,0 +1,189 @@ +diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h +index 59fc867..3b14027 100644 +--- a/lib/includes/nghttp2/nghttp2.h ++++ b/lib/includes/nghttp2/nghttp2.h +@@ -221,6 +221,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 + * +@@ -382,6 +389,11 @@ typedef enum { + * Unexpected internal error, but recovered. + */ + NGHTTP2_ERR_INTERNAL = -534, ++ /** ++ * 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., +@@ -2106,6 +2118,17 @@ NGHTTP2_EXTERN void + nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, + uint32_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 884abc6..b4d9e55 100644 +--- a/lib/nghttp2_helper.c ++++ b/lib/nghttp2_helper.c +@@ -297,6 +297,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 04dbbc6..e8ee7b4 100644 +--- a/lib/nghttp2_option.c ++++ b/lib/nghttp2_option.c +@@ -62,3 +62,8 @@ void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, + option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS; + option->max_reserved_remote_streams = 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 ebf416a..1ae1a4f 100644 +--- a/lib/nghttp2_option.h ++++ b/lib/nghttp2_option.h +@@ -59,7 +59,8 @@ typedef enum { + NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1, + NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2, + NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3, +- NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4 ++ NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4, ++ NGHTTP2_OPT_MAX_SETTINGS = 1 << 12, + } nghttp2_option_flag; + + /** +@@ -91,6 +92,11 @@ struct nghttp2_option { + * NGHTTP2_OPT_NO_HTTP_MESSAGING + */ + int no_http_messaging; ++ ++ /** ++ * NGHTTP2_OPT_MAX_SETTINGS ++ */ ++ size_t max_settings; + }; + + #endif /* NGHTTP2_OPTION_H */ +diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c +index e42e46b..a3a84b4 100644 +--- a/lib/nghttp2_session.c ++++ b/lib/nghttp2_session.c +@@ -375,6 +375,8 @@ static int session_new(nghttp2_session **session_ptr, + /* Limit max outgoing concurrent streams to sensible value */ + (*session_ptr)->remote_settings.max_concurrent_streams = 100; + ++ (*session_ptr)->max_settings = NGHTTP2_DEFAULT_MAX_SETTINGS; ++ + if (option) { + if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) && + option->no_auto_window_update) { +@@ -405,6 +407,11 @@ static int session_new(nghttp2_session **session_ptr, + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; + } ++ ++ if ((option->opt_set_mask & NGHTTP2_OPT_MAX_SETTINGS) && ++ option->max_settings) { ++ (*session_ptr)->max_settings = option->max_settings; ++ } + } + + (*session_ptr)->callbacks = *callbacks; +@@ -4837,6 +4844,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + ssize_t padlen; + int rv; + int busy = 0; ++ int max_niv; + nghttp2_frame_hd cont_hd; + nghttp2_stream *stream; + size_t pri_fieldlen; +@@ -5123,9 +5131,30 @@ 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_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM && ++ !(iframe->frame.hd.flags & NGHTTP2_FLAG_ACK)) { ++ return NGHTTP2_ERR_FLOODED; ++ } ++ + iframe->state = NGHTTP2_IB_READ_SETTINGS; + + if (iframe->payloadleft) { ++ /* We allocate iv with additional one entry, to store the ++ minimum header table size. */ ++ max_niv = ++ iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1; ++ ++ if (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; ++ } ++ + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); + break; + } +@@ -6531,6 +6560,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 204aea9..87aac84 100644 +--- a/lib/nghttp2_session.h ++++ b/lib/nghttp2_session.h +@@ -244,6 +244,8 @@ struct nghttp2_session { + size_t nvbuflen; + /* Counter for detecting flooding in outbound queue */ + size_t obq_flood_counter_; ++ /* 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/SPECS/nghttp2.spec b/SPECS/nghttp2.spec index 78b2cf9..fd053ad 100644 --- a/SPECS/nghttp2.spec +++ b/SPECS/nghttp2.spec @@ -4,13 +4,14 @@ Summary: Meta-package that only requires libnghttp2 Name: %{?scl_prefix}nghttp2 Version: 1.7.1 -Release: 8%{?dist} +Release: 8%{?dist}.1 License: MIT Group: Applications/Internet URL: https://nghttp2.org/ Source0: https://github.com/tatsuhiro-t/nghttp2/releases/download/v%{version}/nghttp2-%{version}.tar.xz Patch0: nghttp2-1.7.0-httpd24.patch Patch1: nghttp2-1.7.1-CVE-2019-9511-and-CVE-2019-9513.patch +Patch2: nghttp2-1.7.1-CVE-2020-11080.patch BuildRequires: CUnit-devel BuildRequires: openssl-devel @@ -49,6 +50,7 @@ for building applications with libnghttp2. %patch0 -p1 -b .httpd24 %patch1 -p1 -b .CVE-2019-9511-and-CVE-2019-9513 +%patch2 -p1 -b .CVE-2020-11080 %build %{?scl:scl enable %{scl} - << \EOF} @@ -115,6 +117,10 @@ make %{?_smp_mflags} check %changelog +* Thu Jun 25 2020 Lubos Uhliarik - 1.7.1-8.1 +- Resolves: #1845302 - CVE-2020-11080 httpd24-nghttp2: nghttp2: overly large + SETTINGS frames can lead to DoS + * Tue Aug 27 2019 Lubos Uhliarik - 1.7.1-8 - Resolves: #1745692 - CVE-2019-9513 httpd24-nghttp2: HTTP/2: flood using PRIORITY frames resulting in excessive resource consumption