Bug 475636: Disallow refresh to javascript uris. r/sr=bz

--HG--
extra : rebase_source : 1fdfc3148c257aee22001be045258cc985ee027a
This commit is contained in:
Jonas Sicking 2009-03-09 23:03:39 -07:00
Родитель 2d12dd4d03
Коммит 47266550e3
8 изменённых файлов: 294 добавлений и 93 удалений

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

@ -727,6 +727,7 @@ nsDocShell::LoadURI(nsIURI * aURI,
nsCOMPtr<nsIInputStream> headersStream;
nsCOMPtr<nsISupports> owner;
PRBool inheritOwner = PR_FALSE;
PRBool ownerIsExplicit = PR_FALSE;
PRBool sendReferrer = PR_TRUE;
nsCOMPtr<nsISHEntry> shEntry;
nsXPIDLString target;
@ -745,6 +746,7 @@ nsDocShell::LoadURI(nsIURI * aURI,
aLoadInfo->GetOwner(getter_AddRefs(owner));
aLoadInfo->GetInheritOwner(&inheritOwner);
aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit);
aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
aLoadInfo->GetTarget(getter_Copies(target));
aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
@ -862,84 +864,99 @@ nsDocShell::LoadURI(nsIURI * aURI,
("nsDocShell[%p]: loading from session history", this));
#endif
rv = LoadHistoryEntry(shEntry, loadType);
return LoadHistoryEntry(shEntry, loadType);
}
// Perform the load...
else {
// We need an owner (a referring principal). 4 possibilities:
// (1) If the system principal was passed in and we're a typeContent
// docshell, inherit the principal from the current document
// instead.
// (2) In all other cases when the principal passed in is not null,
// use that principal.
// (3) If the caller has allowed inheriting from the current document,
// or if we're being called from system code (eg chrome JS or pure
// C++) then inheritOwner should be true and InternalLoad will get
// an owner from the current document. If none of these things are
// true, then
// (4) we pass a null owner into the channel, and an owner will be
// created later from the channel's internal data.
//
// NOTE: This all only works because the only thing the owner is used
// for in InternalLoad is data:, javascript:, and about:blank
// URIs. For other URIs this would all be dead wrong!
nsCOMPtr<nsIScriptSecurityManager> secMan =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
// We need an owner (a referring principal).
//
// If ownerIsExplicit is not set there are 4 possibilities:
// (1) If the system principal was passed in and we're a typeContent
// docshell, inherit the principal from the current document
// instead.
// (2) In all other cases when the principal passed in is not null,
// use that principal.
// (3) If the caller has allowed inheriting from the current document,
// or if we're being called from system code (eg chrome JS or pure
// C++) then inheritOwner should be true and InternalLoad will get
// an owner from the current document. If none of these things are
// true, then
// (4) we pass a null owner into the channel, and an owner will be
// created later from the channel's internal data.
//
// If ownerIsExplicit *is* set, there are 4 possibilities
// (1) If the system principal was passed in and we're a typeContent
// docshell, return an error.
// (2) In all other cases when the principal passed in is not null,
// use that principal.
// (3) If the caller has allowed inheriting from the current document,
// then inheritOwner should be true and InternalLoad will get an owner
// from the current document. If none of these things are true, then
// (4) we pass a null owner into the channel, and an owner will be
// created later from the channel's internal data.
//
// NOTE: This all only works because the only thing the owner is used
// for in InternalLoad is data:, javascript:, and about:blank
// URIs. For other URIs this would all be dead wrong!
nsCOMPtr<nsIScriptSecurityManager> secMan =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (owner && mItemType != typeChrome) {
nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
PRBool isSystem;
rv = secMan->IsSystemPrincipal(ownerPrincipal, &isSystem);
NS_ENSURE_SUCCESS(rv, rv);
if (owner && mItemType != typeChrome) {
nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
PRBool isSystem;
rv = secMan->IsSystemPrincipal(ownerPrincipal, &isSystem);
NS_ENSURE_SUCCESS(rv, rv);
if (isSystem) {
owner = nsnull;
inheritOwner = PR_TRUE;
if (isSystem) {
if (ownerIsExplicit) {
return NS_ERROR_DOM_SECURITY_ERR;
}
owner = nsnull;
inheritOwner = PR_TRUE;
}
if (!owner && !inheritOwner) {
// See if there's system or chrome JS code running
rv = secMan->SubjectPrincipalIsSystem(&inheritOwner);
if (NS_FAILED(rv)) {
// Set it back to false
inheritOwner = PR_FALSE;
}
}
if (!owner && !inheritOwner && !ownerIsExplicit) {
// See if there's system or chrome JS code running
rv = secMan->SubjectPrincipalIsSystem(&inheritOwner);
if (NS_FAILED(rv)) {
// Set it back to false
inheritOwner = PR_FALSE;
}
PRUint32 flags = 0;
if (inheritOwner)
flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
if (!sendReferrer)
flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD)
flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER)
flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
rv = InternalLoad(aURI,
referrer,
owner,
flags,
target.get(),
nsnull, // No type hint
postStream,
headersStream,
loadType,
nsnull, // No SHEntry
aFirstParty,
nsnull, // No nsIDocShell
nsnull); // No nsIRequest
}
return rv;
PRUint32 flags = 0;
if (inheritOwner)
flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
if (!sendReferrer)
flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD)
flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER)
flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
return InternalLoad(aURI,
referrer,
owner,
flags,
target.get(),
nsnull, // No type hint
postStream,
headersStream,
loadType,
nsnull, // No SHEntry
aFirstParty,
nsnull, // No nsIDocShell
nsnull); // No nsIRequest
}
NS_IMETHODIMP
@ -4576,41 +4593,42 @@ nsDocShell::ForceRefreshURI(nsIURI * aURI,
*/
loadInfo->SetReferrer(mCurrentURI);
/* Don't ever "guess" on which owner to use to avoid picking
* the current owner.
*/
loadInfo->SetOwnerIsExplicit(PR_TRUE);
/* Check if this META refresh causes a redirection
* to another site.
*/
PRBool equalUri = PR_FALSE;
nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh) {
if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
aDelay <= REFRESH_REDIRECT_TIMER) {
/* It is a META refresh based redirection. Now check if it happened
within the threshold time we have in mind(15000 ms as defined by
REFRESH_REDIRECT_TIMER). If so, pass a REPLACE flag to LoadURI().
/* It is a META refresh based redirection within the threshold time
* we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
* Pass a REPLACE flag to LoadURI().
*/
if (aDelay <= REFRESH_REDIRECT_TIMER) {
loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
/* for redirects we mimic HTTP, which passes the
* original referrer
*/
nsCOMPtr<nsIURI> internalReferrer;
GetReferringURI(getter_AddRefs(internalReferrer));
if (internalReferrer) {
loadInfo->SetReferrer(internalReferrer);
}
}
else
loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
/*
* LoadURI(...) will cancel all refresh timers... This causes the
* Timer and its refreshData instance to be released...
/* for redirects we mimic HTTP, which passes the
* original referrer
*/
LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
return NS_OK;
nsCOMPtr<nsIURI> internalReferrer;
GetReferringURI(getter_AddRefs(internalReferrer));
if (internalReferrer) {
loadInfo->SetReferrer(internalReferrer);
}
}
else
else {
loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
}
/*
* LoadURI(...) will cancel all refresh timers... This causes the
* Timer and its refreshData instance to be released...
*/
LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
return NS_OK;
@ -4826,6 +4844,18 @@ nsDocShell::SetupRefreshURIFromHeader(nsIURI * aBaseURI,
CheckLoadURI(aBaseURI, uri,
nsIScriptSecurityManager::
LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
if (NS_SUCCEEDED(rv)) {
PRBool isjs = PR_TRUE;
rv = NS_URIChainHasFlags(uri,
nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
NS_ENSURE_SUCCESS(rv, rv);
if (isjs) {
return NS_ERROR_FAILURE;
}
}
if (NS_SUCCEEDED(rv)) {
// Since we can't travel back in time yet, just pretend
// negative numbers do nothing at all.

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

@ -49,6 +49,7 @@
nsDocShellLoadInfo::nsDocShellLoadInfo()
: mInheritOwner(PR_FALSE),
mOwnerIsExplicit(PR_FALSE),
mSendReferrer(PR_TRUE),
mLoadType(nsIDocShellLoadInfo::loadNormal)
{
@ -118,6 +119,18 @@ NS_IMETHODIMP nsDocShellLoadInfo::SetInheritOwner(PRBool aInheritOwner)
return NS_OK;
}
NS_IMETHODIMP nsDocShellLoadInfo::GetOwnerIsExplicit(PRBool* aOwnerIsExplicit)
{
*aOwnerIsExplicit = mOwnerIsExplicit;
return NS_OK;
}
NS_IMETHODIMP nsDocShellLoadInfo::SetOwnerIsExplicit(PRBool aOwnerIsExplicit)
{
mOwnerIsExplicit = aOwnerIsExplicit;
return NS_OK;
}
NS_IMETHODIMP nsDocShellLoadInfo::GetLoadType(nsDocShellInfoLoadType * aLoadType)
{
NS_ENSURE_ARG_POINTER(aLoadType);

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

@ -66,6 +66,7 @@ protected:
nsCOMPtr<nsIURI> mReferrer;
nsCOMPtr<nsISupports> mOwner;
PRPackedBool mInheritOwner;
PRPackedBool mOwnerIsExplicit;
PRPackedBool mSendReferrer;
nsDocShellInfoLoadType mLoadType;
nsCOMPtr<nsISHEntry> mSHEntry;

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

@ -51,7 +51,7 @@ interface nsISHEntry;
typedef long nsDocShellInfoLoadType;
[scriptable, uuid(4f813a88-7aca-4607-9896-d97270cdf15e)]
[scriptable, uuid(92a0a637-373e-4647-9476-ead11e005c75)]
interface nsIDocShellLoadInfo : nsISupports
{
/** This is the referrer for the load. */
@ -67,6 +67,14 @@ interface nsIDocShellLoadInfo : nsISupports
*/
attribute boolean inheritOwner;
/** If this attribute is true only ever use the owner specify by
* the owner and inheritOwner attributes.
* If there are security reasons for why this is unsafe, such
* as trying to use a systemprincipal owner for a content docshell
* the load fails.
*/
attribute boolean ownerIsExplicit;
/* these are load type enums... */
const long loadNormal = 0; // Normal Load
const long loadNormalReplace = 1; // Normal Load but replaces current history slot

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

@ -69,6 +69,8 @@ _TEST_FILES = \
bug413310-subframe.html \
bug413310-post.sjs \
test_bug402210.html \
test_bug475636.html \
file_bug475636.sjs \
$(NULL)
libs:: $(_TEST_FILES)

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

@ -0,0 +1,90 @@
jsURL = "javascript:" + escape('window.parent.postMessage("JS uri ran", "*");\
return \'\
<script>\
window.parent.postMessage("Able to access private: " +\
window.parent.private, "*");\
</script>\'');
dataURL = "data:text/html," + escape('<!DOCTYPE HTML>\
<script>\
try {\
window.parent.postMessage("Able to access private: " +\
window.parent.private, "*");\
}\
catch (e) {\
window.parent.postMessage("pass", "*");\
}\
</script>');
tests = [
// Plain document should work as normal
'<!DOCTYPE HTML>\
<script>\
try {\
window.parent.private;\
window.parent.postMessage("pass", "*");\
}\
catch (e) {\
window.parent.postMessage("Unble to access private", "*");\
}\
</script>',
// refresh to plain doc
{ refresh: "file_bug475636.sjs?1",
doc: '<!DOCTYPE HTML>' },
// meta-refresh to plain doc
'<!DOCTYPE HTML>\
<head>\
<meta http-equiv="refresh" content="0; url=file_bug475636.sjs?1">\
</head>',
// refresh to data url
{ refresh: dataURL,
doc: '<!DOCTYPE HTML>' },
// meta-refresh to data url
'<!DOCTYPE HTML>\
<head>\
<meta http-equiv="refresh" content="0; url=' + dataURL + '">\
</head>',
// refresh to js url should not be followed
{ refresh: jsURL,
doc:
'<!DOCTYPE HTML>\
<script>\
setTimeout(function() {\
window.parent.postMessage("pass", "*");\
}, 2000);\
</script>' },
// meta refresh to js url should not be followed
'<!DOCTYPE HTML>\
<head>\
<meta http-equiv="refresh" content="0; url=' + jsURL + '">\
</head>\
<script>\
setTimeout(function() {\
window.parent.postMessage("pass", "*");\
}, 2000);\
</script>'
];
function handleRequest(request, response)
{
dump("@@@@@@@@@hi there: " + request.queryString + "\n");
test = tests[parseInt(request.queryString, 10) - 1];
response.setHeader("Content-Type", "text/html");
if (!test) {
response.write('<script>parent.postMessage("done", "*");</script>');
}
else if (typeof test == "string") {
response.write(test);
}
else if (test.refresh) {
response.setHeader("Refresh", "0; url=" + test.refresh);
response.write(test.doc);
}
}

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

@ -0,0 +1,53 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=475636
Test that refresh to data: URIs don't inherit the principal
-->
<head>
<title>Test for Bug 475636</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="gen.next()">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=475636">Mozilla Bug 475636</a>
<div id="content" style="display: none">
</div>
<iframe id=loader></iframe>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.8">
SimpleTest.waitForExplicitFinish();
gen = runTests();
window.private = 42;
window.addEventListener("message", function(e) {
gen.send(e.data);
}, false);
var url = "file_bug475636.sjs?";
function runTests() {
var loader = document.getElementById('loader');
for (var testNum = 1; ; ++testNum) {
loader.src = url + testNum;
let res = (yield);
if (res == "done") {
SimpleTest.finish();
yield;
}
is(res, "pass");
}
}
</script>
</pre>
</body>
</html>

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

@ -1209,6 +1209,10 @@ nsExpatDriver::ConsumeToken(nsScanner& aScanner, PRBool& aFlushTokens)
aScanner.EndReading(end);
}
if (start == end && !aScanner->IsIncremental()) {
mInternalState = kEOF;
}
aScanner.SetPosition(currentExpatPosition, PR_TRUE);
aScanner.Mark();