summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-04-19 13:30:44 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-04-20 08:08:53 +0000
commite9ec9ada1c390d2f0bf25e993d072c4ecf36c83a (patch)
tree57a15d6a99019d00653e99d635d2b870d21c1266
parent4ec3a984093ea1a897607e0e5e13c4970f5bf150 (diff)
[Backport] Enable <video> to see if a response from a service worker was cross-origin.
Previously, if the <video> src was same-origin as the page, and the service worker intercepted the request and responded with an opaque response (by making a cross-origin request without CORS sharing), the <video> would be considered same-origin data. The solution is to teach the data backing <video> about FetchResponseType, which tells us if a response is opaque or non-opaque. There is some complexity with multiple responses due to range requests/ partial content responses. It's possible that some responses are opaque and some are non-opaque. Once the opaque bit is set, it shouldn't be cleared. This fixes tests added in: https://chromium-review.googlesource.com/c/chromium/src/+/892683 and https://chromium-review.googlesource.com/c/chromium/src/+/897165 Bug: 780435 Reviewed-on: https://chromium-review.googlesource.com/828564 (CVE-2018-6093) Change-Id: I6a2aba935155dfb897fb55ba0c1d09fb251076bc Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
-rw-r--r--chromium/content/renderer/media/webmediaplayer_ms.cc5
-rw-r--r--chromium/content/renderer/media/webmediaplayer_ms.h1
-rw-r--r--chromium/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc1
-rw-r--r--chromium/media/blink/DEPS1
-rw-r--r--chromium/media/blink/multibuffer_data_source.cc10
-rw-r--r--chromium/media/blink/multibuffer_data_source.h4
-rw-r--r--chromium/media/blink/resource_multibuffer_data_provider.cc24
-rw-r--r--chromium/media/blink/url_index.cc10
-rw-r--r--chromium/media/blink/url_index.h10
-rw-r--r--chromium/media/blink/webmediaplayer_impl.cc6
-rw-r--r--chromium/media/blink/webmediaplayer_impl.h1
-rw-r--r--chromium/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp35
-rw-r--r--chromium/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp5
-rw-r--r--chromium/third_party/WebKit/Source/platform/testing/EmptyWebMediaPlayer.h1
-rw-r--r--chromium/third_party/WebKit/public/platform/WebMediaPlayer.h1
-rw-r--r--chromium/third_party/WebKit/public/platform/WebURLResponse.h5
16 files changed, 106 insertions, 14 deletions
diff --git a/chromium/content/renderer/media/webmediaplayer_ms.cc b/chromium/content/renderer/media/webmediaplayer_ms.cc
index b528aa53ec0..4a6b55c414a 100644
--- a/chromium/content/renderer/media/webmediaplayer_ms.cc
+++ b/chromium/content/renderer/media/webmediaplayer_ms.cc
@@ -642,6 +642,11 @@ void WebMediaPlayerMS::Paint(blink::WebCanvas* canvas,
context_3d);
}
+bool WebMediaPlayerMS::DidGetOpaqueResponseFromServiceWorker() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return false;
+}
+
bool WebMediaPlayerMS::HasSingleSecurityOrigin() const {
DCHECK(thread_checker_.CalledOnValidThread());
return true;
diff --git a/chromium/content/renderer/media/webmediaplayer_ms.h b/chromium/content/renderer/media/webmediaplayer_ms.h
index ef572374903..025c4bc5abe 100644
--- a/chromium/content/renderer/media/webmediaplayer_ms.h
+++ b/chromium/content/renderer/media/webmediaplayer_ms.h
@@ -138,6 +138,7 @@ class CONTENT_EXPORT WebMediaPlayerMS
blink::WebString GetErrorMessage() const override;
bool DidLoadingProgress() override;
+ bool DidGetOpaqueResponseFromServiceWorker() const override;
bool HasSingleSecurityOrigin() const override;
bool DidPassCORSAccessCheck() const override;
diff --git a/chromium/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc b/chromium/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
index 4e28d7cacf3..2f32d3c6c6a 100644
--- a/chromium/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
+++ b/chromium/content/renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc
@@ -63,6 +63,7 @@ class MockWebMediaPlayer : public blink::WebMediaPlayer,
}
bool DidLoadingProgress() override { return true; }
+ bool DidGetOpaqueResponseFromServiceWorker() const override { return false; }
bool HasSingleSecurityOrigin() const override { return true; }
bool DidPassCORSAccessCheck() const override { return true; }
double MediaTimeForTimeValue(double timeValue) const override { return 0.0; }
diff --git a/chromium/media/blink/DEPS b/chromium/media/blink/DEPS
index 090921e2556..35607b195a7 100644
--- a/chromium/media/blink/DEPS
+++ b/chromium/media/blink/DEPS
@@ -13,6 +13,7 @@ include_rules = [
"+mojo/public/cpp/bindings",
"+net/base",
"+net/http",
+ "+services/network/public/interfaces/fetch_api.mojom.h",
"+services/service_manager/public/cpp",
"+third_party/WebKit/public/platform",
"+third_party/WebKit/public/web",
diff --git a/chromium/media/blink/multibuffer_data_source.cc b/chromium/media/blink/multibuffer_data_source.cc
index 45f81d88f7c..20d479d4b2f 100644
--- a/chromium/media/blink/multibuffer_data_source.cc
+++ b/chromium/media/blink/multibuffer_data_source.cc
@@ -278,7 +278,8 @@ bool MultibufferDataSource::HasSingleOrigin() {
bool MultibufferDataSource::DidPassCORSAccessCheck() const {
if (url_data_->cors_mode() == UrlData::CORS_UNSPECIFIED)
return false;
- // If init_cb is set, we initialization is not finished yet.
+
+ // If init_cb is set, we know initialization is not finished yet.
if (!init_cb_.is_null())
return false;
if (failed_)
@@ -286,6 +287,13 @@ bool MultibufferDataSource::DidPassCORSAccessCheck() const {
return true;
}
+bool MultibufferDataSource::DidGetOpaqueResponseViaServiceWorker() const {
+ return url_data_->has_opaque_data();
+
+ // TODO(falken): Do we need to do something about |init_cb_| like
+ // in DidPassCORSAccessCheck()?
+}
+
void MultibufferDataSource::MediaPlaybackRateChanged(double playback_rate) {
DCHECK(render_task_runner_->BelongsToCurrentThread());
diff --git a/chromium/media/blink/multibuffer_data_source.h b/chromium/media/blink/multibuffer_data_source.h
index 43695ab08ec..43d1140c141 100644
--- a/chromium/media/blink/multibuffer_data_source.h
+++ b/chromium/media/blink/multibuffer_data_source.h
@@ -82,6 +82,10 @@ class MEDIA_BLINK_EXPORT MultibufferDataSource : public DataSource {
// Returns true if the media resource passed a CORS access control check.
bool DidPassCORSAccessCheck() const;
+ // Returns true if a service worker provided the media resource response,
+ // and the response was opaque.
+ bool DidGetOpaqueResponseViaServiceWorker() const;
+
// Notifies changes in playback state for controlling media buffering
// behavior.
void MediaPlaybackRateChanged(double playback_rate);
diff --git a/chromium/media/blink/resource_multibuffer_data_provider.cc b/chromium/media/blink/resource_multibuffer_data_provider.cc
index de6d590757d..db9c7fe763c 100644
--- a/chromium/media/blink/resource_multibuffer_data_provider.cc
+++ b/chromium/media/blink/resource_multibuffer_data_provider.cc
@@ -35,6 +35,25 @@ using blink::WebURLResponse;
namespace media {
+namespace {
+
+bool IsOpaqueData(network::mojom::FetchResponseType response_type) {
+ switch (response_type) {
+ case network::mojom::FetchResponseType::kBasic:
+ case network::mojom::FetchResponseType::kCORS:
+ case network::mojom::FetchResponseType::kDefault:
+ return false;
+ case network::mojom::FetchResponseType::kError:
+ case network::mojom::FetchResponseType::kOpaque:
+ case network::mojom::FetchResponseType::kOpaqueRedirect:
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace
+
// The number of milliseconds to wait before retrying a failed load.
const int kLoaderFailedRetryDelayMs = 250;
@@ -323,6 +342,11 @@ void ResourceMultiBufferDataProvider::DidReceiveResponse(
url_data_->url_index()->TryInsert(destination_url_data);
}
+ // This is vital for security! A service worker can respond with a response
+ // from a different origin, so this response type is needed to detect that.
+ destination_url_data->set_has_opaque_data(
+ IsOpaqueData(response.ResponseTypeViaServiceWorker()));
+
if (destination_url_data != url_data_) {
// At this point, we've encountered a redirect, or found a better url data
// instance for the data that we're about to download.
diff --git a/chromium/media/blink/url_index.cc b/chromium/media/blink/url_index.cc
index 99291258eb3..84c5aafa10b 100644
--- a/chromium/media/blink/url_index.cc
+++ b/chromium/media/blink/url_index.cc
@@ -51,6 +51,7 @@ UrlData::UrlData(const GURL& url, CORSMode cors_mode, UrlIndex* url_index)
length_(kPositionNotSpecified),
range_supported_(false),
cacheable_(false),
+ has_opaque_data_(false),
last_used_(),
multibuffer_(this, url_index_->block_shift_) {}
@@ -86,6 +87,9 @@ void UrlData::MergeFrom(const scoped_refptr<UrlData>& other) {
last_modified_ = other->last_modified_;
}
bytes_read_from_cache_ += other->bytes_read_from_cache_;
+ // set_has_opaque_data() will not relax from opaque to non-opaque if already
+ // opaque.
+ set_has_opaque_data(other->has_opaque_data_);
multibuffer()->MergeFrom(other->multibuffer());
}
}
@@ -102,6 +106,12 @@ void UrlData::set_length(int64_t length) {
}
}
+void UrlData::set_has_opaque_data(bool has_opaque_data) {
+ if (has_opaque_data_)
+ return;
+ has_opaque_data_ = has_opaque_data;
+}
+
void UrlData::RedirectTo(const scoped_refptr<UrlData>& url_data) {
DCHECK(thread_checker_.CalledOnValidThread());
// Copy any cached data over to the new location.
diff --git a/chromium/media/blink/url_index.h b/chromium/media/blink/url_index.h
index 165b6187f7b..6ffa80800dc 100644
--- a/chromium/media/blink/url_index.h
+++ b/chromium/media/blink/url_index.h
@@ -19,6 +19,7 @@
#include "media/blink/lru.h"
#include "media/blink/media_blink_export.h"
#include "media/blink/multibuffer.h"
+#include "services/network/public/interfaces/fetch_api.mojom.h"
#include "url/gurl.h"
namespace media {
@@ -98,6 +99,8 @@ class MEDIA_BLINK_EXPORT UrlData : public base::RefCounted<UrlData> {
// Returns our url_index.
UrlIndex* url_index() const { return url_index_; }
+ bool has_opaque_data() const { return has_opaque_data_; }
+
// Notifies the url index that this is currently used.
// The url <-> URLData mapping will be eventually be invalidated if
// this is not called regularly.
@@ -106,7 +109,7 @@ class MEDIA_BLINK_EXPORT UrlData : public base::RefCounted<UrlData> {
// Call this before we add some data to the multibuffer().
// If the multibuffer is empty, the data origin is set from
// |origin| and returns true. If not, it compares |origin|
- // to the previous origin and returns wheather they match or not.
+ // to the previous origin and returns whether they match or not.
bool ValidateDataOrigin(const GURL& origin);
// Setters.
@@ -116,6 +119,7 @@ class MEDIA_BLINK_EXPORT UrlData : public base::RefCounted<UrlData> {
void set_range_supported();
void set_last_modified(base::Time last_modified);
void set_etag(const std::string& etag);
+ void set_has_opaque_data(bool has_opaque_data);
// A redirect has occured (or we've found a better UrlData for the same
// resource).
@@ -187,6 +191,10 @@ class MEDIA_BLINK_EXPORT UrlData : public base::RefCounted<UrlData> {
// will not cache this url.
bool cacheable_;
+ // True if a service worker intercepted a request for this resource
+ // and provided an opaque response.
+ bool has_opaque_data_;
+
// Last time some media time used this resource.
// Note that we use base::Time rather than base::TimeTicks because
// TimeTicks will stop advancing when a machine goes to sleep.
diff --git a/chromium/media/blink/webmediaplayer_impl.cc b/chromium/media/blink/webmediaplayer_impl.cc
index 8b681abb3dc..8628760d4d0 100644
--- a/chromium/media/blink/webmediaplayer_impl.cc
+++ b/chromium/media/blink/webmediaplayer_impl.cc
@@ -1103,6 +1103,12 @@ void WebMediaPlayerImpl::Paint(blink::WebCanvas* canvas,
pipeline_metadata_.video_decoder_config.video_rotation(), context_3d);
}
+bool WebMediaPlayerImpl::DidGetOpaqueResponseFromServiceWorker() const {
+ if (data_source_)
+ return data_source_->DidGetOpaqueResponseViaServiceWorker();
+ return false;
+}
+
bool WebMediaPlayerImpl::HasSingleSecurityOrigin() const {
if (data_source_)
return data_source_->HasSingleOrigin();
diff --git a/chromium/media/blink/webmediaplayer_impl.h b/chromium/media/blink/webmediaplayer_impl.h
index a89c9fb6730..a107e2d3d87 100644
--- a/chromium/media/blink/webmediaplayer_impl.h
+++ b/chromium/media/blink/webmediaplayer_impl.h
@@ -180,6 +180,7 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerImpl
blink::WebString GetErrorMessage() const override;
bool DidLoadingProgress() override;
+ bool DidGetOpaqueResponseFromServiceWorker() const override;
bool HasSingleSecurityOrigin() const override;
bool DidPassCORSAccessCheck() const override;
diff --git a/chromium/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp b/chromium/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp
index 86144b4c736..c05d0a33521 100644
--- a/chromium/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp
+++ b/chromium/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp
@@ -1468,17 +1468,30 @@ bool HTMLMediaElement::IsSafeToLoadURL(const KURL& url,
bool HTMLMediaElement::IsMediaDataCORSSameOrigin(
const SecurityOrigin* origin) const {
- // hasSingleSecurityOrigin() tells us whether the origin in the src is
- // the same as the actual request (i.e. after redirect).
- // didPassCORSAccessCheck() means it was a successful CORS-enabled fetch
- // (vs. non-CORS-enabled or failed).
- // taintsCanvas() does checkAccess() on the URL plus allow data sources,
- // to ensure that it is not a URL that requires CORS (basically same
- // origin).
- return HasSingleSecurityOrigin() &&
- ((GetWebMediaPlayer() &&
- GetWebMediaPlayer()->DidPassCORSAccessCheck()) ||
- !origin->TaintsCanvas(currentSrc()));
+ // If a service worker handled the request, we don't know if the origin in the
+ // src is the same as the actual response URL so can't rely on URL checks
+ // alone. So detect an opaque response via
+ // DidGetOpaqueResponseFromServiceWorker().
+ if (GetWebMediaPlayer() &&
+ GetWebMediaPlayer()->DidGetOpaqueResponseFromServiceWorker()) {
+ return false;
+ }
+
+ // At this point, either a service worker was not used, or it didn't provide
+ // an opaque response, so continue with the normal checks.
+
+ // HasSingleSecurityOrigin() tells us whether the origin in the src
+ // is the same as the actual request (i.e. after redirects).
+ if (!HasSingleSecurityOrigin())
+ return false;
+
+ // DidPassCORSAccessCheck() means it was a successful CORS-enabled fetch (vs.
+ // non-CORS-enabled or failed). TaintsCanvas() does CheckAccess() on the URL
+ // plus allows data sources, to ensure that it is not a URL that requires
+ // CORS (basically same origin).
+ return (GetWebMediaPlayer() &&
+ GetWebMediaPlayer()->DidPassCORSAccessCheck()) ||
+ !origin->TaintsCanvas(currentSrc());
}
bool HTMLMediaElement::IsInCrossOriginFrame() const {
diff --git a/chromium/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp b/chromium/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
index 4e3248a3bd2..02e1622701a 100644
--- a/chromium/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
+++ b/chromium/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
@@ -318,6 +318,11 @@ void WebURLResponse::SetResponseTypeViaServiceWorker(
resource_response_->SetResponseTypeViaServiceWorker(value);
}
+network::mojom::FetchResponseType WebURLResponse::ResponseTypeViaServiceWorker()
+ const {
+ return resource_response_->ResponseTypeViaServiceWorker();
+}
+
void WebURLResponse::SetURLListViaServiceWorker(
const WebVector<WebURL>& url_list_via_service_worker) {
Vector<KURL> url_list(url_list_via_service_worker.size());
diff --git a/chromium/third_party/WebKit/Source/platform/testing/EmptyWebMediaPlayer.h b/chromium/third_party/WebKit/Source/platform/testing/EmptyWebMediaPlayer.h
index 2e4eaaab79e..9956dae8750 100644
--- a/chromium/third_party/WebKit/Source/platform/testing/EmptyWebMediaPlayer.h
+++ b/chromium/third_party/WebKit/Source/platform/testing/EmptyWebMediaPlayer.h
@@ -39,6 +39,7 @@ class EmptyWebMediaPlayer : public WebMediaPlayer {
ReadyState GetReadyState() const override { return kReadyStateHaveNothing; }
WebString GetErrorMessage() const override;
bool DidLoadingProgress() override { return false; }
+ bool DidGetOpaqueResponseFromServiceWorker() const override { return false; }
bool HasSingleSecurityOrigin() const override { return true; }
bool DidPassCORSAccessCheck() const override { return true; }
double MediaTimeForTimeValue(double time_value) const override {
diff --git a/chromium/third_party/WebKit/public/platform/WebMediaPlayer.h b/chromium/third_party/WebKit/public/platform/WebMediaPlayer.h
index c2c2967b3ab..ef20586bd98 100644
--- a/chromium/third_party/WebKit/public/platform/WebMediaPlayer.h
+++ b/chromium/third_party/WebKit/public/platform/WebMediaPlayer.h
@@ -176,6 +176,7 @@ class WebMediaPlayer {
virtual bool DidLoadingProgress() = 0;
+ virtual bool DidGetOpaqueResponseFromServiceWorker() const = 0;
virtual bool HasSingleSecurityOrigin() const = 0;
virtual bool DidPassCORSAccessCheck() const = 0;
diff --git a/chromium/third_party/WebKit/public/platform/WebURLResponse.h b/chromium/third_party/WebKit/public/platform/WebURLResponse.h
index 11621be2034..6689ade5768 100644
--- a/chromium/third_party/WebKit/public/platform/WebURLResponse.h
+++ b/chromium/third_party/WebKit/public/platform/WebURLResponse.h
@@ -225,9 +225,12 @@ class WebURLResponse {
// details.
BLINK_PLATFORM_EXPORT void SetWasFallbackRequiredByServiceWorker(bool);
- // The type of the response which was returned by the ServiceWorker.
+ // The type of the response, if it was returned by a service worker. This is
+ // kDefault if the response was not returned by a service worker.
BLINK_PLATFORM_EXPORT void SetResponseTypeViaServiceWorker(
network::mojom::FetchResponseType);
+ BLINK_PLATFORM_EXPORT network::mojom::FetchResponseType
+ ResponseTypeViaServiceWorker() const;
// The URL list of the Response object the ServiceWorker passed to
// respondWith(). See ServiceWorkerResponseInfo::url_list_via_service_worker()