Bug 1171215 - Compute third-partyness in the loadinfo instead of nsIHttpChannelInternal so that other protocols correctly respect the third-party cookie pref. r=sicking/ckerschb

This commit is contained in:
Blake Kaplan 2015-11-30 13:25:29 -08:00
parent a328c0c4e8
commit b62a6327bd
15 changed files with 229 additions and 131 deletions

View File

@ -184,8 +184,6 @@ ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel,
nsresult rv;
bool doForce = false;
bool checkWindowChain = true;
bool parentIsThird = false;
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
do_QueryInterface(aChannel);
if (httpChannelInternal) {
@ -203,109 +201,50 @@ ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel,
*aResult = false;
return NS_OK;
}
if (flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_THIRD_PARTY) {
// Check that the two PARENT_IS_{THIRD,SAME}_PARTY are mutually exclusive.
MOZ_ASSERT(!(flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_SAME_PARTY));
// If we're not forcing and we know that the window chain of the channel
// is third party, then we know now that we're third party.
if (!doForce) {
*aResult = true;
return NS_OK;
}
checkWindowChain = false;
parentIsThird = true;
} else {
// In e10s, we can't check the parent chain in the parent, so we do so
// in the child and send the result to the parent.
// Note that we only check the window chain if neither
// THIRD_PARTY_PARENT_IS_* flag is set.
checkWindowChain = !(flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_SAME_PARTY);
parentIsThird = false;
}
}
bool parentIsThird = false;
// Obtain the URI from the channel, and its base domain.
nsCOMPtr<nsIURI> channelURI;
aChannel->GetURI(getter_AddRefs(channelURI));
NS_ENSURE_TRUE(channelURI, NS_ERROR_INVALID_ARG);
rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
if (NS_FAILED(rv))
return rv;
nsCString channelDomain;
rv = GetBaseDomain(channelURI, channelDomain);
if (NS_FAILED(rv))
return rv;
if (aURI) {
// Determine whether aURI is foreign with respect to channelURI.
bool result;
rv = IsThirdPartyInternal(channelDomain, aURI, &result);
if (NS_FAILED(rv))
return rv;
// If it's foreign, or we're forcing, we're done.
if (result || doForce) {
*aResult = result;
return NS_OK;
if (!doForce) {
if (nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo()) {
parentIsThird = loadInfo->GetIsInThirdPartyContext();
if (!parentIsThird &&
loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT) {
// Check if the channel itself is third-party to its own requestor.
// Unforunately, we have to go through the loading principal.
nsCOMPtr<nsIURI> parentURI;
loadInfo->LoadingPrincipal()->GetURI(getter_AddRefs(parentURI));
rv = IsThirdPartyInternal(channelDomain, parentURI, &parentIsThird);
if (NS_FAILED(rv))
return rv;
}
} else {
NS_WARNING("Found channel with no loadinfo, assuming third-party request");
parentIsThird = true;
}
}
// If we've already computed this in the child process, we're done.
if (!checkWindowChain) {
// If we're not comparing to a URI, we have our answer. Otherwise, if
// parentIsThird, we're not forcing and we know that we're a third-party
// request.
if (!aURI || parentIsThird) {
*aResult = parentIsThird;
return NS_OK;
}
// Find the associated window and its parent window.
nsCOMPtr<nsILoadContext> ctx;
NS_QueryNotificationCallbacks(aChannel, ctx);
if (!ctx) return NS_ERROR_INVALID_ARG;
// If there is no window, the consumer kicking off the load didn't provide one
// to the channel. This is limited to loads of certain types of resources. If
// those loads require cookies, the forceAllowThirdPartyCookie property should
// be set on the channel.
nsCOMPtr<nsIDOMWindow> ourWin, parentWin;
ctx->GetAssociatedWindow(getter_AddRefs(ourWin));
if (!ourWin) return NS_ERROR_INVALID_ARG;
nsCOMPtr<nsPIDOMWindow> piOurWin = do_QueryInterface(ourWin);
MOZ_ASSERT(piOurWin);
// We use GetScriptableParent rather than GetParent because we consider
// <iframe mozbrowser/mozapp> to be a top-level frame.
parentWin = piOurWin->GetScriptableParent();
NS_ENSURE_TRUE(parentWin, NS_ERROR_INVALID_ARG);
// Check whether this is the document channel for this window (representing a
// load of a new page). In that situation we want to avoid comparing
// channelURI to ourWin, since what's in ourWin right now will be replaced as
// the channel loads. This covers the case of a freshly kicked-off load
// (e.g. the user typing something in the location bar, or clicking on a
// bookmark), where the window's URI hasn't yet been set, and will be bogus.
// It also covers situations where a subframe is navigated to someting that
// is same-origin with all its ancestors. This is a bit of a nasty hack, but
// we will hopefully flag these channels better later.
nsLoadFlags flags;
rv = aChannel->GetLoadFlags(&flags);
NS_ENSURE_SUCCESS(rv, rv);
if (flags & nsIChannel::LOAD_DOCUMENT_URI) {
if (SameCOMIdentity(ourWin, parentWin)) {
// We only need to compare aURI to the channel URI -- the window's will be
// bogus. We already know the answer.
*aResult = false;
return NS_OK;
}
// Make sure to still compare to ourWin's ancestors
ourWin = parentWin;
}
// Check the window hierarchy. This covers most cases for an ordinary page
// load from the location bar.
return IsThirdPartyWindow(ourWin, channelURI, aResult);
// Determine whether aURI is foreign with respect to channelURI.
return IsThirdPartyInternal(channelDomain, aURI, aResult);
}
NS_IMETHODIMP

View File

@ -67,17 +67,17 @@ function run_test() {
NS_ERROR_INVALID_ARG);
// We can't test isThirdPartyWindow since we can't really set up a window
// heirarchy. We leave that to mochitests.
// hierarchy. We leave that to mochitests.
// Test isThirdPartyChannel. As above, we can't test the bits that require
// a load context or window heirarchy.
// a load context or window heirarchy. Because of that, the code assumes
// that these are all third-party loads.
do_check_throws(function() { util.isThirdPartyChannel(null); },
NS_ERROR_INVALID_ARG);
do_check_throws(function() { util.isThirdPartyChannel(channel1); },
NS_ERROR_INVALID_ARG);
do_check_throws(function() { util.isThirdPartyChannel(channel1, uri1); },
NS_ERROR_INVALID_ARG);
do_check_true(util.isThirdPartyChannel(channel1));
do_check_true(util.isThirdPartyChannel(channel1, uri1));
do_check_true(util.isThirdPartyChannel(channel1, uri2));
let httpchannel1 = channel1.QueryInterface(Ci.nsIHttpChannelInternal);
httpchannel1.forceAllowThirdPartyCookie = true;
do_check_false(util.isThirdPartyChannel(channel1));

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<meta charset=UTF-8>
</head>
<body>
<script>
parent.postMessage(document.cookie, "*");
</script>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<meta charset=UTF-8>
</head>
<body>
<script>
document.cookie = "b=c";
parent.postMessage("done", "*");
</script>
</body>
</html>

View File

@ -0,0 +1,9 @@
/* vim: set ft=javascript: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function handleRequest(request, response) {
response.setHeader("Content-type", "text/html", false);
response.setStatusLine(request.httpVersion, "302", "Found");
response.setHeader("Location", request.queryString, false);
response.write("<!DOCTYPE html><html><body>Look away!</body></html>");
}

View File

@ -33,6 +33,9 @@ support-files =
utils_bug743615.js
worker_bug743615.js
file_bug927901.html
file_cookieOutputter.html
file_redirector.sjs
file_prime_cookie.html
[test_DOMWindowCreated_chromeonly.html]
[test_bug132255.html]
@ -166,3 +169,4 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s
[test_bug1022869.html]
[test_bug1112040.html]
[test_bug1171215.html]

View File

@ -0,0 +1,89 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1022869
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1022869</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<iframe src="about:blank"></iframe>
<script type="text/javascript; version=1.8">
var f = document.getElementsByTagName("iframe")[0];
SimpleTest.waitForExplicitFinish();
/** Test for Bug 1022869 **/
function startTest() {
// Initialize our cookie.
document.cookie = "a=b";
// Set a cookie in example.org so we can test that we can't read it in
// third-party cases.
f.contentWindow.location =
"http://example.org/tests/dom/tests/mochitest/bugs/file_prime_cookie.html";
waitForLoad().then(function() {
// Cookies are set up, disallow third-party cookies and start the test.
SpecialPowers.pushPrefEnv({ set: [[ 'network.cookie.cookieBehavior', 1 ]] },
() => { spawn_task(continueTest); });
}).catch((e) => { ok(false, `Got exception: ${e}`) });
}
function waitForLoad() {
return new Promise((resolve) => {
window.addEventListener("message", function received(msg) {
window.removeEventListener("message", received);
info(`got message ${msg.data}`);
resolve(msg.data);
});
});
}
function* continueTest() {
var sameOrigin = "http://mochi.test:8888";
var thirdParty = "http://example.org";
var page = "tests/dom/tests/mochitest/bugs/file_cookieOutputter.html"
var redirect = "tests/dom/tests/mochitest/bugs/file_redirector.sjs";
function createRedirect(firstOrigin, secondOrigin) {
return `${firstOrigin}/${redirect}?${secondOrigin}/${page}`;
}
info("starting test");
// Same origin to same origin.
f.contentWindow.location = createRedirect(sameOrigin, sameOrigin);
let cookie = yield waitForLoad();
is(cookie, "a=b", "got the cookie");
// Cross origin to cross origin.
f.contentWindow.location = createRedirect(thirdParty, thirdParty);
cookie = yield waitForLoad();
is(cookie, "", "no third-party cookies");
// Same origin to cross origin.
f.contentWindow.location = createRedirect(sameOrigin, thirdParty);
cookie = yield waitForLoad();
is(cookie, "", "no third-party cookies");
// Cross origin to same origin
f.contentWindow.location = createRedirect(thirdParty, sameOrigin);
cookie = yield waitForLoad();
is(cookie, "a=b", "got the cookie");
SimpleTest.finish();
}
</script>
</head>
<body onload="startTest()">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1022869">Mozilla Bug 1022869</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -249,6 +249,7 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
aLoadInfo->GetParentOuterWindowID(),
aLoadInfo->GetEnforceSecurity(),
aLoadInfo->GetInitialSecurityCheckDone(),
aLoadInfo->GetIsInThirdPartyContext(),
aLoadInfo->GetOriginAttributes(),
redirectChainIncludingInternalRedirects,
redirectChain);
@ -304,6 +305,7 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
loadInfoArgs.parentOuterWindowID(),
loadInfoArgs.enforceSecurity(),
loadInfoArgs.initialSecurityCheckDone(),
loadInfoArgs.isInThirdPartyContext(),
loadInfoArgs.originAttributes(),
redirectChainIncludingInternalRedirects,
redirectChain);

View File

@ -8,6 +8,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozIThirdPartyUtil.h"
#include "nsFrameLoader.h"
#include "nsIDocShell.h"
#include "nsIDocument.h"
@ -16,6 +17,7 @@
#include "nsISupportsImpl.h"
#include "nsISupportsUtils.h"
#include "nsContentUtils.h"
#include "nsGlobalWindow.h"
namespace mozilla {
@ -39,6 +41,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
, mParentOuterWindowID(0)
, mEnforceSecurity(false)
, mInitialSecurityCheckDone(false)
, mIsThirdPartyContext(true)
{
MOZ_ASSERT(mLoadingPrincipal);
MOZ_ASSERT(mTriggeringPrincipal);
@ -77,6 +80,8 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
nsCOMPtr<nsPIDOMWindow> parent = outerWindow->GetScriptableParent();
mParentOuterWindowID = parent->WindowID();
ComputeIsThirdPartyContext(outerWindow);
}
mUpgradeInsecureRequests = aLoadingContext->OwnerDoc()->GetUpgradeInsecureRequests();
@ -101,6 +106,7 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
, mParentOuterWindowID(rhs.mParentOuterWindowID)
, mEnforceSecurity(rhs.mEnforceSecurity)
, mInitialSecurityCheckDone(rhs.mInitialSecurityCheckDone)
, mIsThirdPartyContext(rhs.mIsThirdPartyContext)
, mOriginAttributes(rhs.mOriginAttributes)
, mRedirectChainIncludingInternalRedirects(
rhs.mRedirectChainIncludingInternalRedirects)
@ -119,6 +125,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
uint64_t aParentOuterWindowID,
bool aEnforceSecurity,
bool aInitialSecurityCheckDone,
bool aIsThirdPartyContext,
const NeckoOriginAttributes& aOriginAttributes,
nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChainIncludingInternalRedirects,
nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain)
@ -133,6 +140,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
, mParentOuterWindowID(aParentOuterWindowID)
, mEnforceSecurity(aEnforceSecurity)
, mInitialSecurityCheckDone(aInitialSecurityCheckDone)
, mIsThirdPartyContext(aIsThirdPartyContext)
, mOriginAttributes(aOriginAttributes)
{
MOZ_ASSERT(mLoadingPrincipal);
@ -148,6 +156,35 @@ LoadInfo::~LoadInfo()
{
}
void
LoadInfo::ComputeIsThirdPartyContext(nsPIDOMWindow* aOuterWindow)
{
nsContentPolicyType type =
nsContentUtils::InternalContentPolicyTypeToExternal(mInternalContentPolicyType);
if (type == nsIContentPolicy::TYPE_DOCUMENT) {
// Top-level loads are never third-party.
mIsThirdPartyContext = false;
return;
}
nsPIDOMWindow* win = aOuterWindow;
if (type == nsIContentPolicy::TYPE_SUBDOCUMENT) {
// If we're loading a subdocument, aOuterWindow points to the new window.
// Check if its parent is third-party (and then we can do the same check for
// it as we would do for other sub-resource loads.
win = aOuterWindow->GetScriptableParent();
MOZ_ASSERT(win);
}
nsCOMPtr<mozIThirdPartyUtil> util(do_GetService(THIRDPARTYUTIL_CONTRACTID));
if (NS_WARN_IF(!util)) {
return;
}
util->IsThirdPartyWindow(win, nullptr, &mIsThirdPartyContext);
}
NS_IMPL_ISUPPORTS(LoadInfo, nsILoadInfo)
already_AddRefed<nsILoadInfo>
@ -228,7 +265,7 @@ LoadInfo::SetWithCredentialsSecFlag()
}
NS_IMETHODIMP
LoadInfo::GetSecurityMode(uint32_t *aFlags)
LoadInfo::GetSecurityMode(uint32_t* aFlags)
{
*aFlags = (mSecurityFlags &
(nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS |
@ -239,6 +276,13 @@ LoadInfo::GetSecurityMode(uint32_t *aFlags)
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetIsInThirdPartyContext(bool* aIsInThirdPartyContext)
{
*aIsInThirdPartyContext = mIsThirdPartyContext;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetRequireCorsWithCredentials(bool* aResult)
{

View File

@ -17,6 +17,7 @@
#include "mozilla/BasePrincipal.h"
class nsINode;
class nsPIDOMWindow;
class nsXMLHttpRequest;
namespace mozilla {
@ -76,6 +77,7 @@ private:
uint64_t aParentOuterWindowID,
bool aEnforceSecurity,
bool aInitialSecurityCheckDone,
bool aIsThirdPartyRequest,
const NeckoOriginAttributes& aOriginAttributes,
nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChainIncludingInternalRedirects,
nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain);
@ -88,6 +90,8 @@ private:
~LoadInfo();
void ComputeIsThirdPartyContext(nsPIDOMWindow* aOuterWindow);
// This function is the *only* function which can change the securityflags
// of a loadinfo. It only exists because of the XHR code. Don't call it
// from anywhere else!
@ -108,6 +112,7 @@ private:
uint64_t mParentOuterWindowID;
bool mEnforceSecurity;
bool mInitialSecurityCheckDone;
bool mIsThirdPartyContext;
NeckoOriginAttributes mOriginAttributes;
nsTArray<nsCOMPtr<nsIPrincipal>> mRedirectChainIncludingInternalRedirects;
nsTArray<nsCOMPtr<nsIPrincipal>> mRedirectChain;

View File

@ -26,7 +26,7 @@ typedef unsigned long nsSecurityFlags;
/**
* An nsILoadOwner represents per-load information about who started the load.
*/
[scriptable, builtinclass, uuid(c21803eb-ad4f-46bd-b0a5-8081381253f2)]
[scriptable, builtinclass, uuid(5a2dce9f-accd-45ae-98cc-319dec0ae4f0)]
interface nsILoadInfo : nsISupports
{
/**
@ -232,6 +232,14 @@ interface nsILoadInfo : nsISupports
*/
[infallible] readonly attribute unsigned long securityMode;
/**
* True if this request is embedded in a context that can't be third-party
* (i.e. an iframe embedded in a cross-origin parent window). If this is
* false, then this request may be third-party if it's a third-party to
* loadingPrincipal.
*/
[infallible] readonly attribute boolean isInThirdPartyContext;
/**
* Determines whether credentials are sent with CORS requests.
* Using this flag requires SEC_REQUIRE_CORS_DATA_INHERITS also to be set.

View File

@ -786,9 +786,7 @@ nsIOService::NewChannelFromURIWithProxyFlagsInternal(nsIURI* aURI,
// Make sure that all the individual protocolhandlers attach a loadInfo.
if (aLoadInfo) {
// make sure we have the same instance of loadInfo on the newly created channel
nsCOMPtr<nsILoadInfo> loadInfo;
channel->GetLoadInfo(getter_AddRefs(loadInfo));
nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
if (aLoadInfo != loadInfo) {
MOZ_ASSERT(false, "newly created channel must have a loadinfo attached");
return NS_ERROR_UNEXPECTED;

View File

@ -38,6 +38,7 @@ struct LoadInfoArgs
uint64_t parentOuterWindowID;
bool enforceSecurity;
bool initialSecurityCheckDone;
bool isInThirdPartyContext;
NeckoOriginAttributes originAttributes;
PrincipalInfo[] redirectChainIncludingInternalRedirects;
PrincipalInfo[] redirectChain;

View File

@ -1856,22 +1856,9 @@ HttpChannelChild::ContinueAsyncOpen()
optionalCorsPreflightArgs = mozilla::void_t();
}
nsCOMPtr<mozIThirdPartyUtil> util(do_GetService(THIRDPARTYUTIL_CONTRACTID));
if (util) {
bool thirdParty;
nsresult rv = util->IsThirdPartyChannel(this, nullptr, &thirdParty);
if (NS_FAILED(rv)) {
// If we couldn't compute whether this is a third-party load, assume that
// it is.
thirdParty = true;
}
mThirdPartyFlags |= thirdParty ?
nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_THIRD_PARTY :
nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_SAME_PARTY;
nsCOMPtr<nsIURI> uri;
GetTopWindowURI(getter_AddRefs(uri));
}
// NB: This call forces us to cache mTopWindowURI if we haven't already.
nsCOMPtr<nsIURI> uri;
GetTopWindowURI(getter_AddRefs(uri));
SerializeURI(mTopWindowURI, openArgs.topWindowURI());

View File

@ -39,7 +39,7 @@ interface nsIHttpUpgradeListener : nsISupports
* using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned.
*/
[scriptable, uuid(9eabaac6-cc7c-4ca1-9430-65f2daaa578f)]
[scriptable, uuid(23b3f883-ce09-4350-8f67-1d9858d619e1)]
interface nsIHttpChannelInternal : nsISupports
{
/**
@ -84,21 +84,6 @@ interface nsIHttpChannelInternal : nsISupports
*/
const unsigned long THIRD_PARTY_FORCE_ALLOW = 1 << 0;
/**
* This flag is set in the parent if the child has already computed that
* it originates from a 3rd party frame (i.e. a 3rd party iframe).
*/
const unsigned long THIRD_PARTY_PARENT_IS_THIRD_PARTY = 1 << 1;
/**
* This flag is set in the parent if the child has already computed that
* it is not a 3rd party request due to iframe parentage. However, if
* someone calls mozIThirdPartyUtil::IsThirdPartyChannel with a 3rd-party
* URI, the result would be true if the URI is third-party from this
* channel's URI.
*/
const unsigned long THIRD_PARTY_PARENT_IS_SAME_PARTY = 1 << 2;
/**
* When set, these flags modify the algorithm used to decide whether to
* send 3rd party cookies for a given channel.