diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3dff807 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/http-parser-2.7.1.tar.gz diff --git a/.http-parser.metadata b/.http-parser.metadata new file mode 100644 index 0000000..c1de908 --- /dev/null +++ b/.http-parser.metadata @@ -0,0 +1 @@ +e122b1178ec5c9920186cc8293aca9eca7584b12 SOURCES/http-parser-2.7.1.tar.gz diff --git a/SOURCES/CVE-2018-12121-backport.patch b/SOURCES/CVE-2018-12121-backport.patch new file mode 100644 index 0000000..ba38f23 --- /dev/null +++ b/SOURCES/CVE-2018-12121-backport.patch @@ -0,0 +1,67 @@ +diff -up http-parser-2.7.1/http_parser.c.cve http-parser-2.7.1/http_parser.c +--- http-parser-2.7.1/http_parser.c.cve 2019-03-22 22:33:28.577238523 +0100 ++++ http-parser-2.7.1/http_parser.c 2019-03-22 22:35:12.237323250 +0100 +@@ -29,6 +29,8 @@ + #include + #include + ++static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE; ++ + #ifndef ULLONG_MAX + # define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ + #endif +@@ -141,20 +143,20 @@ do { + } while (0) + + /* Don't allow the total size of the HTTP headers (including the status +- * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect ++ * line) to exceed max_header_size. This check is here to protect + * embedders against denial-of-service attacks where the attacker feeds + * us a never-ending header that the embedder keeps buffering. + * + * This check is arguably the responsibility of embedders but we're doing + * it on the embedder's behalf because most won't bother and this way we +- * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger ++ * make the web a little safer. max_header_size is still far bigger + * than any reasonable request or response so this should never affect + * day-to-day operation. + */ + #define COUNT_HEADER_SIZE(V) \ + do { \ + parser->nread += (V); \ +- if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ ++ if (UNLIKELY(parser->nread > (max_header_size))) { \ + SET_ERRNO(HPE_HEADER_OVERFLOW); \ + goto error; \ + } \ +@@ -1538,7 +1540,7 @@ reexecute: + const char* p_lf; + size_t limit = data + len - p; + +- limit = MIN(limit, HTTP_MAX_HEADER_SIZE); ++ limit = MIN(limit, max_header_size); + + p_cr = (const char*) memchr(p, CR, limit); + p_lf = (const char*) memchr(p, LF, limit); +@@ -2468,3 +2470,8 @@ http_parser_version(void) { + HTTP_PARSER_VERSION_MINOR * 0x00100 | + HTTP_PARSER_VERSION_PATCH * 0x00001; + } ++ ++void ++http_parser_set_max_header_size(uint32_t size) { ++ max_header_size = size; ++} +diff -up http-parser-2.7.1/http_parser.h.cve http-parser-2.7.1/http_parser.h +--- http-parser-2.7.1/http_parser.h.cve 2019-03-22 22:33:37.133245016 +0100 ++++ http-parser-2.7.1/http_parser.h 2019-03-22 22:34:03.640265140 +0100 +@@ -426,6 +426,9 @@ void http_parser_pause(http_parser *pars + /* Checks if this is the final chunk of the body. */ + int http_body_is_final(const http_parser *parser); + ++/* Change the maximum header size provided at compile time. */ ++void http_parser_set_max_header_size(uint32_t size); ++ + #ifdef __cplusplus + } + #endif diff --git a/SOURCES/CVE-2018-7159-Dissallow-empty-Content-Length.patch b/SOURCES/CVE-2018-7159-Dissallow-empty-Content-Length.patch new file mode 100644 index 0000000..83e92fe --- /dev/null +++ b/SOURCES/CVE-2018-7159-Dissallow-empty-Content-Length.patch @@ -0,0 +1,46 @@ +From 350258965909f249f9c59823aac240313e0d0120 Mon Sep 17 00:00:00 2001 +From: Olga Batyshkina +Date: Wed, 19 Dec 2018 16:02:23 +0100 +Subject: [PATCH] Disallow empty Content-Length + +PR-URL: https://github.com/nodejs/http-parser/pull/459 +Reviewed-By: Ben Noordhuis +--- + http_parser.c | 5 +++++ + test.c | 7 +++++++ + 2 files changed, 12 insertions(+) + +diff --git a/http_parser.c b/http_parser.c +index cd5d0d5..228ada5 100644 +--- a/http_parser.c ++++ b/http_parser.c +@@ -1740,6 +1740,11 @@ size_t http_parser_execute (http_parser *parser, + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; ++ case h_content_length: ++ /* do not allow empty content length */ ++ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); ++ goto error; ++ break; + default: + break; + } +diff --git a/test.c b/test.c +index 25c8f5f..c3fddd5 100644 +--- a/test.c ++++ b/test.c +@@ -4182,6 +4182,13 @@ main (void) + test_invalid_header_field_token_error(HTTP_RESPONSE); + test_invalid_header_field_content_error(HTTP_RESPONSE); + ++ test_simple_type( ++ "POST / HTTP/1.1\r\n" ++ "Content-Length:\r\n" // empty ++ "\r\n", ++ HPE_INVALID_CONTENT_LENGTH, ++ HTTP_REQUEST); ++ + test_simple_type( + "POST / HTTP/1.1\r\n" + "Content-Length: 42 \r\n" // Note the surrounding whitespace. diff --git a/SOURCES/CVE-2018-7159-Fix-Content-Lenght-with-obsolete-line-folding-backport.patch b/SOURCES/CVE-2018-7159-Fix-Content-Lenght-with-obsolete-line-folding-backport.patch new file mode 100644 index 0000000..bcc2deb --- /dev/null +++ b/SOURCES/CVE-2018-7159-Fix-Content-Lenght-with-obsolete-line-folding-backport.patch @@ -0,0 +1,50 @@ +diff -up http-parser-2.7.1/http_parser.c.cve http-parser-2.7.1/http_parser.c +--- http-parser-2.7.1/http_parser.c.cve 2019-03-23 09:08:12.831806096 +0100 ++++ http-parser-2.7.1/http_parser.c 2019-03-23 09:09:45.047875248 +0100 +@@ -1483,6 +1483,11 @@ reexecute: + parser->header_state = h_content_length_num; + break; + ++ /* when obsolete line folding is encountered for content length ++ * continue to the s_header_value state */ ++ case h_content_length_ws: ++ break; ++ + case h_connection: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { +@@ -1727,6 +1732,10 @@ reexecute: + case s_header_value_lws: + { + if (ch == ' ' || ch == '\t') { ++ if (parser->header_state == h_content_length_num) { ++ /* treat obsolete line folding as space */ ++ parser->header_state = h_content_length_ws; ++ } + UPDATE_STATE(s_header_value_start); + REEXECUTE(); + } +diff -up http-parser-2.7.1/test.c.cve http-parser-2.7.1/test.c +--- http-parser-2.7.1/test.c.cve 2019-03-23 09:08:12.831806096 +0100 ++++ http-parser-2.7.1/test.c 2019-03-23 09:09:45.049875249 +0100 +@@ -3968,6 +3968,20 @@ main (void) + HPE_INVALID_CONTENT_LENGTH, + HTTP_REQUEST); + ++ test_simple_type( ++ "POST / HTTP/1.1\r\n" ++ "Content-Length: 42\r\n" ++ " Hello world!\r\n", ++ HPE_INVALID_CONTENT_LENGTH, ++ HTTP_REQUEST); ++ ++ test_simple_type( ++ "POST / HTTP/1.1\r\n" ++ "Content-Length: 42\r\n" ++ " \r\n", ++ HPE_OK, ++ HTTP_REQUEST); ++ + //// RESPONSES + + for (i = 0; i < response_count; i++) { diff --git a/SOURCES/CVE-2018-7159-reject-interior-blanks-in-Content-Length-backport.patch b/SOURCES/CVE-2018-7159-reject-interior-blanks-in-Content-Length-backport.patch new file mode 100644 index 0000000..01a0d8a --- /dev/null +++ b/SOURCES/CVE-2018-7159-reject-interior-blanks-in-Content-Length-backport.patch @@ -0,0 +1,84 @@ +diff -up http-parser-2.7.1/http_parser.c.cve http-parser-2.7.1/http_parser.c +--- http-parser-2.7.1/http_parser.c.cve 2019-03-23 08:58:04.459272497 +0100 ++++ http-parser-2.7.1/http_parser.c 2019-03-23 08:58:21.204279947 +0100 +@@ -376,6 +376,8 @@ enum header_states + + , h_connection + , h_content_length ++ , h_content_length_num ++ , h_content_length_ws + , h_transfer_encoding + , h_upgrade + +@@ -1478,6 +1480,7 @@ reexecute: + + parser->flags |= F_CONTENTLENGTH; + parser->content_length = ch - '0'; ++ parser->header_state = h_content_length_num; + break; + + case h_connection: +@@ -1565,10 +1568,18 @@ reexecute: + break; + + case h_content_length: ++ if (ch == ' ') break; ++ h_state = h_content_length_num; ++ /* FALLTHROUGH */ ++ ++ case h_content_length_num: + { + uint64_t t; + +- if (ch == ' ') break; ++ if (ch == ' ') { ++ h_state = h_content_length_ws; ++ break; ++ } + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); +@@ -1591,6 +1602,12 @@ reexecute: + break; + } + ++ case h_content_length_ws: ++ if (ch == ' ') break; ++ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); ++ parser->header_state = h_state; ++ goto error; ++ + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + parser->index++; +diff -up http-parser-2.7.1/test.c.cve http-parser-2.7.1/test.c +--- http-parser-2.7.1/test.c.cve 2019-03-23 08:57:50.851266439 +0100 ++++ http-parser-2.7.1/test.c 2019-03-23 08:58:25.545281880 +0100 +@@ -3947,6 +3947,27 @@ main (void) + test_invalid_header_field_token_error(HTTP_RESPONSE); + test_invalid_header_field_content_error(HTTP_RESPONSE); + ++ test_simple_type( ++ "POST / HTTP/1.1\r\n" ++ "Content-Length: 42 \r\n" // Note the surrounding whitespace. ++ "\r\n", ++ HPE_OK, ++ HTTP_REQUEST); ++ ++ test_simple_type( ++ "POST / HTTP/1.1\r\n" ++ "Content-Length: 4 2\r\n" ++ "\r\n", ++ HPE_INVALID_CONTENT_LENGTH, ++ HTTP_REQUEST); ++ ++ test_simple_type( ++ "POST / HTTP/1.1\r\n" ++ "Content-Length: 13 37\r\n" ++ "\r\n", ++ HPE_INVALID_CONTENT_LENGTH, ++ HTTP_REQUEST); ++ + //// RESPONSES + + for (i = 0; i < response_count; i++) { diff --git a/SOURCES/cve-tests-backport.patch b/SOURCES/cve-tests-backport.patch new file mode 100644 index 0000000..a425a96 --- /dev/null +++ b/SOURCES/cve-tests-backport.patch @@ -0,0 +1,40 @@ +diff -up http-parser-2.7.1/test.c.tests-simple-type http-parser-2.7.1/test.c +--- http-parser-2.7.1/test.c.tests-simple-type 2019-04-01 11:47:35.658031500 +0200 ++++ http-parser-2.7.1/test.c 2019-04-01 11:48:20.275113054 +0200 +@@ -3313,18 +3313,18 @@ test_message_count_body (const struct me + } + + void +-test_simple (const char *buf, enum http_errno err_expected) ++test_simple_type (const char *buf, ++ enum http_errno err_expected, ++ enum http_parser_type type) + { +- parser_init(HTTP_REQUEST); ++ parser_init(type); + + enum http_errno err; + + parse(buf, strlen(buf)); +- err = HTTP_PARSER_ERRNO(parser); ++ err = HTTP_PARSER_ERRNO(parser); + parse(NULL, 0); + +- parser_free(); +- + /* In strict mode, allow us to pass with an unexpected HPE_STRICT as + * long as the caller isn't expecting success. + */ +@@ -3340,6 +3340,12 @@ test_simple (const char *buf, enum http_ + } + + void ++test_simple (const char *buf, enum http_errno err_expected) ++{ ++ test_simple_type(buf, err_expected, HTTP_REQUEST); ++} ++ ++void + test_invalid_header_content (int req, const char* str) + { + http_parser parser; diff --git a/SOURCES/http-parser-0001-parser-HTTP_STATUS_MAP-XX-and-enum-http_status.patch b/SOURCES/http-parser-0001-parser-HTTP_STATUS_MAP-XX-and-enum-http_status.patch new file mode 100644 index 0000000..c4f691a --- /dev/null +++ b/SOURCES/http-parser-0001-parser-HTTP_STATUS_MAP-XX-and-enum-http_status.patch @@ -0,0 +1,101 @@ +From 335850f6b868d3411968cbf5a4d59fe619dee36f Mon Sep 17 00:00:00 2001 +From: Nathaniel McCallum +Date: Thu, 6 Oct 2016 02:03:36 -0400 +Subject: [PATCH] parser: HTTP_STATUS_MAP(XX) and enum http_status + +This patch provides an enum for the standardized HTTP status codes. +Additionally, the HTTP_STATUS_MAP(XX) can be used for other purposes as +well, such as code-to-name lookups and code-based switch statements. + +PR-URL: https://github.com/nodejs/http-parser/pull/337 +Reviewed-By: Fedor Indutny +Reviewed-By: Brian White +Reviewed-By: Ben Noordhuis +--- + http_parser.h | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 70 insertions(+) + +diff --git a/http_parser.h b/http_parser.h +index ea26394..45c72a0 100644 +--- a/http_parser.h ++++ b/http_parser.h +@@ -90,6 +90,76 @@ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); + typedef int (*http_cb) (http_parser*); + + ++/* Status Codes */ ++#define HTTP_STATUS_MAP(XX) \ ++ XX(100, CONTINUE, Continue) \ ++ XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \ ++ XX(102, PROCESSING, Processing) \ ++ XX(200, OK, OK) \ ++ XX(201, CREATED, Created) \ ++ XX(202, ACCEPTED, Accepted) \ ++ XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \ ++ XX(204, NO_CONTENT, No Content) \ ++ XX(205, RESET_CONTENT, Reset Content) \ ++ XX(206, PARTIAL_CONTENT, Partial Content) \ ++ XX(207, MULTI_STATUS, Multi-Status) \ ++ XX(208, ALREADY_REPORTED, Already Reported) \ ++ XX(226, IM_USED, IM Used) \ ++ XX(300, MULTIPLE_CHOICES, Multiple Choices) \ ++ XX(301, MOVED_PERMANENTLY, Moved Permanently) \ ++ XX(302, FOUND, Found) \ ++ XX(303, SEE_OTHER, See Other) \ ++ XX(304, NOT_MODIFIED, Not Modified) \ ++ XX(305, USE_PROXY, Use Proxy) \ ++ XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \ ++ XX(308, PERMANENT_REDIRECT, Permanent Redirect) \ ++ XX(400, BAD_REQUEST, Bad Request) \ ++ XX(401, UNAUTHORIZED, Unauthorized) \ ++ XX(402, PAYMENT_REQUIRED, Payment Required) \ ++ XX(403, FORBIDDEN, Forbidden) \ ++ XX(404, NOT_FOUND, Not Found) \ ++ XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \ ++ XX(406, NOT_ACCEPTABLE, Not Acceptable) \ ++ XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \ ++ XX(408, REQUEST_TIMEOUT, Request Timeout) \ ++ XX(409, CONFLICT, Conflict) \ ++ XX(410, GONE, Gone) \ ++ XX(411, LENGTH_REQUIRED, Length Required) \ ++ XX(412, PRECONDITION_FAILED, Precondition Failed) \ ++ XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \ ++ XX(414, URI_TOO_LONG, URI Too Long) \ ++ XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \ ++ XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \ ++ XX(417, EXPECTATION_FAILED, Expectation Failed) \ ++ XX(421, MISDIRECTED_REQUEST, Misdirected Request) \ ++ XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \ ++ XX(423, LOCKED, Locked) \ ++ XX(424, FAILED_DEPENDENCY, Failed Dependency) \ ++ XX(426, UPGRADE_REQUIRED, Upgrade Required) \ ++ XX(428, PRECONDITION_REQUIRED, Precondition Required) \ ++ XX(429, TOO_MANY_REQUESTS, Too Many Requests) \ ++ XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \ ++ XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \ ++ XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \ ++ XX(501, NOT_IMPLEMENTED, Not Implemented) \ ++ XX(502, BAD_GATEWAY, Bad Gateway) \ ++ XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \ ++ XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \ ++ XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \ ++ XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \ ++ XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \ ++ XX(508, LOOP_DETECTED, Loop Detected) \ ++ XX(510, NOT_EXTENDED, Not Extended) \ ++ XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \ ++ ++enum http_status ++ { ++#define XX(num, name, string) HTTP_STATUS_##name = num, ++ HTTP_STATUS_MAP(XX) ++#undef XX ++ }; ++ ++ + /* Request Methods */ + #define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ +-- +2.10.2 + diff --git a/SPECS/http-parser.spec b/SPECS/http-parser.spec new file mode 100644 index 0000000..1479589 --- /dev/null +++ b/SPECS/http-parser.spec @@ -0,0 +1,131 @@ +# we use the upstream version from http_parser.h as the SONAME +%global somajor 2 +%global sominor 7 +%global sopoint 1 + +Name: http-parser +Version: %{somajor}.%{sominor}.%{sopoint} +Release: 8%{?dist} +Summary: HTTP request/response parser for C + +License: MIT +URL: https://github.com/nodejs/http-parser +Source0: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz + +# https://github.com/nodejs/http-parser/commit/335850f6b868d3411968cbf5a4d59fe619dee36f +Patch0001: %{name}-0001-parser-HTTP_STATUS_MAP-XX-and-enum-http_status.patch +Patch0002: CVE-2018-12121-backport.patch +Patch0003: CVE-2018-7159-reject-interior-blanks-in-Content-Length-backport.patch +Patch0004: CVE-2018-7159-Fix-Content-Lenght-with-obsolete-line-folding-backport.patch +Patch0005: CVE-2018-7159-Dissallow-empty-Content-Length.patch +Patch0006: cve-tests-backport.patch + +BuildRequires: gcc +BuildRequires: cmake + +%description +This is a parser for HTTP messages written in C. It parses both requests and +responses. The parser is designed to be used in performance HTTP applications. +It does not make any syscalls nor allocations, it does not buffer data, it can +be interrupted at anytime. Depending on your architecture, it only requires +about 40 bytes of data per message stream (in a web server that is per +connection). + +%package devel +Summary: Development headers and libraries for http-parser +Requires: %{name}%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release} + +%description devel +Development headers and libraries for http-parser. + +%prep +%autosetup -p1 +# TODO: try to send upstream? +cat > CMakeLists.txt << EOF +cmake_minimum_required (VERSION 2.8.5) +project (http-parser C) +include (GNUInstallDirs) + +set (SRCS http_parser.c) +set (HDRS http_parser.h) +set (TEST_SRCS test.c) + +# Non-Strict version +add_library (http_parser \${SRCS}) +target_compile_definitions (http_parser + PUBLIC -DHTTP_PARSER_STRICT=0) +add_executable (test-nonstrict \${TEST_SRCS}) +target_link_libraries (test-nonstrict http_parser) +# Strict version +add_library (http_parser_strict \${SRCS}) +target_compile_definitions (http_parser_strict + PUBLIC -DHTTP_PARSER_STRICT=1) +add_executable (test-strict \${TEST_SRCS}) +target_link_libraries (test-strict http_parser_strict) + +set_target_properties (http_parser http_parser_strict + PROPERTIES + SOVERSION %{somajor} + VERSION %{version}) + +install (TARGETS http_parser http_parser_strict + LIBRARY DESTINATION \${CMAKE_INSTALL_LIBDIR}) +install (FILES \${HDRS} + DESTINATION \${CMAKE_INSTALL_INCLUDEDIR}) + +enable_testing () +add_test (NAME test-nonstrict COMMAND test-nonstrict) +add_test (NAME test-strict COMMAND test-strict) +EOF + +%build +mkdir %{_target_platform} +pushd %{_target_platform} + %cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo +popd +%make_build -C %{_target_platform} + +%install +%make_install -C %{_target_platform} + +%check +make test -C %{_target_platform} + +%post -p /sbin/ldconfig +%postun -p /sbin/ldconfig + +%files +%{_libdir}/libhttp_parser.so.* +%{_libdir}/libhttp_parser_strict.so.* +%doc AUTHORS README.md +%license LICENSE-MIT + +%files devel +%{_includedir}/http_parser.h +%{_libdir}/libhttp_parser.so +%{_libdir}/libhttp_parser_strict.so + +%changelog +* Mon Apr 1 2019 Jakub Hrozek - 2.7.1-8 +- Backport needed test fixes +- Related: rhbz#1666024 - CVE-2018-7159 http-parser: nodejs: HTTP parser + allowed for spaces inside Content-Length header + values [rhel-7] + +* Sat Mar 23 2019 Jakub Hrozek - 2.7.1-7 +- Resolves: rhbz#1666024 - CVE-2018-7159 http-parser: nodejs: HTTP parser + allowed for spaces inside Content-Length header + values [rhel-7] + +* Fri Mar 22 2019 Jakub Hrozek - 2.7.1-6 +- Resolves: rhbz#1666381 - CVE-2018-12121 http-parser: nodejs: Denial of + Service with large HTTP headers [rhel-7] + +* Thu Aug 10 2017 Fabiano FidĂȘncio - 2.7.1-5 +- Bump http-parser release number to avoid people pulling EPEL package instead + of RHEL package + Resolves: rhbz#1479471 + +* Wed Feb 01 2017 Fabiano FidĂȘncio - 2.7.1-1 +- Import spec file and patches from latest fc25 package + Resolves: rhbz#1393819