From 4acacd3db737ee1d37ef498ca94cf1816fbd0b76 Mon Sep 17 00:00:00 2001 From: Packit Service Date: Dec 09 2020 16:11:43 +0000 Subject: Prepare for a new update Reverting patches so we can apply the latest update and changes can be seen in the spec file and sources. --- diff --git a/http_parser.c b/http_parser.c index cd120d8..5b5657b 100644 --- a/http_parser.c +++ b/http_parser.c @@ -25,8 +25,6 @@ #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 @@ -139,20 +137,20 @@ do { \ } while (0) /* Don't allow the total size of the HTTP headers (including the status - * line) to exceed max_header_size. This check is here to protect + * line) to exceed HTTP_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. max_header_size is still far bigger + * make the web a little safer. HTTP_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 > (max_header_size))) { \ + if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ SET_ERRNO(HPE_HEADER_OVERFLOW); \ goto error; \ } \ @@ -176,22 +174,6 @@ static const char *method_strings[] = #undef XX }; -/* Added for handling CVE-2019-15605. */ -static void reset_flags(http_parser* p) -{ - p->flags = 0; - p->transfer_encoding = 0; -} - -static void set_transfer_encoding(http_parser* p) -{ - p->transfer_encoding = 1; -} - -static int is_transfer_encoding(const http_parser* p) -{ - return p->transfer_encoding; -} /* Tokens as defined by rfc 2616. Also lowercases them. * token = 1* @@ -388,13 +370,10 @@ enum header_states , h_connection , h_content_length - , h_content_length_num - , h_content_length_ws , h_transfer_encoding , h_upgrade , h_matching_transfer_encoding_chunked - , h_matching_connection_token_start , h_matching_connection_keep_alive , h_matching_connection_close @@ -405,10 +384,6 @@ enum header_states , h_connection_keep_alive , h_connection_close , h_connection_upgrade - - /* CVE-2019-15605 */ - , h_matching_transfer_encoding_token_start - , h_matching_transfer_encoding_token }; enum http_host_state @@ -743,7 +718,7 @@ reexecute: { if (ch == CR || ch == LF) break; - reset_flags(parser); + parser->flags = 0; parser->content_length = ULLONG_MAX; if (ch == 'H') { @@ -778,7 +753,7 @@ reexecute: case s_start_res: { - reset_flags(parser); + parser->flags = 0; parser->content_length = ULLONG_MAX; switch (ch) { @@ -942,7 +917,7 @@ reexecute: { if (ch == CR || ch == LF) break; - reset_flags(parser); + parser->flags = 0; parser->content_length = ULLONG_MAX; if (UNLIKELY(!IS_ALPHA(ch))) { @@ -1334,7 +1309,6 @@ reexecute: parser->header_state = h_general; } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { parser->header_state = h_transfer_encoding; - set_transfer_encoding(parser); } break; @@ -1415,14 +1389,10 @@ reexecute: if ('c' == c) { parser->header_state = h_matching_transfer_encoding_chunked; } else { - parser->header_state = h_matching_transfer_encoding_token; + parser->header_state = h_general; } break; - /* Multi-value `Transfer-Encoding` header */ - case h_matching_transfer_encoding_token_start: - break; - case h_content_length: if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); @@ -1436,7 +1406,6 @@ reexecute: parser->flags |= F_CONTENTLENGTH; parser->content_length = ch - '0'; - parser->header_state = h_content_length_num; break; case h_connection: @@ -1499,7 +1468,7 @@ reexecute: const char* p_lf; size_t limit = data + len - p; - limit = MIN(limit, max_header_size); + limit = MIN(limit, HTTP_MAX_HEADER_SIZE); p_cr = (const char*) memchr(p, CR, limit); p_lf = (const char*) memchr(p, LF, limit); @@ -1524,18 +1493,10 @@ 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 == ' ') { - h_state = h_content_length_ws; - break; - } + if (ch == ' ') break; if (UNLIKELY(!IS_NUM(ch))) { SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); @@ -1558,48 +1519,17 @@ 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_token_start: - /* looking for 'Transfer-Encoding: chunked' */ - if ('c' == c) { - h_state = h_matching_transfer_encoding_chunked; - } else if (STRICT_TOKEN(c)) { - /* TODO(indutny): similar code below does this, but why? - * At the very least it seems to be inconsistent given that - * h_matching_transfer_encoding_token does not check for - * `STRICT_TOKEN` - */ - h_state = h_matching_transfer_encoding_token; - } else if (c == ' ' || c == '\t') { - /* Skip lws */ - } else { - h_state = h_general; - } - break; - case h_matching_transfer_encoding_chunked: parser->index++; if (parser->index > sizeof(CHUNKED)-1 || c != CHUNKED[parser->index]) { - h_state = h_matching_transfer_encoding_token; + h_state = h_general; } else if (parser->index == sizeof(CHUNKED)-2) { h_state = h_transfer_encoding_chunked; } break; - case h_matching_transfer_encoding_token: - if (ch == ',') { - h_state = h_matching_transfer_encoding_token_start; - parser->index = 0; - } - break; - case h_matching_connection_token_start: /* looking for 'Connection: keep-alive' */ if (c == 'k') { @@ -1658,7 +1588,7 @@ reexecute: break; case h_transfer_encoding_chunked: - if (ch != ' ') h_state = h_matching_transfer_encoding_token; + if (ch != ' ') h_state = h_general; break; case h_connection_keep_alive: @@ -1783,17 +1713,12 @@ reexecute: REEXECUTE(); } - /* Cannot use transfer-encoding and a content-length header together - per the HTTP specification. (RFC 7230 Section 3.3.3) */ - if ((is_transfer_encoding(parser)) && + /* Cannot use chunked encoding and a content-length header together + per the HTTP specification. */ + if ((parser->flags & F_CHUNKED) && (parser->flags & F_CONTENTLENGTH)) { - /* Allow it for lenient parsing as long as `Transfer-Encoding` is - * not `chunked` - */ - if (!lenient || (parser->flags & F_CHUNKED)) { - SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); - goto error; - } + SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); + goto error; } UPDATE_STATE(s_headers_done); @@ -1867,31 +1792,8 @@ reexecute: UPDATE_STATE(NEW_MESSAGE()); CALLBACK_NOTIFY(message_complete); } else if (parser->flags & F_CHUNKED) { - /* chunked encoding - ignore Content-Length header, - * prepare for a chunk */ + /* chunked encoding - ignore Content-Length header */ UPDATE_STATE(s_chunk_size_start); - } else if (is_transfer_encoding(parser)) { - if (parser->type == HTTP_REQUEST && !lenient) { - /* RFC 7230 3.3.3 */ - - /* If a Transfer-Encoding header field - * is present in a request and the chunked transfer coding is not - * the final encoding, the message body length cannot be determined - * reliably; the server MUST respond with the 400 (Bad Request) - * status code and then close the connection. - */ - SET_ERRNO(HPE_INVALID_TRANSFER_ENCODING); - RETURN(p - data); /* Error */ - } else { - /* RFC 7230 3.3.3 */ - - /* If a Transfer-Encoding header field is present in a response and - * the chunked transfer coding is not the final encoding, the - * message body length is determined by reading the connection until - * it is closed by the server. - */ - UPDATE_STATE(s_body_identity_eof); - } } else { if (parser->content_length == 0) { /* Content-Length header given but zero: Content-Length: 0\r\n */ @@ -2143,12 +2045,6 @@ http_message_needs_eof (const http_parser *parser) return 0; } - /* RFC 7230 3.3.3, see `s_headers_almost_done` */ - if ((is_transfer_encoding(parser)) && - (parser->flags & F_CHUNKED) == 0) { - return 1; - } - if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { return 0; } @@ -2192,7 +2088,6 @@ http_parser_init (http_parser *parser, enum http_parser_type t) parser->type = t; parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); parser->http_errno = HPE_OK; - reset_flags(parser); } void @@ -2526,8 +2421,3 @@ 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 --git a/http_parser.h b/http_parser.h index a4841be..1fbf30e 100644 --- a/http_parser.h +++ b/http_parser.h @@ -275,9 +275,8 @@ enum flags XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ XX(STRICT, "strict mode assertion failed") \ XX(PAUSED, "parser is paused") \ - XX(UNKNOWN, "an unknown error occurred") \ - XX(INVALID_TRANSFER_ENCODING, \ - "request has invalid transfer-encoding") + XX(UNKNOWN, "an unknown error occurred") + /* Define HPE_* values for each errno value above */ #define HTTP_ERRNO_GEN(n, s) HPE_##n, @@ -294,7 +293,7 @@ enum http_errno { struct http_parser { /** PRIVATE **/ unsigned int type : 2; /* enum http_parser_type */ - unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */ + unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */ unsigned int state : 7; /* enum state from http_parser.c */ unsigned int header_state : 7; /* enum header_state from http_parser.c */ unsigned int index : 7; /* index into current matcher */ @@ -319,7 +318,6 @@ struct http_parser { /** PUBLIC **/ void *data; /* A pointer to get hook to the "connection" or "socket" object */ - unsigned int transfer_encoding : 8; /* CVE-2019-15605 */ }; @@ -429,9 +427,6 @@ void http_parser_pause(http_parser *parser, int paused); /* 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/test.c b/test.c index bb83d14..bc4e664 100644 --- a/test.c +++ b/test.c @@ -27,7 +27,9 @@ #include #if defined(__APPLE__) +# undef strlcat # undef strlncpy +# undef strlcpy #endif /* defined(__APPLE__) */ #undef TRUE @@ -260,6 +262,7 @@ const struct message requests[] = ,.type= HTTP_REQUEST ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n" "Accept: */*\r\n" + "Transfer-Encoding: identity\r\n" "Content-Length: 5\r\n" "\r\n" "World" @@ -272,9 +275,10 @@ const struct message requests[] = ,.fragment= "hey" ,.request_path= "/post_identity_body_world" ,.request_url= "/post_identity_body_world?q=search#hey" - ,.num_headers= 2 + ,.num_headers= 3 ,.headers= { { "Accept", "*/*" } + , { "Transfer-Encoding", "identity" } , { "Content-Length", "5" } } ,.body= "World" @@ -1170,61 +1174,6 @@ const struct message requests[] = ,.body= "" } -#define POST_MULTI_TE_LAST_CHUNKED 43 -, {.name= "post - multi coding transfer-encoding chunked body" - ,.type= HTTP_REQUEST - ,.raw= "POST / HTTP/1.1\r\n" - "Transfer-Encoding: deflate, chunked\r\n" - "\r\n" - "1e\r\nall your base are belong to us\r\n" - "0\r\n" - "\r\n" - ,.should_keep_alive= TRUE - ,.message_complete_on_eof= FALSE - ,.http_major= 1 - ,.http_minor= 1 - ,.method= HTTP_POST - ,.query_string= "" - ,.fragment= "" - ,.request_path= "/" - ,.request_url= "/" - ,.num_headers= 1 - ,.headers= - { { "Transfer-Encoding" , "deflate, chunked" } - } - ,.body= "all your base are belong to us" - ,.num_chunks_complete= 2 - ,.chunk_lengths= { 0x1e } - } - -#define POST_MULTI_LINE_TE_LAST_CHUNKED 43 -, {.name= "post - multi coding transfer-encoding chunked body" - ,.type= HTTP_REQUEST - ,.raw= "POST / HTTP/1.1\r\n" - "Transfer-Encoding: deflate,\r\n" - " chunked\r\n" - "\r\n" - "1e\r\nall your base are belong to us\r\n" - "0\r\n" - "\r\n" - ,.should_keep_alive= TRUE - ,.message_complete_on_eof= FALSE - ,.http_major= 1 - ,.http_minor= 1 - ,.method= HTTP_POST - ,.query_string= "" - ,.fragment= "" - ,.request_path= "/" - ,.request_url= "/" - ,.num_headers= 1 - ,.headers= - { { "Transfer-Encoding" , "deflate, chunked" } - } - ,.body= "all your base are belong to us" - ,.num_chunks_complete= 2 - ,.chunk_lengths= { 0x1e } - } - , {.name= NULL } /* sentinel */ }; @@ -2004,29 +1953,6 @@ const struct message responses[] = ,.chunk_lengths= { 2, 2 } } -#define HTTP_200_MULTI_TE_NOT_LAST_CHUNKED 28 -, {.name= "HTTP 200 response with `chunked` being *not last* Transfer-Encoding" - ,.type= HTTP_RESPONSE - ,.raw= "HTTP/1.1 200 OK\r\n" - "Transfer-Encoding: chunked, identity\r\n" - "\r\n" - "2\r\n" - "OK\r\n" - "0\r\n" - "\r\n" - ,.should_keep_alive= FALSE - ,.message_complete_on_eof= TRUE - ,.http_major= 1 - ,.http_minor= 1 - ,.status_code= 200 - ,.response_status= "OK" - ,.num_headers= 1 - ,.headers= { { "Transfer-Encoding", "chunked, identity" } - } - ,.body= "2\r\nOK\r\n0\r\n\r\n" - ,.num_chunks_complete= 0 - } - , {.name= NULL } /* sentinel */ }; @@ -2068,6 +1994,12 @@ strlncat(char *dst, size_t len, const char *src, size_t n) } size_t +strlcat(char *dst, const char *src, size_t len) +{ + return strlncat(dst, len, src, (size_t) -1); +} + +size_t strlncpy(char *dst, size_t len, const char *src, size_t n) { size_t slen; @@ -2085,6 +2017,12 @@ strlncpy(char *dst, size_t len, const char *src, size_t n) return slen; } +size_t +strlcpy(char *dst, const char *src, size_t len) +{ + return strlncpy(dst, len, src, (size_t) -1); +} + int request_url_cb (http_parser *p, const char *buf, size_t len) { @@ -3705,7 +3643,7 @@ test_chunked_content_length_error (int req) parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); assert(parsed == strlen(buf)); - buf = "Transfer-Encoding: anything\r\nContent-Length: 1\r\n\r\n"; + buf = "Transfer-Encoding: chunked\r\nContent-Length: 1\r\n\r\n"; size_t buflen = strlen(buf); parsed = http_parser_execute(&parser, &settings_null, buf, buflen); @@ -4230,27 +4168,6 @@ 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 test_simple_type("HTP/1.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE); @@ -4353,12 +4270,6 @@ main (void) "fooba", HPE_OK); - // Unknown Transfer-Encoding in request - test_simple("GET / HTTP/1.1\r\n" - "Transfer-Encoding: unknown\r\n" - "\r\n", - HPE_INVALID_TRANSFER_ENCODING); - static const char *all_methods[] = { "DELETE", "GET",