From 9e09943ad5ed141ed83c3e9204f2acf5f3b1b32d Mon Sep 17 00:00:00 2001 From: Tomislav Jovanovic Date: Wed, 4 Apr 2018 16:54:26 +0200 Subject: [PATCH] Bug 1441336 - Use addon permissions for PerformanceTiming properties r=bz,kmag We need to side-step existing cross-origin checks in Performance Timing code when the caller is a web extension content script that otherwise has permission to access the cross-origin resource. MozReview-Commit-ID: 8IgtqZgPWgY --HG-- extra : rebase_source : e8152c5d8ab32096d1ff7f97311c1b43b57c3694 --- caps/BasePrincipal.cpp | 3 ++ caps/ExpandedPrincipal.cpp | 11 +++++ caps/ExpandedPrincipal.h | 2 + dom/performance/PerformanceResourceTiming.cpp | 38 +++++++++++++++ dom/performance/PerformanceResourceTiming.h | 70 +++++++++++++++++---------- dom/performance/PerformanceTiming.h | 6 +-- dom/webidl/PerformanceResourceTiming.webidl | 16 ++++++ 7 files changed, 117 insertions(+), 29 deletions(-) diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp index e9680dfc20c7..26f7a6d76c7e 100644 --- a/caps/BasePrincipal.cpp +++ b/caps/BasePrincipal.cpp @@ -464,6 +464,9 @@ BasePrincipal::CloneStrippingUserContextIdAndFirstPartyDomain() bool BasePrincipal::AddonAllowsLoad(nsIURI* aURI, bool aExplicit /* = false */) { + if (Is()) { + return As()->AddonAllowsLoad(aURI, aExplicit); + } if (auto policy = AddonPolicy()) { return policy->CanAccessURI(aURI, aExplicit); } diff --git a/caps/ExpandedPrincipal.cpp b/caps/ExpandedPrincipal.cpp index 3c32f1790538..1825c768b767 100644 --- a/caps/ExpandedPrincipal.cpp +++ b/caps/ExpandedPrincipal.cpp @@ -180,6 +180,17 @@ ExpandedPrincipal::AddonHasPermission(const nsAtom* aPerm) return false; } +bool +ExpandedPrincipal::AddonAllowsLoad(nsIURI* aURI, bool aExplicit /* = false */) +{ + for (const auto& principal : mPrincipals) { + if (Cast(principal)->AddonAllowsLoad(aURI, aExplicit)) { + return true; + } + } + return false; +} + nsIPrincipal* ExpandedPrincipal::PrincipalToInherit(nsIURI* aRequestedURI) { diff --git a/caps/ExpandedPrincipal.h b/caps/ExpandedPrincipal.h index eda1049f4fb7..dbabab6aefb4 100644 --- a/caps/ExpandedPrincipal.h +++ b/caps/ExpandedPrincipal.h @@ -37,6 +37,8 @@ public: virtual bool AddonHasPermission(const nsAtom* aPerm) override; virtual nsresult GetScriptLocation(nsACString &aStr) override; + bool AddonAllowsLoad(nsIURI* aURI, bool aExplicit = false); + // Returns the principal to inherit when this principal requests the given // URL. See BasePrincipal::PrincipalToInherit. nsIPrincipal* PrincipalToInherit(nsIURI* aRequestedURI = nullptr); diff --git a/dom/performance/PerformanceResourceTiming.cpp b/dom/performance/PerformanceResourceTiming.cpp index 31f0aa67ae0c..54e8da0672e1 100644 --- a/dom/performance/PerformanceResourceTiming.cpp +++ b/dom/performance/PerformanceResourceTiming.cpp @@ -6,6 +6,7 @@ #include "PerformanceResourceTiming.h" #include "mozilla/dom/PerformanceResourceTimingBinding.h" +#include "nsNetUtil.h" using namespace mozilla::dom; @@ -31,6 +32,11 @@ PerformanceResourceTiming::PerformanceResourceTiming(UniquePtrNextHopProtocol().SizeOfExcludingThisIfUnshared(aMallocSizeOf) : 0); } + +bool +PerformanceResourceTiming::TimingAllowedForCaller(Maybe& aCaller) const +{ + if (!mTimingData) { + return false; + } + + if (mTimingData->TimingAllowed()) { + return true; + } + + // Check if the addon has permission to access the cross-origin resource. + return mOriginalURI && aCaller.isSome() && + BasePrincipal::Cast(aCaller.value())->AddonAllowsLoad(mOriginalURI); +} + +bool +PerformanceResourceTiming::ReportRedirectForCaller(Maybe& aCaller) const +{ + if (!mTimingData) { + return false; + } + + if (mTimingData->ShouldReportCrossOriginRedirect()) { + return true; + } + + // Only report cross-origin redirect if the addon has permission. + return aCaller.isSome() && + BasePrincipal::Cast(aCaller.value())->AddonHasPermission(nsGkAtoms::all_urlsPermission); +} diff --git a/dom/performance/PerformanceResourceTiming.h b/dom/performance/PerformanceResourceTiming.h index cb1f1b5c69cb..404314bc2560 100644 --- a/dom/performance/PerformanceResourceTiming.h +++ b/dom/performance/PerformanceResourceTiming.h @@ -70,54 +70,54 @@ public: : 0; } - DOMHighResTimeStamp RedirectStart() const { + DOMHighResTimeStamp RedirectStart(Maybe& aSubjectPrincipal) const { // We have to check if all the redirect URIs had the same origin (since - // there is no check in RedirectEndHighRes()) - return mTimingData && mTimingData->ShouldReportCrossOriginRedirect() + // there is no check in RedirectStartHighRes()) + return ReportRedirectForCaller(aSubjectPrincipal) ? mTimingData->RedirectStartHighRes(mPerformance) : 0; } - DOMHighResTimeStamp RedirectEnd() const { + DOMHighResTimeStamp RedirectEnd(Maybe& aSubjectPrincipal) const { // We have to check if all the redirect URIs had the same origin (since // there is no check in RedirectEndHighRes()) - return mTimingData && mTimingData->ShouldReportCrossOriginRedirect() + return ReportRedirectForCaller(aSubjectPrincipal) ? mTimingData->RedirectEndHighRes(mPerformance) : 0; } - DOMHighResTimeStamp DomainLookupStart() const { - return mTimingData && mTimingData->TimingAllowed() + DOMHighResTimeStamp DomainLookupStart(Maybe& aSubjectPrincipal) const { + return TimingAllowedForCaller(aSubjectPrincipal) ? mTimingData->DomainLookupStartHighRes(mPerformance) : 0; } - DOMHighResTimeStamp DomainLookupEnd() const { - return mTimingData && mTimingData->TimingAllowed() + DOMHighResTimeStamp DomainLookupEnd(Maybe& aSubjectPrincipal) const { + return TimingAllowedForCaller(aSubjectPrincipal) ? mTimingData->DomainLookupEndHighRes(mPerformance) : 0; } - DOMHighResTimeStamp ConnectStart() const { - return mTimingData && mTimingData->TimingAllowed() + DOMHighResTimeStamp ConnectStart(Maybe& aSubjectPrincipal) const { + return TimingAllowedForCaller(aSubjectPrincipal) ? mTimingData->ConnectStartHighRes(mPerformance) : 0; } - DOMHighResTimeStamp ConnectEnd() const { - return mTimingData && mTimingData->TimingAllowed() + DOMHighResTimeStamp ConnectEnd(Maybe& aSubjectPrincipal) const { + return TimingAllowedForCaller(aSubjectPrincipal) ? mTimingData->ConnectEndHighRes(mPerformance) : 0; } - DOMHighResTimeStamp RequestStart() const { - return mTimingData && mTimingData->TimingAllowed() + DOMHighResTimeStamp RequestStart(Maybe& aSubjectPrincipal) const { + return TimingAllowedForCaller(aSubjectPrincipal) ? mTimingData->RequestStartHighRes(mPerformance) : 0; } - DOMHighResTimeStamp ResponseStart() const { - return mTimingData && mTimingData->TimingAllowed() + DOMHighResTimeStamp ResponseStart(Maybe& aSubjectPrincipal) const { + return TimingAllowedForCaller(aSubjectPrincipal) ? mTimingData->ResponseStartHighRes(mPerformance) : 0; } @@ -128,10 +128,10 @@ public: : 0; } - DOMHighResTimeStamp SecureConnectionStart() const + DOMHighResTimeStamp SecureConnectionStart(Maybe& aSubjectPrincipal) const { - return mTimingData && mTimingData->TimingAllowed() - ? mTimingData->SecureConnectionStartHighRes(mPerformance) + return TimingAllowedForCaller(aSubjectPrincipal) + ? mTimingData->SecureConnectionStartHighRes(mPerformance) : 0; } @@ -140,19 +140,25 @@ public: return this; } - uint64_t TransferSize() const + uint64_t TransferSize(Maybe& aSubjectPrincipal) const { - return mTimingData ? mTimingData->TransferSize() : 0; + return TimingAllowedForCaller(aSubjectPrincipal) + ? mTimingData->TransferSize() + : 0; } - uint64_t EncodedBodySize() const + uint64_t EncodedBodySize(Maybe& aSubjectPrincipal) const { - return mTimingData ? mTimingData->EncodedBodySize() : 0; + return TimingAllowedForCaller(aSubjectPrincipal) + ? mTimingData->EncodedBodySize() + : 0; } - uint64_t DecodedBodySize() const + uint64_t DecodedBodySize(Maybe& aSubjectPrincipal) const { - return mTimingData ? mTimingData->DecodedBodySize() : 0; + return TimingAllowedForCaller(aSubjectPrincipal) + ? mTimingData->DecodedBodySize() + : 0; } size_t @@ -164,9 +170,21 @@ protected: size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override; + // Check if caller has access to cross-origin timings, either by the rules + // from the spec, or based on addon permissions. + bool + TimingAllowedForCaller(Maybe& aCaller) const; + + // Check if cross-origin redirects should be reported to the caller. + bool + ReportRedirectForCaller(Maybe& aCaller) const; + nsString mInitiatorType; UniquePtr mTimingData; RefPtr mPerformance; + + // The same initial requested URI as the `name` attribute. + nsCOMPtr mOriginalURI; }; } // namespace dom diff --git a/dom/performance/PerformanceTiming.h b/dom/performance/PerformanceTiming.h index 6fe88e2db160..9eb7dffedc9f 100644 --- a/dom/performance/PerformanceTiming.h +++ b/dom/performance/PerformanceTiming.h @@ -54,17 +54,17 @@ public: uint64_t TransferSize() const { - return mTimingAllowed ? mTransferSize : 0; + return mTransferSize; } uint64_t EncodedBodySize() const { - return mTimingAllowed ? mEncodedBodySize : 0; + return mEncodedBodySize; } uint64_t DecodedBodySize() const { - return mTimingAllowed ? mDecodedBodySize : 0; + return mDecodedBodySize; } /** diff --git a/dom/webidl/PerformanceResourceTiming.webidl b/dom/webidl/PerformanceResourceTiming.webidl index d494d01dd980..7b4a033f46c9 100644 --- a/dom/webidl/PerformanceResourceTiming.webidl +++ b/dom/webidl/PerformanceResourceTiming.webidl @@ -17,20 +17,36 @@ interface PerformanceResourceTiming : PerformanceEntry readonly attribute DOMString nextHopProtocol; readonly attribute DOMHighResTimeStamp workerStart; + + [NeedsSubjectPrincipal] readonly attribute DOMHighResTimeStamp redirectStart; + [NeedsSubjectPrincipal] readonly attribute DOMHighResTimeStamp redirectEnd; + readonly attribute DOMHighResTimeStamp fetchStart; + + [NeedsSubjectPrincipal] readonly attribute DOMHighResTimeStamp domainLookupStart; + [NeedsSubjectPrincipal] readonly attribute DOMHighResTimeStamp domainLookupEnd; + [NeedsSubjectPrincipal] readonly attribute DOMHighResTimeStamp connectStart; + [NeedsSubjectPrincipal] readonly attribute DOMHighResTimeStamp connectEnd; + [NeedsSubjectPrincipal] readonly attribute DOMHighResTimeStamp secureConnectionStart; + [NeedsSubjectPrincipal] readonly attribute DOMHighResTimeStamp requestStart; + [NeedsSubjectPrincipal] readonly attribute DOMHighResTimeStamp responseStart; + readonly attribute DOMHighResTimeStamp responseEnd; + [NeedsSubjectPrincipal] readonly attribute unsigned long long transferSize; + [NeedsSubjectPrincipal] readonly attribute unsigned long long encodedBodySize; + [NeedsSubjectPrincipal] readonly attribute unsigned long long decodedBodySize; jsonifier; -- 2.11.4.GIT