1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

Fix HTTP parser vulnerability to prevent request smuggling

This patch addresses an HTTP request smuggling vulnerability by:
1. Adding allow_chunked_length parameter
2. Properly handling multiple Transfer-Encoding headers
3. Implementing RFC 7230 Section 3.3.3 checks for Transfer-Encoding and Content-Length conflicts

Based on fix: fc70ce08f5
This commit is contained in:
yannaingtun 2025-02-28 00:12:14 +08:00
parent 93cba246bc
commit 133795193a

View file

@ -2200,6 +2200,7 @@ size_t http_parser_execute (http_parser *parser,
const char *status_mark = 0; const char *status_mark = 0;
enum state p_state = (enum state) parser->state; enum state p_state = (enum state) parser->state;
const unsigned int lenient = parser->lenient_http_headers; const unsigned int lenient = parser->lenient_http_headers;
const unsigned int allow_chunked_length = parser->allow_chunked_length;
uint32_t nread = parser->nread; uint32_t nread = parser->nread;
/* We're in an error state. Don't bother doing anything. */ /* We're in an error state. Don't bother doing anything. */
@ -2319,8 +2320,9 @@ reexecute:
{ {
if (ch == CR || ch == LF) if (ch == CR || ch == LF)
break; break;
parser->flags = 0; parser->flags = 0;
parser->content_length = ULLONG_MAX; parser->uses_transfer_encoding = 0;
parser->content_length = ULLONG_MAX;
if (ch == 'H') { if (ch == 'H') {
UPDATE_STATE(s_res_H); UPDATE_STATE(s_res_H);
@ -2496,8 +2498,9 @@ reexecute:
{ {
if (ch == CR || ch == LF) if (ch == CR || ch == LF)
break; break;
parser->flags = 0; parser->flags = 0;
parser->content_length = ULLONG_MAX; parser->uses_transfer_encoding = 0;
parser->content_length = ULLONG_MAX;
if (UNLIKELY(!IS_ALPHA(ch))) { if (UNLIKELY(!IS_ALPHA(ch))) {
SET_ERRNO(HPE_INVALID_METHOD); SET_ERRNO(HPE_INVALID_METHOD);
@ -2941,6 +2944,14 @@ reexecute:
parser->header_state = h_general; parser->header_state = h_general;
} else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
parser->header_state = h_transfer_encoding; parser->header_state = h_transfer_encoding;
parser->uses_transfer_encoding = 1;
/* Multiple `Transfer-Encoding` headers should be treated as
* one, but with values separate by a comma.
*
* See: https://tools.ietf.org/html/rfc7230#section-3.2.2
*/
parser->flags &= ~F_CHUNKED;
} }
break; break;
@ -3371,13 +3382,23 @@ reexecute:
REEXECUTE(); REEXECUTE();
} }
/* Cannot use chunked encoding and a content-length header together /* Cannot use transfer-encoding and a content-length header together
per the HTTP specification. */ per the HTTP specification. (RFC 7230 Section 3.3.3) */
if ((parser->flags & F_CHUNKED) && if ((parser->uses_transfer_encoding == 1) &&
(parser->flags & F_CONTENTLENGTH)) { (parser->flags & F_CONTENTLENGTH)) {
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); /* Allow it for lenient parsing as long as `Transfer-Encoding` is
goto error; * not `chunked` or allow_length_with_encoding is set
} */
if (parser->flags & F_CHUNKED) {
if (!allow_chunked_length) {
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
goto error;
}
} else if (!lenient) {
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
goto error;
}
}
UPDATE_STATE(s_headers_done); UPDATE_STATE(s_headers_done);
@ -3451,8 +3472,31 @@ reexecute:
UPDATE_STATE(NEW_MESSAGE()); UPDATE_STATE(NEW_MESSAGE());
CALLBACK_NOTIFY(message_complete); CALLBACK_NOTIFY(message_complete);
} else if (parser->flags & F_CHUNKED) { } else if (parser->flags & F_CHUNKED) {
/* chunked encoding - ignore Content-Length header */ /* chunked encoding - ignore Content-Length header,
* prepare for a chunk */
UPDATE_STATE(s_chunk_size_start); UPDATE_STATE(s_chunk_size_start);
} else if (parser->uses_transfer_encoding == 1) {
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 { } else {
if (parser->content_length == 0) { if (parser->content_length == 0) {
/* Content-Length header given but zero: Content-Length: 0\r\n */ /* Content-Length header given but zero: Content-Length: 0\r\n */