From 122ebb1e31fdd0721db38d659a4537fcfb1f7bcd Mon Sep 17 00:00:00 2001 From: Junior Hsu Date: Sat, 10 Aug 2019 23:24:41 +0000 Subject: [PATCH] Bug 1543066 - P1 Implement COOP:unsafe-inherit r=nika Differential Revision: https://phabricator.services.mozilla.com/D40356 --HG-- extra : moz-landing-system : lando --- dom/ipc/WindowGlobalChild.cpp | 3 +- netwerk/base/nsILoadInfo.idl | 1 + .../protocol/http/ClassifierDummyChannel.cpp | 3 +- netwerk/protocol/http/HttpBaseChannel.cpp | 113 ++++++++++++------ netwerk/protocol/http/HttpBaseChannel.h | 7 +- netwerk/protocol/http/nsHttpChannel.cpp | 2 +- .../protocol/http/nsIHttpChannelInternal.idl | 3 +- 7 files changed, 91 insertions(+), 41 deletions(-) diff --git a/dom/ipc/WindowGlobalChild.cpp b/dom/ipc/WindowGlobalChild.cpp index 1475d1e14f7f..fe5828a48a10 100644 --- a/dom/ipc/WindowGlobalChild.cpp +++ b/dom/ipc/WindowGlobalChild.cpp @@ -74,7 +74,8 @@ already_AddRefed WindowGlobalChild::Create( nsCOMPtr chan = do_QueryInterface(aWindow->GetDocument()->GetChannel()); nsILoadInfo::CrossOriginOpenerPolicy policy; - if (chan && NS_SUCCEEDED(chan->GetCrossOriginOpenerPolicy(&policy))) { + if (chan && NS_SUCCEEDED(chan->GetCrossOriginOpenerPolicy( + nsILoadInfo::OPENER_POLICY_NULL, &policy))) { bc->SetOpenerPolicy(policy); } diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl index fc0778afc6b1..956b8d8b8348 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -1199,6 +1199,7 @@ interface nsILoadInfo : nsISupports OPENER_POLICY_NULL = 0, OPENER_POLICY_SAME_ORIGIN = 1, OPENER_POLICY_SAME_SITE = 2, + OPENER_POLICY_SAMENESS_MASK = 0x0f, OPENER_POLICY_EMBEDDER_POLICY_REQUIRE_CORP_FLAG = 0x10, OPENER_POLICY_UNSAFE_ALLOW_OUTGOING_FLAG = 0x80, OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP = diff --git a/netwerk/protocol/http/ClassifierDummyChannel.cpp b/netwerk/protocol/http/ClassifierDummyChannel.cpp index 9e2fd9e80928..25f550ead0ed 100644 --- a/netwerk/protocol/http/ClassifierDummyChannel.cpp +++ b/netwerk/protocol/http/ClassifierDummyChannel.cpp @@ -636,7 +636,8 @@ void ClassifierDummyChannel::SetHasSandboxedAuxiliaryNavigations( bool aHasSandboxedAuxiliaryNavigations) {} NS_IMETHODIMP ClassifierDummyChannel::GetCrossOriginOpenerPolicy( - nsILoadInfo::CrossOriginOpenerPolicy* aPolicy) { + nsILoadInfo::CrossOriginOpenerPolicy aInitiatorPolicy, + nsILoadInfo::CrossOriginOpenerPolicy* aOutPolicy) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 6ae06e1054e3..1298940d1101 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -4299,8 +4299,41 @@ nsresult HttpBaseChannel::GetResponseEmbedderPolicy( return NS_OK; } +namespace { + +nsILoadInfo::CrossOriginOpenerPolicy GetSameness( + nsILoadInfo::CrossOriginOpenerPolicy aPolicy) { + uint8_t sameness = aPolicy & nsILoadInfo::OPENER_POLICY_SAMENESS_MASK; + return nsILoadInfo::CrossOriginOpenerPolicy(sameness); +} + +nsILoadInfo::CrossOriginOpenerPolicy CreateCrossOriginOpenerPolicy( + nsILoadInfo::CrossOriginOpenerPolicy aSameness, bool aUnsafeAllowOutgoing, + bool aEmbedderPolicy) { + uint8_t policy = aSameness; + + if (aUnsafeAllowOutgoing) { + policy |= nsILoadInfo::OPENER_POLICY_UNSAFE_ALLOW_OUTGOING_FLAG; + } + + if (aEmbedderPolicy) { + policy |= nsILoadInfo::OPENER_POLICY_EMBEDDER_POLICY_REQUIRE_CORP_FLAG; + } + + return nsILoadInfo::CrossOriginOpenerPolicy(policy); +} + +} // anonymous namespace + +// Obtain a cross-origin opener-policy from a response response and a +// cross-origin opener policy initiator. +// https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e NS_IMETHODIMP HttpBaseChannel::GetCrossOriginOpenerPolicy( - nsILoadInfo::CrossOriginOpenerPolicy* aPolicy) { + nsILoadInfo::CrossOriginOpenerPolicy aInitiatorPolicy, + nsILoadInfo::CrossOriginOpenerPolicy* aOutPolicy) { + MOZ_ASSERT(aOutPolicy); + *aOutPolicy = nsILoadInfo::OPENER_POLICY_NULL; + if (!mResponseHead) { return NS_ERROR_NOT_AVAILABLE; } @@ -4309,59 +4342,67 @@ NS_IMETHODIMP HttpBaseChannel::GetCrossOriginOpenerPolicy( Unused << mResponseHead->GetHeader(nsHttp::Cross_Origin_Opener_Policy, openerPolicy); - // Cross-Origin-Opener-Policy = sameness [ RWS outgoing ] + // Cross-Origin-Opener-Policy = sameness [ RWS outgoing ] / inherit // sameness = %s"same-origin" / %s"same-site" ; case-sensitive // outgoing = %s"unsafe-allow-outgoing" ; case-sensitive + // inherit = %s"unsafe-inherit" ; case-sensitive - Tokenizer t(openerPolicy); - nsAutoCString sameness; - nsAutoCString outgoing; + nsILoadInfo::CrossOriginOpenerPolicy sameness = + nsILoadInfo::OPENER_POLICY_NULL; + bool unsafeAllowOutgoing = false; + bool embedderPolicy = false; + if (openerPolicy.EqualsLiteral("unsafe-inherit")) { + // Step 6 + sameness = GetSameness(aInitiatorPolicy); + unsafeAllowOutgoing = + !!(aInitiatorPolicy & + nsILoadInfo::OPENER_POLICY_UNSAFE_ALLOW_OUTGOING_FLAG); + } else { + // Step 7 + Tokenizer t(openerPolicy); + nsAutoCString samenessString; + nsAutoCString outgoingString; - // The return value will be true if we find any whitespace. If there is - // whitespace, then it must be followed by "unsafe-allow-outgoing" otherwise - // this is a malformed header value. - bool allowOutgoing = t.ReadUntil(Tokenizer::Token::Whitespace(), sameness); - if (allowOutgoing) { - t.SkipWhites(); - bool foundEOF = t.ReadUntil(Tokenizer::Token::EndOfFile(), outgoing); - if (!foundEOF) { - // Malformed response. There should be no text after the second token. - *aPolicy = nsILoadInfo::OPENER_POLICY_NULL; - return NS_OK; + // The return value will be true if we find any whitespace. If there is + // whitespace, then it must be followed by "unsafe-allow-outgoing" otherwise + // this is a malformed header value. + bool unsafeAllowOutgoing = + t.ReadUntil(Tokenizer::Token::Whitespace(), samenessString); + if (unsafeAllowOutgoing) { + t.SkipWhites(); + bool foundEOF = + t.ReadUntil(Tokenizer::Token::EndOfFile(), outgoingString); + if (!foundEOF) { + // Malformed response. There should be no text after the second token. + return NS_OK; + } + if (!outgoingString.EqualsLiteral("unsafe-allow-outgoing")) { + // Malformed response. Only one allowed value for the second token. + return NS_OK; + } } - if (!outgoing.EqualsLiteral("unsafe-allow-outgoing")) { - // Malformed response. Only one allowed value for the second token. - *aPolicy = nsILoadInfo::OPENER_POLICY_NULL; - return NS_OK; - } - } - nsILoadInfo::CrossOriginOpenerPolicy policy = nsILoadInfo::OPENER_POLICY_NULL; - if (sameness.EqualsLiteral("same-origin")) { - policy = nsILoadInfo::OPENER_POLICY_SAME_ORIGIN; - if (allowOutgoing) { - policy = nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_OUTGOING; - } - } else if (sameness.EqualsLiteral("same-site")) { - policy = nsILoadInfo::OPENER_POLICY_SAME_SITE; - if (allowOutgoing) { - policy = nsILoadInfo::OPENER_POLICY_SAME_SITE_ALLOW_OUTGOING; + if (samenessString.EqualsLiteral("same-origin")) { + sameness = nsILoadInfo::OPENER_POLICY_SAME_ORIGIN; + } else if (samenessString.EqualsLiteral("same-site")) { + sameness = nsILoadInfo::OPENER_POLICY_SAME_SITE; } } // Step 9 in obtain a cross-origin opener-policy // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e - if (policy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN) { + if (sameness == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN && + !unsafeAllowOutgoing) { nsILoadInfo::CrossOriginEmbedderPolicy coep = nsILoadInfo::EMBEDDER_POLICY_NULL; if (NS_SUCCEEDED(GetResponseEmbedderPolicy(&coep)) && coep == nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP) { - policy = - nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP; + embedderPolicy = true; } } - *aPolicy = policy; + *aOutPolicy = CreateCrossOriginOpenerPolicy(sameness, unsafeAllowOutgoing, + embedderPolicy); return NS_OK; } diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index 4e2cb8673139..391e6eeeb06e 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -316,7 +316,8 @@ class HttpBaseChannel : public nsHashPropertyBag, virtual void SetIPv4Disabled(void) override; virtual void SetIPv6Disabled(void) override; NS_IMETHOD GetCrossOriginOpenerPolicy( - nsILoadInfo::CrossOriginOpenerPolicy* aPolicy) override; + nsILoadInfo::CrossOriginOpenerPolicy aInitiatorPolicy, + nsILoadInfo::CrossOriginOpenerPolicy* aOutPolicy) override; virtual bool GetHasSandboxedAuxiliaryNavigations() override { return mHasSandboxedNavigations; } @@ -548,6 +549,10 @@ class HttpBaseChannel : public nsHashPropertyBag, nsresult GetResponseEmbedderPolicy( nsILoadInfo::CrossOriginEmbedderPolicy* aResponseEmbedderPolicy); + nsresult GetCrossOriginOpenerPolicyWithInitiator( + nsILoadInfo::CrossOriginOpenerPolicy aInitiatorPolicy, + nsILoadInfo::CrossOriginOpenerPolicy* aOutPolicy); + friend class PrivateBrowsingChannel; friend class InterceptFailedOnStop; diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 20b0181a883f..67c4f2ead3d4 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -7419,7 +7419,7 @@ nsresult nsHttpChannel::ComputeCrossOriginOpenerPolicyMismatch() { nsILoadInfo::CrossOriginOpenerPolicy documentPolicy = ctx->GetOpenerPolicy(); nsILoadInfo::CrossOriginOpenerPolicy resultPolicy = nsILoadInfo::OPENER_POLICY_NULL; - GetCrossOriginOpenerPolicy(&resultPolicy); + Unused << GetCrossOriginOpenerPolicy(documentPolicy, &resultPolicy); if (!ctx->Canonical()->GetCurrentWindowGlobal()) { return NS_ERROR_NOT_AVAILABLE; diff --git a/netwerk/protocol/http/nsIHttpChannelInternal.idl b/netwerk/protocol/http/nsIHttpChannelInternal.idl index f9607c01463e..97bca909f6db 100644 --- a/netwerk/protocol/http/nsIHttpChannelInternal.idl +++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl @@ -379,7 +379,8 @@ interface nsIHttpChannelInternal : nsISupports void setIPv6Disabled(); [noscript] - nsILoadInfo_CrossOriginOpenerPolicy getCrossOriginOpenerPolicy(); + nsILoadInfo_CrossOriginOpenerPolicy getCrossOriginOpenerPolicy( + in nsILoadInfo_CrossOriginOpenerPolicy aInitiatorPolicy); [notxpcom, nostdcall] attribute boolean hasSandboxedAuxiliaryNavigations; };