Skip to content

Commit

Permalink
Bug 369814: don't open jar: content unless served from a safe mime ty…
Browse files Browse the repository at this point in the history
…pe. r=bz, sr=dveditz, ui-r=beltzner
  • Loading branch information
dcamp@mozilla.com committed Nov 27, 2007
1 parent 5c63302 commit 1b9adf5
Show file tree
Hide file tree
Showing 18 changed files with 390 additions and 8 deletions.
Expand Up @@ -53,6 +53,7 @@ deniedPortAccess=This address uses a network port which is normally used for pur
proxyResolveFailure=Firefox is configured to use a proxy server that can't be found.
proxyConnectFailure=Firefox is configured to use a proxy server that is refusing connections.
contentEncodingError=The page you are trying to view cannot be shown because it uses an invalid or unsupported form of compression. Please contact the website owners to inform them of this problem.
unsafeContentType=The page you are trying to view cannot be shown because it is contained in a file type that may not be safe to open. Please contact the website owners to inform them of this problem.
externalProtocolTitle=External Protocol Request
externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n
#LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined
Expand Down
7 changes: 7 additions & 0 deletions browser/locales/en-US/chrome/overrides/netError.dtd
Expand Up @@ -66,6 +66,13 @@
</ul>
">

<!ENTITY unsafeContentType.title "Unsafe File Type">
<!ENTITY unsafeContentType.longDesc "
<ul>
<li>Please contact the website owners to inform them of this problem.</li>
</ul>
">

<!ENTITY netReset.title "The connection was reset">
<!ENTITY netReset.longDesc "&sharedLongDesc;">

Expand Down
1 change: 1 addition & 0 deletions docshell/base/Makefile.in
Expand Up @@ -85,6 +85,7 @@ REQUIRES = xpcom \
windowwatcher \
imglib2 \
mimetype \
jar \
$(NULL)

SDK_XPIDLSRCS = \
Expand Down
62 changes: 62 additions & 0 deletions docshell/base/nsDocShell.cpp
Expand Up @@ -168,6 +168,8 @@

#include "nsITextToSubURI.h"

#include "nsIJARChannel.h"

#include "prlog.h"
#include "prmem.h"

Expand Down Expand Up @@ -1300,12 +1302,37 @@ nsDocShell::SetDocumentCharsetInfo(nsIDocumentCharsetInfo *
return NS_OK;
}

NS_IMETHODIMP
nsDocShell::GetChannelIsUnsafe(PRBool *aUnsafe)
{
*aUnsafe = PR_FALSE;

nsCOMPtr<nsIChannel> channel;
GetCurrentDocumentChannel(getter_AddRefs(channel));
if (!channel) {
return NS_OK;
}

nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
if (!jarChannel) {
return NS_OK;
}

return jarChannel->GetIsUnsafe(aUnsafe);
}

NS_IMETHODIMP
nsDocShell::GetAllowPlugins(PRBool * aAllowPlugins)
{
NS_ENSURE_ARG_POINTER(aAllowPlugins);

*aAllowPlugins = mAllowPlugins;
if (!mAllowPlugins) {
return NS_OK;
}

PRBool unsafe;
*aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
return NS_OK;
}

Expand All @@ -1323,6 +1350,12 @@ nsDocShell::GetAllowJavascript(PRBool * aAllowJavascript)
NS_ENSURE_ARG_POINTER(aAllowJavascript);

*aAllowJavascript = mAllowJavascript;
if (!mAllowJavascript) {
return NS_OK;
}

PRBool unsafe;
*aAllowJavascript = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
return NS_OK;
}

Expand All @@ -1338,6 +1371,12 @@ NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(PRBool * aReturn)
NS_ENSURE_ARG_POINTER(aReturn);

*aReturn = mAllowMetaRedirects;
if (!mAllowMetaRedirects) {
return NS_OK;
}

PRBool unsafe;
*aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
return NS_OK;
}

Expand Down Expand Up @@ -3036,6 +3075,10 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
// Bad Content Encoding.
error.AssignLiteral("contentEncodingError");
break;
case NS_ERROR_UNSAFE_CONTENT_TYPE:
// Channel refused to load from an unrecognized content type.
error.AssignLiteral("unsafeContentType");
break;
}
}

Expand Down Expand Up @@ -6553,6 +6596,25 @@ nsDocShell::InternalLoad(nsIURI * aURI,
(aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER) &&
NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &inherits)) &&
inherits) {

// Don't allow loads that would inherit our security context
// if this document came from an unsafe channel.
nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
do {
nsCOMPtr<nsIDocShell> itemDocShell =
do_QueryInterface(treeItem);
PRBool isUnsafe;
if (itemDocShell &&
NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
isUnsafe) {
return NS_ERROR_DOM_SECURITY_ERR;
}

nsCOMPtr<nsIDocShellTreeItem> parent;
treeItem->GetSameTypeParent(getter_AddRefs(parent));
parent.swap(treeItem);
} while (treeItem);

owner = GetInheritedPrincipal(PR_TRUE);
}
}
Expand Down
9 changes: 8 additions & 1 deletion docshell/base/nsIDocShell.idl
Expand Up @@ -68,7 +68,7 @@ interface nsILayoutHistoryState;
interface nsISecureBrowserUI;
interface nsIDOMStorage;

[scriptable, uuid(10ed386d-8598-408c-b571-e75ad18edeb0)]
[scriptable, uuid(4b00222a-8d0a-46d7-a1fe-43bd89d19324)]
interface nsIDocShell : nsISupports
{
/**
Expand Down Expand Up @@ -448,5 +448,12 @@ interface nsIDocShell : nsISupports
* document has been set up)
*/
readonly attribute boolean isInUnload;

/**
* Find out if the currently loaded document came from a suspicious channel
* (such as a JAR channel where the server-returned content type isn't a
* known JAR type).
*/
readonly attribute boolean channelIsUnsafe;
};

1 change: 1 addition & 0 deletions docshell/base/nsWebShell.cpp
Expand Up @@ -1196,6 +1196,7 @@ nsresult nsWebShell::EndPageLoad(nsIWebProgress *aProgress,
aStatus == NS_ERROR_NET_RESET ||
aStatus == NS_ERROR_MALWARE_URI ||
aStatus == NS_ERROR_PHISHING_URI ||
aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
DisplayLoadError(aStatus, url, nsnull, channel);
}
Expand Down
2 changes: 2 additions & 0 deletions docshell/resources/content/netError.xhtml
Expand Up @@ -219,6 +219,7 @@
<h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1>
<h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1>
<h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
<h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
<h1 id="et_nssFailure2">&nssFailure2.title;</h1>
<h1 id="et_nssBadCert">&nssBadCert.title;</h1>
<h1 id="et_malwareBlocked">&malwareBlocked.title;</h1>
Expand All @@ -240,6 +241,7 @@
<div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div>
<div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
<div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
<div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
<div id="ed_nssFailure2">&nssFailure2.longDesc;</div>
<div id="ed_nssBadCert">&nssBadCert.longDesc2;</div>
<div id="ed_malwareBlocked">&malwareBlocked.longDesc;</div>
Expand Down
2 changes: 2 additions & 0 deletions docshell/test/Makefile.in
Expand Up @@ -54,6 +54,8 @@ include $(topsrcdir)/config/rules.mk

_TEST_FILES = \
test_bug344861.html \
test_bug369814.html \
bug369814.zip \
test_bug384014.html \
test_bug387979.html \
test_bug404548.html \
Expand Down
Binary file added docshell/test/bug369814.zip
Binary file not shown.
204 changes: 204 additions & 0 deletions docshell/test/test_bug369814.html
@@ -0,0 +1,204 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=369814
-->
<head>
<title>Test for Bug 369814</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=384014">Mozilla Bug 369814</a>

<p>

<iframe id="testFrame"></iframe>

<pre id="test">
<script class="testbody" type="text/javascript">

/** Tests for Bug 369814 **/

SimpleTest.waitForExplicitFinish();

// Because child scripts won't be able to run to tell us they're done,
// we need to just wait for them. Wait this amount of time before checking
// the results.
const gLoadTimeout = 3000;

var Ci = Components.interfaces;

var gCurrentTest;
var gTargetWindow;
var gNumPokes;
var gPrefValue;

var gTestFrame = document.getElementById('testFrame');

/**
* Called by documents loaded from jar files to indicate that they can access
* this document.
*/
function poke(description) {
ok(false, gCurrentTest['name'] + ": got unexpected poke: " + description);
gNumPokes++;
}

function loadEvent(window, callback)
{
var fn = function() {
window.removeEventListener("load", fn, false);
callback();
};
window.addEventListener("load", fn, false);
}

function loadTestTarget(callback)
{
gTargetWindow = window.open("http://localhost:8888", "bug369814target");
loadEvent(gTargetWindow, callback);
}

function closeTestTarget()
{
gTargetWindow.close();
gTargetWindow = null;
}

function loadErrorTest(test)
{
gTestFrame.src = test['url'];

// Give the frame a chance to fail at loading
setTimeout(function() {
// XXX: There doesn't seem to be a reliable check for "got an error,"
// but reaching in to an error document will throw an exception
var errorPage;
try {
var item = gTestFrame.contentDocument.getElementById(gCurrentTest['data-iframe']);
errorPage = false;
} catch (e) {
errorPage = true;
}
ok(errorPage, gCurrentTest["name"] + ": should block a suspicious JAR load.");

finishTest();
}, gLoadTimeout);
}

function iframeTest(test) {
gTestFrame.src = test['url'];
loadEvent(gTestFrame, function() {
finishTest();
});
}

function refreshTest(test) {
gTestFrame.src = test['url'];
loadEvent(gTestFrame, function() {
// Wait for the frame to try and refresh
// XXX: a "blocked redirect" signal would be needed to get rid of
// this timeout.
setTimeout(function() {
finishTest();
}, gLoadTimeout);
});
}

function anchorTest(test) {
loadTestTarget(function() {
gTestFrame.src = test['url'];
loadEvent(gTestFrame, function() {
sendMouseEvent({type:'click'}, 'target', gTestFrame.contentWindow);
sendMouseEvent({type:'click'}, 'notarget', gTestFrame.contentWindow);

// Give the clicks a chance to load
setTimeout(function() {
closeTestTarget();
finishTest();
}, gLoadTimeout);
});
});
}

var gTests = [
{ "name" : "iframes.html loaded from non-jar type, pref disabled",
"url" : "jar:http://localhost:8888/tests/docshell/test/bug369814.zip!/iframes.html",
"pref" : false,
"pokes" : { },
"func" : loadErrorTest,
},
{ "name" : "refresh.html loaded from non-jar type, pref enabled",
"url" : "jar:http://localhost:8888/tests/docshell/test/bug369814.zip!/refresh.html",
"pref" : true,
"pokes" : { },
"func" : refreshTest,
},
{ "name" : "iframes.html loaded from non-jar type, pref enabled",
"url" : "jar:http://localhost:8888/tests/docshell/test/bug369814.zip!/iframes.html",
"pref" : true,
"pokes" : { },
"func" : iframeTest,
},
{ "name" : "anchors.html loaded from non-jar type, pref enabled",
"url" : "jar:http://localhost:8888/tests/docshell/test/bug369814.zip!/anchors.html",
"pref" : true,
"pokes" : { },
"func" : anchorTest,
},
];

var gNextTest = 0;

function runNextTest()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);

if (gNextTest < gTests.length) {
gCurrentTest = gTests[gNextTest++];
gNumPokes = 0;

prefs.setBoolPref("network.jar.open-unsafe-types", gCurrentTest['pref']);

gCurrentTest['func'](gCurrentTest);
} else {
// Put back the pref value we had at test start
prefs.setBoolPref("network.jar.open-unsafe-types", gPrefValue);
SimpleTest.finish();
}
}

function finishTest()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
prefs.setBoolPref("network.jar.open-unsafe-types", false);

if (gNumPokes == 0) {
ok(true, gCurrentTest["name"] + ": no unexpected pokes");
}

runNextTest();
}

function startTests()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
gPrefValue = prefs.getBoolPref("network.jar.open-unsafe-types");
}

addLoadEvent(runNextTest);

</script>
</pre>
</body>
</html>

0 comments on commit 1b9adf5

Please sign in to comment.