summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorhoro <horo@chromium.org>2015-07-13 19:05:20 -0700
committerCommit bot <commit-bot@chromium.org>2015-07-14 02:05:52 +0000
commit7ab9c6c314f589251c85913bd0a360cba24eb76a (patch)
tree7792430acf57140883e77e0f436fc8917e156d24 /media
parent826aae36bc7da8a39f9b5eebefe9e51c59a63cfd (diff)
downloadchromium_src-7ab9c6c314f589251c85913bd0a360cba24eb76a.zip
chromium_src-7ab9c6c314f589251c85913bd0a360cba24eb76a.tar.gz
chromium_src-7ab9c6c314f589251c85913bd0a360cba24eb76a.tar.bz2
Check the response URL origin in BufferedDataSource to avoid mixing cross-origin responses.
In current implementation malicious attackers can scan the bytes of cross-origin resources by mixing their generated bytes and the target response. See http://crbug.com/489060#c32 for details. To avoid this, we have to deny mixing cross-origin responses in the middle of playback. This CL introduces the check logic of the response URL origin of the partial responses. When BufferedDataSource receives the first HTTP responses, it remembers the original URL of it. And when BufferedDataSource receives the succeeding response, it checks the origin of the new response. If the origin is not same as the origin of the first response, the response is treated as an error. BUG=505829 TEST=media_blink_unittests with https://codereview.chromium.org/1221973002/, LayoutTests in https://codereview.chromium.org/1226473002/ Review URL: https://codereview.chromium.org/1220963004 Cr-Commit-Position: refs/heads/master@{#338620}
Diffstat (limited to 'media')
-rw-r--r--media/blink/buffered_data_source.cc17
-rw-r--r--media/blink/buffered_data_source.h10
-rw-r--r--media/blink/buffered_data_source_unittest.cc134
-rw-r--r--media/blink/buffered_resource_loader.cc3
-rw-r--r--media/blink/buffered_resource_loader.h7
-rw-r--r--media/blink/test_response_generator.cc16
-rw-r--r--media/blink/test_response_generator.h12
7 files changed, 195 insertions, 4 deletions
diff --git a/media/blink/buffered_data_source.cc b/media/blink/buffered_data_source.cc
index d5f32b1..614fa39 100644
--- a/media/blink/buffered_data_source.cc
+++ b/media/blink/buffered_data_source.cc
@@ -356,6 +356,7 @@ void BufferedDataSource::StartCallback(
loader_->Stop();
return;
}
+ response_original_url_ = loader_->response_original_url();
// All responses must be successful. Resources that are assumed to be fully
// buffered must have a known content length.
@@ -403,8 +404,8 @@ void BufferedDataSource::PartialReadStartCallback(
BufferedResourceLoader::Status status) {
DCHECK(render_task_runner_->BelongsToCurrentThread());
DCHECK(loader_.get());
-
- if (status == BufferedResourceLoader::kOk) {
+ if (status == BufferedResourceLoader::kOk &&
+ CheckPartialResponseURL(loader_->response_original_url())) {
// Once the request has started successfully, we can proceed with
// reading from it.
ReadInternal();
@@ -422,6 +423,18 @@ void BufferedDataSource::PartialReadStartCallback(
ReadOperation::Run(read_op_.Pass(), kReadError);
}
+bool BufferedDataSource::CheckPartialResponseURL(
+ const GURL& partial_response_original_url) const {
+ // We check the redirected URL of partial responses in case malicious
+ // attackers scan the bytes of other origin resources by mixing their
+ // generated bytes and the target response. See http://crbug.com/489060#c32
+ // for details.
+ // If the origin of the new response is different from the first response we
+ // deny the redirected response.
+ return response_original_url_.GetOrigin() ==
+ partial_response_original_url.GetOrigin();
+}
+
void BufferedDataSource::ReadCallback(
BufferedResourceLoader::Status status,
int bytes_read) {
diff --git a/media/blink/buffered_data_source.h b/media/blink/buffered_data_source.h
index 32f1481..fd85b7c 100644
--- a/media/blink/buffered_data_source.h
+++ b/media/blink/buffered_data_source.h
@@ -159,6 +159,9 @@ class MEDIA_EXPORT BufferedDataSource : public DataSource {
// when accessing ranges that are outside initial buffered region).
void PartialReadStartCallback(BufferedResourceLoader::Status status);
+ // Returns true if we can accept the new partial response.
+ bool CheckPartialResponseURL(const GURL& partial_response_original_url) const;
+
// BufferedResourceLoader callbacks.
void ReadCallback(BufferedResourceLoader::Status status, int bytes_read);
void LoadingStateChangedCallback(BufferedResourceLoader::LoadingState state);
@@ -238,6 +241,13 @@ class MEDIA_EXPORT BufferedDataSource : public DataSource {
DownloadingCB downloading_cb_;
+ // The original URL of the first response. If the request is redirected to
+ // another URL it is the URL after redirected. If the response is generated in
+ // a Service Worker this URL is empty. BufferedDataSource checks the original
+ // URL of each successive response. If the origin URL of it is different from
+ // the original URL of the first response, it is treated as an error.
+ GURL response_original_url_;
+
// Disallow rebinding WeakReference ownership to a different thread by keeping
// a persistent reference. This avoids problems with the thread-safety of
// reaching into this class from multiple threads to attain a WeakPtr.
diff --git a/media/blink/buffered_data_source_unittest.cc b/media/blink/buffered_data_source_unittest.cc
index b2716ac..878d5f7 100644
--- a/media/blink/buffered_data_source_unittest.cc
+++ b/media/blink/buffered_data_source_unittest.cc
@@ -108,6 +108,8 @@ static const int kDataSize = 1024;
static const char kHttpUrl[] = "http://localhost/foo.webm";
static const char kFileUrl[] = "file:///tmp/bar.webm";
+static const char kHttpDifferentPathUrl[] = "http://localhost/bar.webm";
+static const char kHttpDifferentOriginUrl[] = "http://127.0.0.1/foo.webm";
class BufferedDataSourceTest : public testing::Test {
public:
@@ -220,6 +222,46 @@ class BufferedDataSourceTest : public testing::Test {
message_loop_.RunUntilIdle();
}
+ void ExecuteMixedResponseSuccessTest(const WebURLResponse& response1,
+ const WebURLResponse& response2) {
+ EXPECT_CALL(host_, SetTotalBytes(kFileSize));
+ EXPECT_CALL(host_, AddBufferedByteRange(kDataSize, kDataSize * 2 - 1));
+ EXPECT_CALL(host_, AddBufferedByteRange(0, kDataSize - 1));
+ EXPECT_CALL(*this, ReadCallback(kDataSize)).Times(2);
+
+ Respond(response1);
+ ReadAt(0);
+ ReceiveData(kDataSize);
+ EXPECT_TRUE(data_source_->loading());
+
+ ExpectCreateResourceLoader();
+ FinishLoading();
+ ReadAt(kDataSize);
+ Respond(response2);
+ ReceiveData(kDataSize);
+ FinishLoading();
+ Stop();
+ }
+
+ void ExecuteMixedResponseFailureTest(const WebURLResponse& response1,
+ const WebURLResponse& response2) {
+ EXPECT_CALL(host_, SetTotalBytes(kFileSize));
+ EXPECT_CALL(host_, AddBufferedByteRange(0, kDataSize - 1));
+ EXPECT_CALL(*this, ReadCallback(kDataSize));
+ EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
+
+ Respond(response1);
+ ReadAt(0);
+ ReceiveData(kDataSize);
+ EXPECT_TRUE(data_source_->loading());
+
+ ExpectCreateResourceLoader();
+ FinishLoading();
+ ReadAt(kDataSize);
+ Respond(response2);
+ Stop();
+ }
+
// Accessors for private variables on |data_source_|.
BufferedResourceLoader* loader() {
return data_source_->loader_.get();
@@ -443,6 +485,98 @@ TEST_F(BufferedDataSourceTest, Http_RetryOnError) {
Stop();
}
+TEST_F(BufferedDataSourceTest, Http_PartialResponse) {
+ Initialize(kHttpUrl, true);
+ WebURLResponse response1 =
+ response_generator_->GeneratePartial206(0, kDataSize - 1);
+ WebURLResponse response2 =
+ response_generator_->GeneratePartial206(kDataSize, kDataSize * 2 - 1);
+ // The origin URL of response1 and response2 are same. So no error should
+ // occur.
+ ExecuteMixedResponseSuccessTest(response1, response2);
+}
+
+TEST_F(BufferedDataSourceTest,
+ Http_MixedResponse_RedirectedToDifferentPathResponse) {
+ Initialize(kHttpUrl, true);
+ WebURLResponse response1 =
+ response_generator_->GeneratePartial206(0, kDataSize - 1);
+ WebURLResponse response2 =
+ response_generator_->GeneratePartial206(kDataSize, kDataSize * 2 - 1);
+ response2.setURL(GURL(kHttpDifferentPathUrl));
+ // The origin URL of response1 and response2 are same. So no error should
+ // occur.
+ ExecuteMixedResponseSuccessTest(response1, response2);
+}
+
+TEST_F(BufferedDataSourceTest,
+ Http_MixedResponse_RedirectedToDifferentOriginResponse) {
+ Initialize(kHttpUrl, true);
+ WebURLResponse response1 =
+ response_generator_->GeneratePartial206(0, kDataSize - 1);
+ WebURLResponse response2 =
+ response_generator_->GeneratePartial206(kDataSize, kDataSize * 2 - 1);
+ response2.setURL(GURL(kHttpDifferentOriginUrl));
+ // The origin URL of response1 and response2 are different. So an error should
+ // occur.
+ ExecuteMixedResponseFailureTest(response1, response2);
+}
+
+TEST_F(BufferedDataSourceTest,
+ Http_MixedResponse_ServiceWorkerGeneratedResponseAndNormalResponse) {
+ Initialize(kHttpUrl, true);
+ WebURLResponse response1 =
+ response_generator_->GeneratePartial206(0, kDataSize - 1);
+ response1.setWasFetchedViaServiceWorker(true);
+ WebURLResponse response2 =
+ response_generator_->GeneratePartial206(kDataSize, kDataSize * 2 - 1);
+ // response1 is generated in a Service Worker but response2 is from a native
+ // server. So an error should occur.
+ ExecuteMixedResponseFailureTest(response1, response2);
+}
+
+TEST_F(BufferedDataSourceTest,
+ Http_MixedResponse_ServiceWorkerProxiedAndSameURLResponse) {
+ Initialize(kHttpUrl, true);
+ WebURLResponse response1 =
+ response_generator_->GeneratePartial206(0, kDataSize - 1);
+ response1.setWasFetchedViaServiceWorker(true);
+ response1.setOriginalURLViaServiceWorker(GURL(kHttpUrl));
+ WebURLResponse response2 =
+ response_generator_->GeneratePartial206(kDataSize, kDataSize * 2 - 1);
+ // The origin URL of response1 and response2 are same. So no error should
+ // occur.
+ ExecuteMixedResponseSuccessTest(response1, response2);
+}
+
+TEST_F(BufferedDataSourceTest,
+ Http_MixedResponse_ServiceWorkerProxiedAndDifferentPathResponse) {
+ Initialize(kHttpUrl, true);
+ WebURLResponse response1 =
+ response_generator_->GeneratePartial206(0, kDataSize - 1);
+ response1.setWasFetchedViaServiceWorker(true);
+ response1.setOriginalURLViaServiceWorker(GURL(kHttpDifferentPathUrl));
+ WebURLResponse response2 =
+ response_generator_->GeneratePartial206(kDataSize, kDataSize * 2 - 1);
+ // The origin URL of response1 and response2 are same. So no error should
+ // occur.
+ ExecuteMixedResponseSuccessTest(response1, response2);
+}
+
+TEST_F(BufferedDataSourceTest,
+ Http_MixedResponse_ServiceWorkerProxiedAndDifferentOriginResponse) {
+ Initialize(kHttpUrl, true);
+ WebURLResponse response1 =
+ response_generator_->GeneratePartial206(0, kDataSize - 1);
+ response1.setWasFetchedViaServiceWorker(true);
+ response1.setOriginalURLViaServiceWorker(GURL(kHttpDifferentOriginUrl));
+ WebURLResponse response2 =
+ response_generator_->GeneratePartial206(kDataSize, kDataSize * 2 - 1);
+ // The origin URL of response1 and response2 are different. So an error should
+ // occur.
+ ExecuteMixedResponseFailureTest(response1, response2);
+}
+
TEST_F(BufferedDataSourceTest, File_Retry) {
InitializeWithFileResponse();
diff --git a/media/blink/buffered_resource_loader.cc b/media/blink/buffered_resource_loader.cc
index 81b76d0..8f276de 100644
--- a/media/blink/buffered_resource_loader.cc
+++ b/media/blink/buffered_resource_loader.cc
@@ -363,6 +363,9 @@ void BufferedResourceLoader::didReceiveResponse(
"Unknown")
<< " " << response.httpStatusCode();
DCHECK(active_loader_.get());
+ response_original_url_ = response.wasFetchedViaServiceWorker()
+ ? response.originalURLViaServiceWorker()
+ : response.url();
// The loader may have been stopped and |start_cb| is destroyed.
// In this case we shouldn't do anything.
diff --git a/media/blink/buffered_resource_loader.h b/media/blink/buffered_resource_loader.h
index cb02720..6e466a3 100644
--- a/media/blink/buffered_resource_loader.h
+++ b/media/blink/buffered_resource_loader.h
@@ -205,6 +205,11 @@ class MEDIA_EXPORT BufferedResourceLoader
// playback rate).
void CancelUponDeferral();
+ // Returns the original URL of the response. If the request is redirected to
+ // another URL it is the URL after redirected. If the response is generated in
+ // a Service Worker it is empty.
+ const GURL response_original_url() const { return response_original_url_; }
+
private:
friend class BufferedDataSourceTest;
friend class BufferedResourceLoaderTest;
@@ -316,6 +321,8 @@ class MEDIA_EXPORT BufferedResourceLoader
// Playback rate of the media.
double playback_rate_;
+ GURL response_original_url_;
+
scoped_refptr<MediaLog> media_log_;
bool cancel_upon_deferral_;
diff --git a/media/blink/test_response_generator.cc b/media/blink/test_response_generator.cc
index de7b5d5..e6883db 100644
--- a/media/blink/test_response_generator.cc
+++ b/media/blink/test_response_generator.cc
@@ -41,13 +41,25 @@ WebURLResponse TestResponseGenerator::Generate200() {
}
WebURLResponse TestResponseGenerator::Generate206(int64 first_byte_offset) {
- return Generate206(first_byte_offset, kNormal);
+ return GeneratePartial206(first_byte_offset, content_length_ - 1, kNormal);
}
WebURLResponse TestResponseGenerator::Generate206(int64 first_byte_offset,
Flags flags) {
+ return GeneratePartial206(first_byte_offset, content_length_ - 1, flags);
+}
+
+WebURLResponse TestResponseGenerator::GeneratePartial206(
+ int64 first_byte_offset,
+ int64 last_byte_offset) {
+ return GeneratePartial206(first_byte_offset, last_byte_offset, kNormal);
+}
+
+WebURLResponse TestResponseGenerator::GeneratePartial206(
+ int64 first_byte_offset,
+ int64 last_byte_offset,
+ Flags flags) {
int64 range_content_length = content_length_ - first_byte_offset;
- int64 last_byte_offset = content_length_ - 1;
WebURLResponse response(gurl_);
response.setHTTPStatusCode(206);
diff --git a/media/blink/test_response_generator.h b/media/blink/test_response_generator.h
index 7e3e98d..e5a1161 100644
--- a/media/blink/test_response_generator.h
+++ b/media/blink/test_response_generator.h
@@ -42,6 +42,18 @@ class TestResponseGenerator {
// headers via |flags|.
blink::WebURLResponse Generate206(int64 first_byte_offset, Flags flags);
+ // Generates a regular HTTP 206 response starting from |first_byte_offset|
+ // until |last_byte_offset|.
+ blink::WebURLResponse GeneratePartial206(int64 first_byte_offset,
+ int64 last_byte_offset);
+
+ // Generates a custom HTTP 206 response starting from |first_byte_offset|
+ // until |last_byte_offset|. You can tweak what gets included in the
+ // headers via |flags|.
+ blink::WebURLResponse GeneratePartial206(int64 first_byte_offset,
+ int64 last_byte_offset,
+ Flags flags);
+
// Generates a regular HTTP 404 response.
blink::WebURLResponse Generate404();