diff options
author | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-23 18:36:00 +0000 |
---|---|---|
committer | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-23 18:36:00 +0000 |
commit | 9fe44f54a25b803b1b47ad168cf1d4cd008640d4 (patch) | |
tree | d5a712942cc267ef87712b8db6f09d72995c7197 /net | |
parent | d181ce31f11e8e7d95d956ea0f7fe241621f36a6 (diff) | |
download | chromium_src-9fe44f54a25b803b1b47ad168cf1d4cd008640d4.zip chromium_src-9fe44f54a25b803b1b47ad168cf1d4cd008640d4.tar.gz chromium_src-9fe44f54a25b803b1b47ad168cf1d4cd008640d4.tar.bz2 |
Support net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH.
If we encounter multiple Content-Length headers without a
Transfer-Encoding header, then it's a potential response smuggling
attempt. Return an error.
BUG=56344
TEST=HttpNetworkTransactionTest.MultipleContentLengthHeadersNoTransferEncoding,HttpNetworkTransactionTest.MultipleContentLengthHeadersTransferEncoding
Review URL: http://codereview.chromium.org/3394016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60317 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/net_error_list.h | 3 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 35 | ||||
-rw-r--r-- | net/http/http_stream_parser.cc | 33 | ||||
-rw-r--r-- | net/http/http_stream_parser.h | 13 |
4 files changed, 74 insertions, 10 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index abf18d1..0370874 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h @@ -382,6 +382,9 @@ NET_ERROR(UNDOCUMENTED_SECURITY_LIBRARY_STATUS, -344) // The HTTP response was too big to drain. NET_ERROR(RESPONSE_BODY_TOO_BIG_TO_DRAIN, -345) +// The HTTP response was too big to drain. +NET_ERROR(RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH, -346) + // The cache does not have the requested entry. NET_ERROR(CACHE_MISS, -400) diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index a8d9ea5..196c6ea 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -490,6 +490,41 @@ TEST_F(HttpNetworkTransactionTest, ChunkedEncoding) { EXPECT_EQ("Hello world", out.response_data); } +// Next tests deal with http://crbug.com/56344. + +TEST_F(HttpNetworkTransactionTest, + MultipleContentLengthHeadersNoTransferEncoding) { + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Content-Length: 10\r\n"), + MockRead("Content-Length: 5\r\n\r\n"), + }; + SimpleGetHelperResult out = SimpleGetHelper(data_reads, + arraysize(data_reads)); + EXPECT_EQ(ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH, out.rv); +} + +TEST_F(HttpNetworkTransactionTest, + MultipleContentLengthHeadersTransferEncoding) { + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Content-Length: 666\r\n"), + MockRead("Content-Length: 1337\r\n"), + MockRead("Transfer-Encoding: chunked\r\n\r\n"), + MockRead("5\r\nHello\r\n"), + MockRead("1\r\n"), + MockRead(" \r\n"), + MockRead("5\r\nworld\r\n"), + MockRead("0\r\n\r\nHTTP/1.1 200 OK\r\n"), + MockRead(false, OK), + }; + SimpleGetHelperResult out = SimpleGetHelper(data_reads, + arraysize(data_reads)); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("Hello world", out.response_data); +} + // Do a request using the HEAD method. Verify that we don't try to read the // message body (since HEAD has none). TEST_F(HttpNetworkTransactionTest, Head) { diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc index b114519..ce115b3 100644 --- a/net/http/http_stream_parser.cc +++ b/net/http/http_stream_parser.cc @@ -303,16 +303,24 @@ int HttpStreamParser::DoReadHeadersComplete(int result) { io_state_ = STATE_BODY_PENDING; end_offset = 0; } - DoParseResponseHeaders(end_offset); + int rv = DoParseResponseHeaders(end_offset); + if (rv < 0) + return rv; return result; } } read_buf_->set_offset(read_buf_->offset() + result); DCHECK_LE(read_buf_->offset(), read_buf_->capacity()); - DCHECK(result >= 0); + DCHECK_GE(result, 0); int end_of_header_offset = ParseResponseHeaders(); + + // Note: -1 is special, it indicates we haven't found the end of headers. + // Anything less than -1 is a net::Error, so we bail out. + if (end_of_header_offset < -1) + return end_of_header_offset; + if (end_of_header_offset == -1) { io_state_ = STATE_READ_HEADERS; // Prevent growing the headers buffer indefinitely. @@ -481,11 +489,13 @@ int HttpStreamParser::ParseResponseHeaders() { if (end_offset == -1) return -1; - DoParseResponseHeaders(end_offset); + int rv = DoParseResponseHeaders(end_offset); + if (rv < 0) + return rv; return end_offset + read_buf_unused_offset_; } -void HttpStreamParser::DoParseResponseHeaders(int end_offset) { +int HttpStreamParser::DoParseResponseHeaders(int end_offset) { scoped_refptr<HttpResponseHeaders> headers; if (response_header_start_offset_ >= 0) { headers = new HttpResponseHeaders(HttpUtil::AssembleRawHeaders( @@ -495,8 +505,23 @@ void HttpStreamParser::DoParseResponseHeaders(int end_offset) { headers = new HttpResponseHeaders(std::string("HTTP/0.9 200 OK")); } + // Check for multiple Content-Length headers with a Transfer-Encoding header. + // If they exist, it's a potential response smuggling attack. + + void* it = NULL; + const std::string content_length_header("Content-Length"); + std::string ignored_header_value; + if (!headers->HasHeader("Transfer-Encoding") && + headers->EnumerateHeader( + &it, content_length_header, &ignored_header_value) && + headers->EnumerateHeader( + &it, content_length_header, &ignored_header_value)) { + return ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH; + } + response_->headers = headers; response_->vary_data.Init(*request_, *response_->headers); + return OK; } void HttpStreamParser::CalculateResponseBodySize() { diff --git a/net/http/http_stream_parser.h b/net/http/http_stream_parser.h index ec84d056..a1dda07 100644 --- a/net/http/http_stream_parser.h +++ b/net/http/http_stream_parser.h @@ -111,14 +111,15 @@ class HttpStreamParser { int DoReadBody(); int DoReadBodyComplete(int result); - // Examines |read_buf_| to find the start and end of the headers. Return - // the offset for the end of the headers, or -1 if the complete headers - // were not found. If they are are found, parse them with - // DoParseResponseHeaders(). + // Examines |read_buf_| to find the start and end of the headers. If they are + // found, parse them with DoParseResponseHeaders(). Return the offset for + // the end of the headers, or -1 if the complete headers were not found, or + // with a net::Error if we encountered an error during parsing. int ParseResponseHeaders(); - // Parse the headers into response_. - void DoParseResponseHeaders(int end_of_header_offset); + // Parse the headers into response_. Returns OK on success or a net::Error on + // failure. + int DoParseResponseHeaders(int end_of_header_offset); // Examine the parsed headers to try to determine the response body size. void CalculateResponseBodySize(); |