summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-08 18:05:57 +0000
committermbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-08 18:05:57 +0000
commita7a265efd24072f9dc7b5f737ec84d5ae0553cd6 (patch)
tree62180201efd7568a6db59bc3b8c06caa260625d7
parentc1784804a5634728d147baea37257a06ab5cc031 (diff)
downloadchromium_src-a7a265efd24072f9dc7b5f737ec84d5ae0553cd6.zip
chromium_src-a7a265efd24072f9dc7b5f737ec84d5ae0553cd6.tar.gz
chromium_src-a7a265efd24072f9dc7b5f737ec84d5ae0553cd6.tar.bz2
Add origin checking for server pushed resources.
BUG=64108 TEST=PushedStream, ServerPushCrossOriginCorrectness Review URL: http://codereview.chromium.org/5516012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@68605 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--net/spdy/spdy_network_transaction_unittest.cc119
-rw-r--r--net/spdy/spdy_session.cc28
-rw-r--r--net/spdy/spdy_stream.cc37
-rw-r--r--net/spdy/spdy_stream.h8
-rw-r--r--net/spdy/spdy_stream_unittest.cc54
-rw-r--r--net/spdy/spdy_test_util.cc8
6 files changed, 232 insertions, 22 deletions
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index 730d53f..fc0cbe8 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -398,7 +398,7 @@ class SpdyNetworkTransactionTest
void RunServerPushTest(OrderedSocketData* data,
HttpResponseInfo* response,
- HttpResponseInfo* response2,
+ HttpResponseInfo* push_response,
std::string& expected) {
NormalSpdyTransactionHelper helper(CreateGetRequest(),
BoundNetLog(), GetParam());
@@ -443,7 +443,7 @@ class SpdyNetworkTransactionTest
// Verify the SYN_REPLY.
// Copy the response info, because trans goes away.
*response = *trans->GetResponseInfo();
- *response2 = *trans2->GetResponseInfo();
+ *push_response = *trans2->GetResponseInfo();
VerifyStreamsClosed(helper);
}
@@ -2958,7 +2958,6 @@ TEST_P(SpdyNetworkTransactionTest, SynReplyHeaders) {
"cookie: val2\n"
"hello: bye\n"
"status: 200\n"
- "url: /index.php\n"
"version: HTTP/1.1\n"
},
// This is the minimalist set of headers.
@@ -2966,7 +2965,6 @@ TEST_P(SpdyNetworkTransactionTest, SynReplyHeaders) {
{ NULL },
"hello: bye\n"
"status: 200\n"
- "url: /index.php\n"
"version: HTTP/1.1\n"
},
// Headers with a comma separated list.
@@ -2977,7 +2975,6 @@ TEST_P(SpdyNetworkTransactionTest, SynReplyHeaders) {
"cookie: val1,val2\n"
"hello: bye\n"
"status: 200\n"
- "url: /index.php\n"
"version: HTTP/1.1\n"
}
};
@@ -5303,4 +5300,116 @@ TEST_P(SpdyNetworkTransactionTest, SynReplyWithDuplicateLateHeaders) {
EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, out.rv);
}
+TEST_P(SpdyNetworkTransactionTest, ServerPushCrossOriginCorrectness) {
+ // In this test we want to verify that we can't accidentally push content
+ // which can't be pushed by this content server.
+ // This test assumes that:
+ // - if we're requesting http://www.foo.com/barbaz
+ // - the browser has made a connection to "www.foo.com".
+
+ // A list of the URL to fetch, followed by the URL being pushed.
+ static const char* const kTestCases[] = {
+ "http://www.google.com/foo.html",
+ "http://www.google.com:81/foo.js", // Bad port
+
+ "http://www.google.com/foo.html",
+ "https://www.google.com/foo.js", // Bad protocol
+
+ "http://www.google.com/foo.html",
+ "ftp://www.google.com/foo.js", // Invalid Protocol
+
+ "http://www.google.com/foo.html",
+ "http://blat.www.google.com/foo.js", // Cross subdomain
+
+ "http://www.google.com/foo.html",
+ "http://www.foo.com/foo.js", // Cross domain
+ };
+
+
+ static const unsigned char kPushBodyFrame[] = {
+ 0x00, 0x00, 0x00, 0x02, // header, ID
+ 0x01, 0x00, 0x00, 0x06, // FIN, length
+ 'p', 'u', 's', 'h', 'e', 'd' // "pushed"
+ };
+
+ for (int index = 0; index < arraysize(kTestCases); index += 2) {
+ const char* url_to_fetch = kTestCases[index];
+ const char* url_to_push = kTestCases[index + 1];
+
+ scoped_ptr<spdy::SpdyFrame>
+ stream1_syn(ConstructSpdyGet(url_to_fetch, false, 1, LOWEST));
+ scoped_ptr<spdy::SpdyFrame>
+ stream1_body(ConstructSpdyBodyFrame(1, true));
+ scoped_ptr<spdy::SpdyFrame> push_rst(
+ ConstructSpdyRstStream(2, spdy::REFUSED_STREAM));
+ MockWrite writes[] = {
+ CreateMockWrite(*stream1_syn, 1),
+ CreateMockWrite(*push_rst, 4),
+ };
+
+ scoped_ptr<spdy::SpdyFrame>
+ stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<spdy::SpdyFrame>
+ stream2_syn(ConstructSpdyPush(NULL,
+ 0,
+ 2,
+ 1,
+ url_to_push));
+ scoped_ptr<spdy::SpdyFrame> rst(
+ ConstructSpdyRstStream(2, spdy::CANCEL));
+
+ MockRead reads[] = {
+ CreateMockRead(*stream1_reply, 2),
+ CreateMockRead(*stream2_syn, 3),
+ CreateMockRead(*stream1_body, 5, false),
+ MockRead(true, reinterpret_cast<const char*>(kPushBodyFrame),
+ arraysize(kPushBodyFrame), 6),
+ MockRead(true, ERR_IO_PENDING, 7), // Force a pause
+ };
+
+ HttpResponseInfo response;
+ scoped_refptr<OrderedSocketData> data(new OrderedSocketData(
+ reads,
+ arraysize(reads),
+ writes,
+ arraysize(writes)));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL(url_to_fetch);
+ request.load_flags = 0;
+ NormalSpdyTransactionHelper helper(request,
+ BoundNetLog(), GetParam());
+ helper.RunPreTestSetup();
+ helper.AddData(data);
+
+ HttpNetworkTransaction* trans = helper.trans();
+
+ // Start the transaction with basic parameters.
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, &callback, BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback.WaitForResult();
+
+ // Read the response body.
+ std::string result;
+ ReadResult(trans, data, &result);
+
+ // Verify that we consumed all test data.
+ EXPECT_TRUE(data->at_read_eof());
+ EXPECT_TRUE(data->at_write_eof());
+
+ // Verify the SYN_REPLY.
+ // Copy the response info, because trans goes away.
+ response = *trans->GetResponseInfo();
+
+ VerifyStreamsClosed(helper);
+
+ // Verify the SYN_REPLY.
+ EXPECT_TRUE(response.headers != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response.headers->GetStatusLine());
+ }
+}
+
} // namespace net
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index a788d13..bcdafe4 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -1026,19 +1026,19 @@ void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame& frame,
// Server-initiated streams should have even sequence numbers.
if ((stream_id & 0x1) != 0) {
- LOG(ERROR) << "Received invalid OnSyn stream id " << stream_id;
+ LOG(WARNING) << "Received invalid OnSyn stream id " << stream_id;
return;
}
if (IsStreamActive(stream_id)) {
- LOG(ERROR) << "Received OnSyn for active stream " << stream_id;
+ LOG(WARNING) << "Received OnSyn for active stream " << stream_id;
return;
}
if (associated_stream_id == 0) {
- LOG(ERROR) << "Received invalid OnSyn associated stream id "
- << associated_stream_id
- << " for stream " << stream_id;
+ LOG(WARNING) << "Received invalid OnSyn associated stream id "
+ << associated_stream_id
+ << " for stream " << stream_id;
ResetStream(stream_id, spdy::INVALID_STREAM);
return;
}
@@ -1047,10 +1047,9 @@ void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame& frame,
// TODO(mbelshe): DCHECK that this is a GET method?
+ // Verify that the response had a URL for us.
const std::string& url = ContainsKey(*headers, "url") ?
headers->find("url")->second : "";
-
- // Verify that the response had a URL for us.
if (url.empty()) {
ResetStream(stream_id, spdy::PROTOCOL_ERROR);
LOG(WARNING) << "Pushed stream did not contain a url.";
@@ -1064,19 +1063,28 @@ void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame& frame,
return;
}
+ // Verify we have a valid stream association.
if (!IsStreamActive(associated_stream_id)) {
- LOG(ERROR) << "Received OnSyn with inactive associated stream "
+ LOG(WARNING) << "Received OnSyn with inactive associated stream "
<< associated_stream_id;
ResetStream(stream_id, spdy::INVALID_ASSOCIATED_STREAM);
return;
}
- // TODO(erikchen): Actually do something with the associated id.
+ scoped_refptr<SpdyStream> associated_stream =
+ active_streams_[associated_stream_id];
+ GURL associated_url(associated_stream->GetUrl());
+ if (associated_url.GetOrigin() != gurl.GetOrigin()) {
+ LOG(WARNING) << "Rejected Cross Origin Push Stream "
+ << associated_stream_id;
+ ResetStream(stream_id, spdy::REFUSED_STREAM);
+ return;
+ }
// There should not be an existing pushed stream with the same path.
PushedStreamMap::iterator it = unclaimed_pushed_streams_.find(url);
if (it != unclaimed_pushed_streams_.end()) {
- LOG(ERROR) << "Received duplicate pushed stream with url: " << url;
+ LOG(WARNING) << "Received duplicate pushed stream with url: " << url;
ResetStream(stream_id, spdy::PROTOCOL_ERROR);
return;
}
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index 44d1243..8a5d4a4 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -397,6 +397,43 @@ bool SpdyStream::GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) {
return session_->GetSSLCertRequestInfo(cert_request_info);
}
+bool SpdyStream::HasUrl() const {
+ if (pushed_)
+ return response_received();
+ return request_.get() != NULL;
+}
+
+GURL SpdyStream::GetUrl() const {
+ DCHECK(HasUrl());
+
+ if (pushed_) {
+ // assemble from the response
+ std::string url;
+ spdy::SpdyHeaderBlock::const_iterator it;
+ it = response_->find("url");
+ if (it != (*response_).end())
+ url = it->second;
+ return GURL(url);
+ }
+
+ // assemble from the request
+ std::string scheme;
+ std::string host_port;
+ std::string path;
+ spdy::SpdyHeaderBlock::const_iterator it;
+ it = request_->find("scheme");
+ if (it != (*request_).end())
+ scheme = it->second;
+ it = request_->find("host");
+ if (it != (*request_).end())
+ host_port = it->second;
+ it = request_->find("path");
+ if (it != (*request_).end())
+ path = it->second;
+ std::string url = scheme + "://" + host_port + path;
+ return GURL(url);
+}
+
int SpdyStream::DoLoop(int result) {
do {
State state = io_state_;
diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h
index d3dd242..9a055c2 100644
--- a/net/spdy/spdy_stream.h
+++ b/net/spdy/spdy_stream.h
@@ -13,6 +13,7 @@
#include "base/linked_ptr.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
+#include "googleurl/src/gurl.h"
#include "net/base/bandwidth_metrics.h"
#include "net/base/io_buffer.h"
#include "net/base/net_log.h"
@@ -214,6 +215,13 @@ class SpdyStream : public base::RefCounted<SpdyStream> {
int response_status() const { return response_status_; }
+ // Returns true if the URL for this stream is known.
+ bool HasUrl() const;
+
+ // Get the URL associated with this stream. Only valid when has_url() is
+ // true.
+ GURL GetUrl() const;
+
private:
enum State {
STATE_NONE,
diff --git a/net/spdy/spdy_stream_unittest.cc b/net/spdy/spdy_stream_unittest.cc
index cdb116f..3bf0a23 100644
--- a/net/spdy/spdy_stream_unittest.cc
+++ b/net/spdy/spdy_stream_unittest.cc
@@ -150,8 +150,12 @@ TEST_F(SpdyStreamTest, SendDataAfterOpen) {
static const char* const kGetHeaders[] = {
"method",
"GET",
- "url",
- "http://www.google.com/",
+ "scheme",
+ "http",
+ "host",
+ "www.google.com",
+ "path",
+ "/",
"version",
"HTTP/1.1",
};
@@ -189,7 +193,8 @@ TEST_F(SpdyStreamTest, SendDataAfterOpen) {
SpdySession::SetSSLMode(false);
scoped_refptr<SpdySession> session(CreateSpdySession());
- GURL url("http://www.google.com/");
+ const char* kStreamUrl = "http://www.google.com/";
+ GURL url(kStreamUrl);
HostPortPair host_port_pair("www.google.com", 80);
scoped_refptr<TCPSocketParams> tcp_params(
@@ -213,11 +218,17 @@ TEST_F(SpdyStreamTest, SendDataAfterOpen) {
new TestSpdyStreamDelegate(stream.get(), buf.get(), &callback));
stream->SetDelegate(delegate.get());
+ EXPECT_FALSE(stream->HasUrl());
+
linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock);
(*headers)["method"] = "GET";
- (*headers)["url"] = "http://www.google.com/";
+ (*headers)["scheme"] = url.scheme();
+ (*headers)["host"] = url.host();
+ (*headers)["path"] = url.path();
(*headers)["version"] = "HTTP/1.1";
stream->set_spdy_headers(headers);
+ EXPECT_TRUE(stream->HasUrl());
+ EXPECT_EQ(kStreamUrl, stream->GetUrl().spec());
EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(true));
@@ -231,4 +242,39 @@ TEST_F(SpdyStreamTest, SendDataAfterOpen) {
EXPECT_TRUE(delegate->closed());
}
+TEST_F(SpdyStreamTest, PushedStream) {
+ const char kStreamUrl[] = "http://www.google.com/";
+
+ SpdySessionDependencies session_deps;
+ session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps);
+ SpdySessionPoolPeer pool_peer_(session_->spdy_session_pool());
+ scoped_refptr<SpdySession> spdy_session(CreateSpdySession());
+ BoundNetLog net_log;
+
+ // Conjure up a stream.
+ scoped_refptr<SpdyStream> stream = new SpdyStream(spdy_session,
+ 2,
+ true,
+ net_log);
+ EXPECT_FALSE(stream->response_received());
+ EXPECT_FALSE(stream->HasUrl());
+
+ // Set a couple of headers.
+ spdy::SpdyHeaderBlock response;
+ response["url"] = kStreamUrl;
+ stream->OnResponseReceived(response);
+
+ // Send some basic headers.
+ spdy::SpdyHeaderBlock headers;
+ response["status"] = "200";
+ response["version"] = "OK";
+ stream->OnHeaders(headers);
+
+ stream->set_response_received();
+ EXPECT_TRUE(stream->response_received());
+ EXPECT_TRUE(stream->HasUrl());
+ EXPECT_EQ(kStreamUrl, stream->GetUrl().spec());
+}
+
+
} // namespace net
diff --git a/net/spdy/spdy_test_util.cc b/net/spdy/spdy_test_util.cc
index 5d6c9b0..3a7f771 100644
--- a/net/spdy/spdy_test_util.cc
+++ b/net/spdy/spdy_test_util.cc
@@ -334,7 +334,11 @@ spdy::SpdyFrame* ConstructSpdyGet(const char* const url,
// This is so ugly. Why are we using char* in here again?
std::string str_path = gurl.PathForRequest();
std::string str_scheme = gurl.scheme();
- std::string str_host = gurl.host(); // TODO(mbelshe): should have a port.
+ std::string str_host = gurl.host();
+ if (gurl.has_port()) {
+ str_host += ":";
+ str_host += gurl.port();
+ }
scoped_array<char> req(new char[str_path.size() + 1]);
scoped_array<char> scheme(new char[str_scheme.size() + 1]);
scoped_array<char> host(new char[str_host.size() + 1]);
@@ -618,8 +622,6 @@ spdy::SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[],
"bye",
"status",
"200",
- "url",
- "/index.php",
"version",
"HTTP/1.1"
};