Bug 1554847 - Improve cross-origin checks in canvas API - consider intermediate redirects, r=jya

Differential Revision: https://phabricator.services.mozilla.com/D32792

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andrea Marchesini 2019-06-04 06:32:37 +00:00
parent ee29b15f6b
commit f4c9f068fa
22 changed files with 129 additions and 11 deletions

View File

@ -4238,6 +4238,11 @@ CanvasRenderingContext2D::CachedSurfaceFromElement(Element* aElement) {
return res;
}
if (NS_FAILED(imgRequest->GetHadCrossOriginRedirects(
&res.mHadCrossOriginRedirects))) {
return res;
}
res.mSourceSurface = CanvasImageCache::LookupAllCanvas(aElement);
if (!res.mSourceSurface) {
return res;
@ -4251,7 +4256,8 @@ CanvasRenderingContext2D::CachedSurfaceFromElement(Element* aElement) {
res.mSize = res.mSourceSurface->GetSize();
res.mPrincipal = principal.forget();
res.mImageRequest = imgRequest.forget();
res.mIsWriteOnly = CheckWriteOnlySecurity(res.mCORSUsed, res.mPrincipal);
res.mIsWriteOnly = CheckWriteOnlySecurity(res.mCORSUsed, res.mPrincipal,
res.mHadCrossOriginRedirects);
return res;
}

View File

@ -174,8 +174,7 @@ bool IsImageExtractionAllowed(Document* aDocument, JSContext* aCx,
if (XRE_IsContentProcess()) {
BrowserChild* browserChild = BrowserChild::GetFrom(win);
if (browserChild) {
browserChild->SendShowCanvasPermissionPrompt(origin,
isAutoBlockCanvas);
browserChild->SendShowCanvasPermissionPrompt(origin, isAutoBlockCanvas);
}
} else {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
@ -295,12 +294,17 @@ bool HasDrawWindowPrivilege(JSContext* aCx, JSObject* /* unused */) {
nsGkAtoms::all_urlsPermission);
}
bool CheckWriteOnlySecurity(bool aCORSUsed, nsIPrincipal* aPrincipal) {
bool CheckWriteOnlySecurity(bool aCORSUsed, nsIPrincipal* aPrincipal,
bool aHadCrossOriginRedirects) {
if (!aPrincipal) {
return true;
}
if (!aCORSUsed) {
if (aHadCrossOriginRedirects) {
return true;
}
nsIGlobalObject* incumbentSettingsObject = dom::GetIncumbentGlobal();
if (NS_WARN_IF(!incumbentSettingsObject)) {
return true;

View File

@ -176,7 +176,8 @@ void DashArrayToJSVal(nsTArray<T>& dashes, JSContext* cx,
// returns true if write-only mode must used for this principal based on
// the incumbent global.
bool CheckWriteOnlySecurity(bool aCORSUsed, nsIPrincipal* aPrincipal);
bool CheckWriteOnlySecurity(bool aCORSUsed, nsIPrincipal* aPrincipal,
bool aHadCrossOriginRedirects);
} // namespace CanvasUtils
} // namespace mozilla

View File

@ -770,8 +770,10 @@ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
// Check security.
nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentVideoPrincipal();
bool hadCrossOriginRedirects = aVideoEl.HadCrossOriginRedirects();
bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE;
bool writeOnly = CheckWriteOnlySecurity(CORSUsed, principal);
bool writeOnly =
CheckWriteOnlySecurity(CORSUsed, principal, hadCrossOriginRedirects);
// Create ImageBitmap.
RefPtr<layers::Image> data = aVideoEl.GetCurrentImage();

View File

@ -5867,6 +5867,13 @@ already_AddRefed<nsIPrincipal> HTMLMediaElement::GetCurrentPrincipal() {
return nullptr;
}
bool HTMLMediaElement::HadCrossOriginRedirects() {
if (mDecoder) {
return mDecoder->HadCrossOriginRedirects();
}
return false;
}
already_AddRefed<nsIPrincipal> HTMLMediaElement::GetCurrentVideoPrincipal() {
if (mDecoder) {
return mDecoder->GetCurrentPrincipal();

View File

@ -279,6 +279,10 @@ class HTMLMediaElement : public nsGenericHTMLElement,
// principal. Returns null if nothing is playing.
already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
// Return true if the loading of this resource required cross-origin
// redirects.
bool HadCrossOriginRedirects();
// Principal of the currently playing video resource. Anything accessing the
// image container of this element must have a principal that subsumes this
// principal. If there are no live video tracks but content has been rendered

View File

@ -72,6 +72,10 @@ class BaseMediaResource : public MediaResource,
// Get the current principal for the channel
virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
// Return true if the loading of this resource required cross-origin
// redirects.
virtual bool HadCrossOriginRedirects() = 0;
/**
* Open the stream. This creates a stream listener and returns it in
* aStreamListener; this listener needs to be notified of incoming data.

View File

@ -501,6 +501,11 @@ already_AddRefed<nsIPrincipal> ChannelMediaDecoder::GetCurrentPrincipal() {
return mResource ? mResource->GetCurrentPrincipal() : nullptr;
}
bool ChannelMediaDecoder::HadCrossOriginRedirects() {
MOZ_ASSERT(NS_IsMainThread());
return mResource ? mResource->HadCrossOriginRedirects() : false;
}
bool ChannelMediaDecoder::IsTransportSeekable() {
MOZ_ASSERT(NS_IsMainThread());
return mResource->IsTransportSeekable();

View File

@ -91,6 +91,7 @@ class ChannelMediaDecoder
void AddSizeOfResources(ResourceSizes* aSizes) override;
already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
bool HadCrossOriginRedirects() override;
bool IsTransportSeekable() override;
void SetLoadInBackground(bool aLoadInBackground) override;
void Suspend() override;

View File

@ -11,6 +11,7 @@
#include "nsIClassOfService.h"
#include "nsIInputStream.h"
#include "nsIThreadRetargetableRequest.h"
#include "nsITimedChannel.h"
#include "nsHttp.h"
#include "nsNetUtil.h"
@ -604,6 +605,11 @@ already_AddRefed<nsIPrincipal> ChannelMediaResource::GetCurrentPrincipal() {
return do_AddRef(mSharedInfo->mPrincipal);
}
bool ChannelMediaResource::HadCrossOriginRedirects() {
MOZ_ASSERT(NS_IsMainThread());
return mSharedInfo->mHadCrossOriginRedirects;
}
bool ChannelMediaResource::CanClone() {
return !mClosed && mCacheStream.IsAvailableForSharing();
}
@ -816,6 +822,20 @@ void ChannelMediaResource::UpdatePrincipal() {
r->CacheClientNotifyPrincipalChanged();
}
}
// ChannelMediaResource can recreate the channel. When this happens, we don't
// want to overwrite mHadCrossOriginRedirects because the new channel could
// skip intermediate redirects.
if (!mSharedInfo->mHadCrossOriginRedirects) {
nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel);
if (timedChannel) {
bool allRedirectsSameOrigin = false;
mSharedInfo->mHadCrossOriginRedirects =
NS_SUCCEEDED(timedChannel->GetAllRedirectsSameOrigin(
&allRedirectsSameOrigin)) &&
!allRedirectsSameOrigin;
}
}
}
void ChannelMediaResource::CacheClientNotifySuspendedStatusChanged(

View File

@ -66,8 +66,12 @@ class ChannelMediaResource
// Store information shared among resources. Main thread only.
struct SharedInfo {
NS_INLINE_DECL_REFCOUNTING(SharedInfo);
SharedInfo() : mHadCrossOriginRedirects(false) {}
nsCOMPtr<nsIPrincipal> mPrincipal;
nsTArray<ChannelMediaResource*> mResources;
bool mHadCrossOriginRedirects;
private:
~SharedInfo() = default;
@ -117,6 +121,7 @@ class ChannelMediaResource
void Suspend(bool aCloseImmediately) override;
void Resume() override;
already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
bool HadCrossOriginRedirects() override;
bool CanClone() override;
already_AddRefed<BaseMediaResource> CloneData(
MediaResourceCallback* aDecoder) override;

View File

@ -164,6 +164,20 @@ CloneableWithRangeMediaResource::GetCurrentPrincipal() {
return principal.forget();
}
bool CloneableWithRangeMediaResource::HadCrossOriginRedirects() {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel);
if (!timedChannel) {
return false;
}
bool allRedirectsSameOrigin = false;
return NS_SUCCEEDED(timedChannel->GetAllRedirectsSameOrigin(
&allRedirectsSameOrigin)) &&
!allRedirectsSameOrigin;
}
nsresult CloneableWithRangeMediaResource::ReadFromCache(char* aBuffer,
int64_t aOffset,
uint32_t aCount) {

View File

@ -31,6 +31,7 @@ class CloneableWithRangeMediaResource : public BaseMediaResource {
void Suspend(bool aCloseImmediately) override {}
void Resume() override {}
already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
bool HadCrossOriginRedirects() override;
nsresult ReadFromCache(char* aBuffer, int64_t aOffset,
uint32_t aCount) override;

View File

@ -11,6 +11,7 @@
#include "nsContentUtils.h"
#include "nsIFileChannel.h"
#include "nsIFileStreams.h"
#include "nsITimedChannel.h"
#include "nsNetUtil.h"
namespace mozilla {
@ -117,6 +118,20 @@ already_AddRefed<nsIPrincipal> FileMediaResource::GetCurrentPrincipal() {
return principal.forget();
}
bool FileMediaResource::HadCrossOriginRedirects() {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel);
if (!timedChannel) {
return false;
}
bool allRedirectsSameOrigin = false;
return NS_SUCCEEDED(timedChannel->GetAllRedirectsSameOrigin(
&allRedirectsSameOrigin)) &&
!allRedirectsSameOrigin;
}
nsresult FileMediaResource::ReadFromCache(char* aBuffer, int64_t aOffset,
uint32_t aCount) {
MutexAutoLock lock(mLock);

View File

@ -27,6 +27,7 @@ class FileMediaResource : public BaseMediaResource {
void Suspend(bool aCloseImmediately) override {}
void Resume() override {}
already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
bool HadCrossOriginRedirects() override;
nsresult ReadFromCache(char* aBuffer, int64_t aOffset,
uint32_t aCount) override;

View File

@ -126,6 +126,10 @@ class MediaDecoder : public DecoderDoctorLifeLogger<MediaDecoder> {
// Return the principal of the current URI being played or downloaded.
virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
// Return true if the loading of this resource required cross-origin
// redirects.
virtual bool HadCrossOriginRedirects() = 0;
// Return the time position in the video stream being
// played measured in seconds.
virtual double GetCurrentTime();

View File

@ -182,6 +182,12 @@ already_AddRefed<nsIPrincipal> HLSDecoder::GetCurrentPrincipal() {
return nullptr;
}
bool HLSDecoder::HadCrossOriginRedirects() {
MOZ_ASSERT(NS_IsMainThread());
// Bug 1478843
return false;
}
void HLSDecoder::Play() {
MOZ_ASSERT(NS_IsMainThread());
HLS_DEBUG("HLSDecoder", "MediaElement called Play");

View File

@ -35,6 +35,7 @@ class HLSDecoder final : public MediaDecoder {
void AddSizeOfResources(ResourceSizes* aSizes) override;
already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
bool HadCrossOriginRedirects() override;
bool IsTransportSeekable() override { return true; }
void Suspend() override;
void Resume() override;

View File

@ -335,6 +335,11 @@ already_AddRefed<nsIPrincipal> MediaSourceDecoder::GetCurrentPrincipal() {
return do_AddRef(mPrincipal);
}
bool MediaSourceDecoder::HadCrossOriginRedirects() {
MOZ_ASSERT(NS_IsMainThread());
return false;
}
#undef MSE_DEBUG
#undef MSE_DEBUGV

View File

@ -50,6 +50,8 @@ class MediaSourceDecoder : public MediaDecoder,
already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
bool HadCrossOriginRedirects() override;
bool IsTransportSeekable() override { return true; }
// Returns a structure describing the state of the MediaSource internal

View File

@ -7514,10 +7514,14 @@ nsLayoutUtils::SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
result.mCORSUsed = (corsmode != imgIRequest::CORS_NONE);
}
bool hadCrossOriginRedirects = true;
imgRequest->GetHadCrossOriginRedirects(&hadCrossOriginRedirects);
result.mPrincipal = principal.forget();
result.mHadCrossOriginRedirects = hadCrossOriginRedirects;
result.mImageRequest = imgRequest.forget();
result.mIsWriteOnly =
CanvasUtils::CheckWriteOnlySecurity(result.mCORSUsed, result.mPrincipal);
result.mIsWriteOnly = CanvasUtils::CheckWriteOnlySecurity(
result.mCORSUsed, result.mPrincipal, result.mHadCrossOriginRedirects);
return result;
}
@ -7565,6 +7569,7 @@ nsLayoutUtils::SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
result.mHasSize = true;
result.mSize = size;
result.mPrincipal = aElement->NodePrincipal();
result.mHadCrossOriginRedirects = false;
result.mIsWriteOnly = aElement->IsWriteOnly();
return result;
@ -7611,8 +7616,9 @@ nsLayoutUtils::SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
result.mHasSize = true;
result.mSize = result.mLayersImage->GetSize();
result.mPrincipal = principal.forget();
result.mIsWriteOnly =
CanvasUtils::CheckWriteOnlySecurity(result.mCORSUsed, result.mPrincipal);
result.mHadCrossOriginRedirects = aElement->HadCrossOriginRedirects();
result.mIsWriteOnly = CanvasUtils::CheckWriteOnlySecurity(
result.mCORSUsed, result.mPrincipal, result.mHadCrossOriginRedirects);
return result;
}
@ -8626,7 +8632,8 @@ bool nsLayoutUtils::IsAPZTestLoggingEnabled() {
nsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult()
// Use safe default values here
: mIsWriteOnly(true),
: mHadCrossOriginRedirects(false),
mIsWriteOnly(true),
mIsStillLoading(false),
mHasSize(false),
mCORSUsed(false),

View File

@ -2163,6 +2163,9 @@ class nsLayoutUtils {
nsCOMPtr<nsIPrincipal> mPrincipal;
/* The image request, if the element is an nsIImageLoadingContent */
nsCOMPtr<imgIRequest> mImageRequest;
/* True if cross-origins redirects have been done in order to load this
* resource */
bool mHadCrossOriginRedirects;
/* Whether the element was "write only", that is, the bits should not be
* exposed to content */
bool mIsWriteOnly;