Bug 1521808 - Implement Cross-Origin-Opener-Policy header r=nika,mayhemer

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Valentin Gosu 2019-02-15 22:02:58 +00:00
Родитель 12f7bded8e
Коммит 865d80fff4
16 изменённых файлов: 240 добавлений и 8 удалений

Просмотреть файл

@ -9805,6 +9805,8 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
securityFlags |= nsILoadInfo::SEC_SANDBOXED;
}
// TODO: pass openerPolicy through loadInfo?
RefPtr<LoadInfo> loadInfo =
(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT)
? new LoadInfo(loadingWindow, aLoadState->TriggeringPrincipal(),

Просмотреть файл

@ -489,7 +489,8 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadInfo* aLoadInfo,
aLoadInfo->GetServiceWorkerTaintingSynthesized(),
aLoadInfo->GetDocumentHasUserInteracted(),
aLoadInfo->GetDocumentHasLoaded(), cspNonce,
aLoadInfo->GetIsFromProcessingFrameAttributes());
aLoadInfo->GetIsFromProcessingFrameAttributes(),
aLoadInfo->GetOpenerPolicy());
return NS_OK;
}
@ -651,6 +652,8 @@ nsresult LoadInfoArgsToLoadInfo(
loadInfo->SetIsFromProcessingFrameAttributes();
}
loadInfo->SetOpenerPolicy(loadInfoArgs.openerPolicy());
loadInfo.forget(outLoadInfo);
return NS_OK;
}
@ -662,8 +665,8 @@ void LoadInfoToParentLoadInfoForwarder(
false, void_t(), nsILoadInfo::TAINTING_BASIC,
false, // serviceWorkerTaintingSynthesized
false, // documentHasUserInteracted
false // documentHasLoaded
);
false, // documentHasLoaded
nsILoadInfo::OPENER_POLICY_NULL);
return;
}
@ -676,11 +679,14 @@ void LoadInfoToParentLoadInfoForwarder(
uint32_t tainting = nsILoadInfo::TAINTING_BASIC;
Unused << aLoadInfo->GetTainting(&tainting);
nsILoadInfo::CrossOriginOpenerPolicy openerPolicy =
aLoadInfo->GetOpenerPolicy();
*aForwarderArgsOut = ParentLoadInfoForwarderArgs(
aLoadInfo->GetAllowInsecureRedirectToDataURI(), ipcController, tainting,
aLoadInfo->GetServiceWorkerTaintingSynthesized(),
aLoadInfo->GetDocumentHasUserInteracted(),
aLoadInfo->GetDocumentHasLoaded());
aLoadInfo->GetDocumentHasLoaded(), openerPolicy);
}
nsresult MergeParentLoadInfoForwarder(
@ -709,6 +715,8 @@ nsresult MergeParentLoadInfoForwarder(
aLoadInfo->MaybeIncreaseTainting(aForwarderArgs.tainting());
}
// TODO: merge openerPolicy
MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetDocumentHasUserInteracted(
aForwarderArgs.documentHasUserInteracted()));
MOZ_ALWAYS_SUCCEEDS(

Просмотреть файл

@ -32,6 +32,7 @@
#include "nsExceptionHandler.h"
#include "nsHashKeys.h"
#include "nsID.h"
#include "nsILoadInfo.h"
#include "nsIWidget.h"
#include "nsMemory.h"
#include "nsString.h"
@ -1073,6 +1074,21 @@ struct ParamTraits<mozilla::dom::Optional<T>> {
}
};
struct CrossOriginOpenerPolicyValidator {
static bool IsLegalValue(nsILoadInfo::CrossOriginOpenerPolicy e) {
return e == nsILoadInfo::OPENER_POLICY_NULL ||
e == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN ||
e == nsILoadInfo::OPENER_POLICY_SAME_SITE ||
e == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_OUTGOING ||
e == nsILoadInfo::OPENER_POLICY_SAME_SITE_ALLOW_OUTGOING;
}
};
template <>
struct ParamTraits<nsILoadInfo::CrossOriginOpenerPolicy>
: EnumSerializer<nsILoadInfo::CrossOriginOpenerPolicy,
CrossOriginOpenerPolicyValidator> {};
} /* namespace IPC */
#endif /* __IPC_GLUE_IPCMESSAGEUTILS_H__ */

Просмотреть файл

@ -91,7 +91,8 @@ LoadInfo::LoadInfo(
mServiceWorkerTaintingSynthesized(false),
mDocumentHasUserInteracted(false),
mDocumentHasLoaded(false),
mIsFromProcessingFrameAttributes(false) {
mIsFromProcessingFrameAttributes(false),
mOpenerPolicy(nsILoadInfo::OPENER_POLICY_NULL) {
MOZ_ASSERT(mLoadingPrincipal);
MOZ_ASSERT(mTriggeringPrincipal);
@ -377,7 +378,8 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
mServiceWorkerTaintingSynthesized(false),
mDocumentHasUserInteracted(false),
mDocumentHasLoaded(false),
mIsFromProcessingFrameAttributes(false) {
mIsFromProcessingFrameAttributes(false),
mOpenerPolicy(nsILoadInfo::OPENER_POLICY_NULL) {
// Top-level loads are never third-party
// Grab the information we can out of the window.
MOZ_ASSERT(aOuterWindow);
@ -484,7 +486,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
mDocumentHasUserInteracted(rhs.mDocumentHasUserInteracted),
mDocumentHasLoaded(rhs.mDocumentHasLoaded),
mCspNonce(rhs.mCspNonce),
mIsFromProcessingFrameAttributes(rhs.mIsFromProcessingFrameAttributes) {}
mIsFromProcessingFrameAttributes(rhs.mIsFromProcessingFrameAttributes),
mOpenerPolicy(rhs.mOpenerPolicy) {}
LoadInfo::LoadInfo(
nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal,
@ -564,7 +567,8 @@ LoadInfo::LoadInfo(
mDocumentHasUserInteracted(aDocumentHasUserInteracted),
mDocumentHasLoaded(aDocumentHasLoaded),
mCspNonce(aCspNonce),
mIsFromProcessingFrameAttributes(false) {
mIsFromProcessingFrameAttributes(false),
mOpenerPolicy(nsILoadInfo::OPENER_POLICY_NULL) {
// Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
MOZ_ASSERT(mLoadingPrincipal ||
aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
@ -1411,5 +1415,17 @@ LoadInfo::SetCspEventListener(nsICSPEventListener* aCSPEventListener) {
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetOpenerPolicy(nsILoadInfo::CrossOriginOpenerPolicy* aOpenerPolicy) {
*aOpenerPolicy = mOpenerPolicy;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetOpenerPolicy(nsILoadInfo::CrossOriginOpenerPolicy aOpenerPolicy) {
mOpenerPolicy = aOpenerPolicy;
return NS_OK;
}
} // namespace net
} // namespace mozilla

Просмотреть файл

@ -207,6 +207,8 @@ class LoadInfo final : public nsILoadInfo {
// browsing context container.
// See nsILoadInfo.isFromProcessingFrameAttributes
bool mIsFromProcessingFrameAttributes;
nsILoadInfo::CrossOriginOpenerPolicy mOpenerPolicy;
};
} // namespace net

Просмотреть файл

@ -1075,4 +1075,15 @@ interface nsILoadInfo : nsISupports
* or https://html.spec.whatwg.org/multipage/obsolete.html#process-the-frame-attributes
*/
[infallible] readonly attribute boolean isFromProcessingFrameAttributes;
cenum CrossOriginOpenerPolicy : 8 {
OPENER_POLICY_NULL = 0,
OPENER_POLICY_SAME_ORIGIN = 1,
OPENER_POLICY_SAME_SITE = 2,
OPENER_POLICY_UNSAFE_ALLOW_OUTGOING_FLAG = 0x80,
OPENER_POLICY_SAME_ORIGIN_ALLOW_OUTGOING = OPENER_POLICY_SAME_ORIGIN | OPENER_POLICY_UNSAFE_ALLOW_OUTGOING_FLAG,
OPENER_POLICY_SAME_SITE_ALLOW_OUTGOING = OPENER_POLICY_SAME_SITE | OPENER_POLICY_UNSAFE_ALLOW_OUTGOING_FLAG
};
[infallible] attribute nsILoadInfo_CrossOriginOpenerPolicy openerPolicy;
};

Просмотреть файл

@ -22,6 +22,7 @@ using ArrayOfStringPairs from "mozilla/net/PHttpChannelParams.h";
using struct nsHttpAtom from "nsHttp.h";
using class mozilla::net::nsHttpResponseHead from "nsHttpResponseHead.h";
using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
using nsILoadInfo::CrossOriginOpenerPolicy from "ipc/IPCMessageUtils.h";
namespace mozilla {
namespace net {
@ -112,6 +113,7 @@ struct LoadInfoArgs
bool documentHasLoaded;
nsString cspNonce;
bool isFromProcessingFrameAttributes;
CrossOriginOpenerPolicy openerPolicy;
};
/**
@ -152,6 +154,8 @@ struct ParentLoadInfoForwarderArgs
bool documentHasUserInteracted;
bool documentHasLoaded;
CrossOriginOpenerPolicy openerPolicy;
// IMPORTANT: when you add new properites here you must also update
// LoadInfoToParentLoadInfoForwarder and MergeParentLoadInfoForwarder
// in BackgroundUtils.cpp/.h!

Просмотреть файл

@ -16,6 +16,7 @@
#include "prio.h"
#include "mozilla/net/DNS.h"
#include "TimingStruct.h"
#include "nsILoadInfo.h"
namespace IPC {

Просмотреть файл

@ -2222,6 +2222,11 @@ HttpBaseChannel::SwitchProcessTo(mozilla::dom::Promise* aTabParent,
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
HttpBaseChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
HttpBaseChannel::UpgradeToSecure() {
// Upgrades are handled internally between http-on-modify-request and

Просмотреть файл

@ -220,6 +220,7 @@ class HttpBaseChannel : public nsHashPropertyBag,
NS_IMETHOD RedirectTo(nsIURI *newURI) override;
NS_IMETHOD SwitchProcessTo(mozilla::dom::Promise *aTabParent,
uint64_t aIdentifier) override;
NS_IMETHOD HasCrossOriginOpenerPolicyMismatch(bool *aMismatch) override;
NS_IMETHOD UpgradeToSecure() override;
NS_IMETHOD GetRequestContextID(uint64_t *aRCID) override;
NS_IMETHOD GetTransferSize(uint64_t *aTransferSize) override;

Просмотреть файл

@ -274,6 +274,11 @@ NullHttpChannel::SwitchProcessTo(mozilla::dom::Promise *aTabParent,
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
NullHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool *aMismatch) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
NullHttpChannel::UpgradeToSecure() { return NS_ERROR_NOT_IMPLEMENTED; }

Просмотреть файл

@ -40,6 +40,7 @@ HTTP_ATOM(Content_MD5, "Content-MD5")
HTTP_ATOM(Content_Range, "Content-Range")
HTTP_ATOM(Content_Type, "Content-Type")
HTTP_ATOM(Cookie, "Cookie")
HTTP_ATOM(Cross_Origin_Opener_Policy, "Cross-Origin-Opener-Policy")
HTTP_ATOM(Date, "Date")
HTTP_ATOM(DAV, "DAV")
HTTP_ATOM(Depth, "Depth")

Просмотреть файл

@ -123,6 +123,7 @@
#include "mozilla/dom/ServiceWorkerUtils.h"
#include "mozilla/net/AsyncUrlChannelClassifier.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "nsIWebNavigation.h"
#ifdef MOZ_TASK_TRACER
# include "GeckoTaskTracer.h"
@ -7145,6 +7146,147 @@ nsresult nsHttpChannel::StartCrossProcessRedirect() {
return rv;
}
static nsILoadInfo::CrossOriginOpenerPolicy GetCrossOriginOpenerPolicy(
nsHttpResponseHead *responseHead) {
MOZ_ASSERT(responseHead);
nsAutoCString openerPolicy;
Unused << responseHead->GetHeader(nsHttp::Cross_Origin_Opener_Policy,
openerPolicy);
// Cross-Origin-Opener-Policy = sameness [ RWS outgoing ]
// sameness = %s"same-origin" / %s"same-site" ; case-sensitive
// outgoing = %s"unsafe-allow-outgoing" ; case-sensitive
Tokenizer t(openerPolicy);
nsAutoCString sameness;
nsAutoCString outgoing;
// 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.
return nsILoadInfo::OPENER_POLICY_NULL;
}
if (!outgoing.EqualsLiteral("unsafe-allow-outgoing")) {
// Malformed response. Only one allowed value for the second token.
return nsILoadInfo::OPENER_POLICY_NULL;
}
}
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;
}
}
return policy;
}
static bool CompareCrossOriginOpenerPolicies(
nsILoadInfo::CrossOriginOpenerPolicy documentPolicy,
nsIPrincipal *documentOrigin,
nsILoadInfo::CrossOriginOpenerPolicy resultPolicy,
nsIPrincipal *resultOrigin) {
if (documentPolicy == nsILoadInfo::OPENER_POLICY_NULL &&
resultPolicy == nsILoadInfo::OPENER_POLICY_NULL) {
return true;
}
if (documentPolicy != resultPolicy) {
return false;
}
// For the next checks the document and result will have matching policies.
// We either check if they are same origin or same site.
if ((documentPolicy & nsILoadInfo::OPENER_POLICY_SAME_ORIGIN) &&
documentOrigin->Equals(resultOrigin)) {
return true;
}
if (documentPolicy & nsILoadInfo::OPENER_POLICY_SAME_SITE) {
nsAutoCString siteOriginA;
nsAutoCString siteOriginB;
documentOrigin->GetSiteOrigin(siteOriginA);
resultOrigin->GetSiteOrigin(siteOriginB);
if (siteOriginA == siteOriginB) {
return true;
}
}
return false;
}
NS_IMETHODIMP
nsHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool *aMismatch) {
MOZ_ASSERT(aMismatch);
if (!aMismatch) {
return NS_ERROR_INVALID_ARG;
}
*aMismatch = false;
// Only consider Cross-Origin-Opener-Policy for toplevel document loads.
if (mLoadInfo->GetExternalContentPolicyType() !=
nsIContentPolicy::TYPE_DOCUMENT) {
return NS_OK;
}
// Maybe the channel failed and we have no response head?
nsHttpResponseHead *head =
mResponseHead ? mResponseHead : mCachedResponseHead;
if (!head) {
return NS_ERROR_NOT_AVAILABLE;
}
// Get the policy of the active document, and the policy for the result.
nsILoadInfo::CrossOriginOpenerPolicy documentPolicy =
mLoadInfo->GetOpenerPolicy();
nsILoadInfo::CrossOriginOpenerPolicy resultPolicy =
GetCrossOriginOpenerPolicy(head);
// We use the top window principal as the documentOrigin
if (!mTopWindowPrincipal) {
GetTopWindowPrincipal(getter_AddRefs(mTopWindowPrincipal));
}
nsCOMPtr<nsIPrincipal> documentOrigin = mTopWindowPrincipal;
nsCOMPtr<nsIPrincipal> resultOrigin;
nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
this, getter_AddRefs(resultOrigin));
if (!CompareCrossOriginOpenerPolicies(documentPolicy, documentOrigin,
resultPolicy, resultOrigin)) {
// If one of the following is false:
// - doc is the initial about:blank document
// - document's unsafe-allow-outgoing is true
// - resultPolicy is null
// then we have a mismatch.
if (!documentOrigin->GetIsNullPrincipal() ||
!(documentPolicy &
nsILoadInfo::OPENER_POLICY_UNSAFE_ALLOW_OUTGOING_FLAG) ||
!(resultPolicy == nsILoadInfo::OPENER_POLICY_NULL)) {
*aMismatch = true;
return NS_OK;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
nsresult rv;

Просмотреть файл

@ -162,6 +162,7 @@ class nsHttpChannel final : public HttpBaseChannel,
NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
NS_IMETHOD SwitchProcessTo(mozilla::dom::Promise *aTabParent,
uint64_t aIdentifier) override;
NS_IMETHOD HasCrossOriginOpenerPolicyMismatch(bool *aMismatch) override;
// nsIHttpChannelInternal
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
NS_IMETHOD SetChannelIsForDownload(bool aChannelIsForDownload) override;

Просмотреть файл

@ -457,6 +457,13 @@ interface nsIHttpChannel : nsIChannel
[must_use] void switchProcessTo(in Promise aTabPromise,
in unsigned long long aIdentifier);
/**
* Used to determine if there is a Cross-Origin-Opener-Policy mismatch
* that would require switching the channel to another process.
* @throws NS_ERROR_NOT_AVAILABLE if we don't have a responseHead
*/
[must_use] boolean hasCrossOriginOpenerPolicyMismatch();
/**
* Flags a channel to be upgraded to HTTPS.
*

Просмотреть файл

@ -972,6 +972,16 @@ nsViewSourceChannel::SwitchProcessTo(mozilla::dom::Promise *aTabParent,
: mHttpChannel->SwitchProcessTo(aTabParent, aIdentifier);
}
NS_IMETHODIMP
nsViewSourceChannel::HasCrossOriginOpenerPolicyMismatch(bool *aMismatch) {
MOZ_ASSERT(aMismatch);
if (!aMismatch) {
return NS_ERROR_INVALID_ARG;
}
*aMismatch = false;
return NS_OK;
}
NS_IMETHODIMP
nsViewSourceChannel::UpgradeToSecure() {
return !mHttpChannel ? NS_ERROR_NULL_POINTER