Bug 1320201 - When an XHR is made with an expanded principal, use the expanded principal for CORS security checks; r=bzbarsky

We should only ensure that the resulting document doesn't end up
inheriting the expanded principal.
This commit is contained in:
Ehsan Akhgari 2016-12-14 20:25:38 -05:00
Родитель 2c794979ff
Коммит d67534c9f0
2 изменённых файлов: 66 добавлений и 24 удалений

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

@ -2500,27 +2500,6 @@ XMLHttpRequestMainThread::CreateChannel()
secFlags |= nsILoadInfo::SEC_COOKIES_OMIT; secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
} }
nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(mPrincipal);
if (ep) {
// If we have an expanded principal, instead of using that, select the
// principal in the whitelist which can load our URL, and use that instead.
nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist = nullptr;
ep->GetWhiteList(&whitelist);
if (!whitelist) {
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(!(secFlags & nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS));
bool dataInherits = (secFlags &
(nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) != 0;
for (const auto& principal : *whitelist) {
if (NS_SUCCEEDED(principal->CheckMayLoad(mRequestURL, false, dataInherits))) {
mPrincipal = principal;
break;
}
}
}
// Use the responsibleDocument if we have it, except for dedicated workers // Use the responsibleDocument if we have it, except for dedicated workers
// where it will be the parent document, which is not the one we want to use. // where it will be the parent document, which is not the one we want to use.
nsresult rv; nsresult rv;
@ -2559,6 +2538,35 @@ XMLHttpRequestMainThread::CreateChannel()
} }
} }
// Using the provided principal as the triggeringPrincipal is fine, since we
// want to be able to access any of the origins that the principal has access
// to during the security checks, but we don't want a document to inherit an
// expanded principal, so in that case we need to select the principal in the
// expanded principal's whitelist that can load our URL as principalToInherit.
nsCOMPtr<nsIPrincipal> resultingDocumentPrincipal(mPrincipal);
nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(mPrincipal);
if (ep) {
nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist = nullptr;
ep->GetWhiteList(&whitelist);
if (!whitelist) {
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(!(secFlags & nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS));
bool dataInherits = (secFlags &
(nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) != 0;
for (const auto& principal : *whitelist) {
if (NS_SUCCEEDED(principal->CheckMayLoad(mRequestURL, false, dataInherits))) {
resultingDocumentPrincipal = principal;
break;
}
}
}
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
rv = loadInfo->SetPrincipalToInherit(resultingDocumentPrincipal);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK; return NS_OK;
} }

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

@ -4,12 +4,16 @@ cu.import("resource://testing-common/httpd.js");
var httpserver = new HttpServer(); var httpserver = new HttpServer();
var httpserver2 = new HttpServer(); var httpserver2 = new HttpServer();
var httpserver3 = new HttpServer();
var testpath = "/simple"; var testpath = "/simple";
var redirectpath = "/redirect";
var negativetestpath = "/negative"; var negativetestpath = "/negative";
var httpbody = "<?xml version='1.0' ?><root>0123456789</root>"; var httpbody = "<?xml version='1.0' ?><root>0123456789</root>";
var sb = cu.Sandbox(["http://www.example.com", var sb = cu.Sandbox(["http://www.example.com",
"http://localhost:4444/simple"], "http://localhost:4444/redirect",
"http://localhost:4444/simple",
"http://localhost:4446/redirect"],
{ wantGlobalProperties: ["XMLHttpRequest"] }); { wantGlobalProperties: ["XMLHttpRequest"] });
function createXHR(loc, async) function createXHR(loc, async)
@ -35,7 +39,7 @@ function checkResults(xhr)
var httpServersClosed = 0; var httpServersClosed = 0;
function finishIfDone() function finishIfDone()
{ {
if (++httpServersClosed == 2) if (++httpServersClosed == 3)
do_test_finished(); do_test_finished();
} }
@ -44,11 +48,15 @@ function run_test()
do_test_pending(); do_test_pending();
httpserver.registerPathHandler(testpath, serverHandler); httpserver.registerPathHandler(testpath, serverHandler);
httpserver.registerPathHandler(redirectpath, redirectHandler1);
httpserver.start(4444); httpserver.start(4444);
httpserver2.registerPathHandler(negativetestpath, serverHandler); httpserver2.registerPathHandler(negativetestpath, serverHandler);
httpserver2.start(4445); httpserver2.start(4445);
httpserver3.registerPathHandler(redirectpath, redirectHandler2);
httpserver3.start(4446);
// Test sync XHR sending // Test sync XHR sending
cu.evalInSandbox('var createXHR = ' + createXHR.toString(), sb); cu.evalInSandbox('var createXHR = ' + createXHR.toString(), sb);
var res = cu.evalInSandbox('var sync = createXHR("4444/simple"); sync.send(null); sync', sb); var res = cu.evalInSandbox('var sync = createXHR("4444/simple"); sync.send(null); sync', sb);
@ -68,7 +76,21 @@ function run_test()
do_check_true(true); do_check_true(true);
} }
// Test redirect handling.
// This request bounces to server 2 and then back to server 1. Neither of
// these servers support CORS, but if the expanded principal is used as the
// triggering principal, this should work.
cu.evalInSandbox('var createXHR = ' + createXHR.toString(), sb);
var res = cu.evalInSandbox('var sync = createXHR("4444/redirect"); sync.send(null); sync', sb);
do_check_true(checkResults(res));
var principal = res.responseXML.nodePrincipal;
do_check_true(principal.isCodebasePrincipal);
var requestURL = "http://localhost:4444/simple";
do_check_eq(principal.URI.spec, requestURL);
httpserver2.stop(finishIfDone); httpserver2.stop(finishIfDone);
httpserver3.stop(finishIfDone);
// Test async XHR sending // Test async XHR sending
sb.finish = function(){ sb.finish = function(){
@ -95,8 +117,20 @@ function run_test()
async.send(null); async.send(null);
} }
function serverHandler(metadata, response) function serverHandler(request, response)
{ {
response.setHeader("Content-Type", "text/xml", false); response.setHeader("Content-Type", "text/xml", false);
response.bodyOutputStream.write(httpbody, httpbody.length); response.bodyOutputStream.write(httpbody, httpbody.length);
} }
function redirectHandler1(request, response)
{
response.setStatusLine(request.httpVersion, 302, "Found");
response.setHeader("Location", "http://localhost:4446/redirect", false);
}
function redirectHandler2(request, response)
{
response.setStatusLine(request.httpVersion, 302, "Found");
response.setHeader("Location", "http://localhost:4444/simple", false);
}