summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-23 18:36:00 +0000
committerwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-23 18:36:00 +0000
commit9fe44f54a25b803b1b47ad168cf1d4cd008640d4 (patch)
treed5a712942cc267ef87712b8db6f09d72995c7197 /net
parentd181ce31f11e8e7d95d956ea0f7fe241621f36a6 (diff)
downloadchromium_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.h3
-rw-r--r--net/http/http_network_transaction_unittest.cc35
-rw-r--r--net/http/http_stream_parser.cc33
-rw-r--r--net/http/http_stream_parser.h13
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();