Bug 1172165 - part 0: make view-source dangerous to load, check nested URI schemes, r?bz
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -602,16 +602,52 @@ EqualOrSubdomain(nsIURI* aProbeArg, nsIU
return false;
}
NS_ENSURE_SUCCESS(rv, false);
rv = probe->SetHost(newHost);
NS_ENSURE_SUCCESS(rv, false);
}
}
+static bool
+AllSchemesMatch(nsIURI* aURI, nsIURI* aOtherURI)
+{
+ nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
+ nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(aOtherURI);
+ auto stringComparator = nsCaseInsensitiveCStringComparator();
+ if (!nestedURI && !nestedOtherURI) {
+ // Neither of the URIs is nested, compare their schemes directly:
+ nsAutoCString scheme, otherScheme;
+ aURI->GetScheme(scheme);
+ aOtherURI->GetScheme(otherScheme);
+ return scheme.Equals(otherScheme, stringComparator);
+ }
+ while (nestedURI && nestedOtherURI) {
+ nsCOMPtr<nsIURI> currentURI = do_QueryInterface(nestedURI);
+ nsCOMPtr<nsIURI> currentOtherURI = do_QueryInterface(nestedOtherURI);
+ nsAutoCString scheme, otherScheme;
+ currentURI->GetScheme(scheme);
+ currentOtherURI->GetScheme(otherScheme);
+ if (!scheme.Equals(otherScheme, stringComparator)) {
+ return false;
+ }
+
+ nestedURI->GetInnerURI(getter_AddRefs(currentURI));
+ nestedOtherURI->GetInnerURI(getter_AddRefs(currentOtherURI));
+ nestedURI = do_QueryInterface(currentURI);
+ nestedOtherURI = do_QueryInterface(currentOtherURI);
+ }
+ if (!!nestedURI != !!nestedOtherURI) {
+ // If only one of the scheme chains runs out at one point, clearly the chains
+ // aren't of the same length, so we bail:
+ return false;
+ }
+ return true;
+}
+
NS_IMETHODIMP
nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
nsIURI *aTargetURI,
uint32_t aFlags)
{
NS_PRECONDITION(aPrincipal, "CheckLoadURIWithPrincipal must have a principal");
// If someone passes a flag that we don't understand, we should
// fail, because they may need a security check that we don't
@@ -708,18 +744,17 @@ nsScriptSecurityManager::CheckLoadURIWit
if (NS_FAILED(rv)) return rv;
if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) {
// A null principal can target its own URI.
if (sourceURI == aTargetURI) {
return NS_OK;
}
}
- else if (targetScheme.Equals(sourceScheme,
- nsCaseInsensitiveCStringComparator()))
+ else if (AllSchemesMatch(sourceURI, aTargetURI))
{
// every scheme can access another URI from the same scheme,
// as long as they don't represent null principals...
// Or they don't require an special permission to do so
// See bug#773886
bool hasFlags;
rv = NS_URIChainHasFlags(targetBaseURI,
@@ -758,17 +793,17 @@ nsScriptSecurityManager::CheckLoadURIWit
// If the schemes don't match, the policy is specified by the protocol
// flags on the target URI. Note that the order of policy checks here is
// very important! We start from most restrictive and work our way down.
// Note that since we're working with the innermost URI, we can just use
// the methods that work on chains of nested URIs and they will only look
// at the flags for our one URI.
// Check for system target URI
- rv = DenyAccessIfURIHasFlags(targetBaseURI,
+ rv = DenyAccessIfURIHasFlags(aTargetURI,
nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
if (NS_FAILED(rv)) {
// Deny access, since the origin principal is not system
if (reportErrors) {
ReportError(nullptr, errorTag, sourceURI, aTargetURI);
}
return rv;
}
@@ -826,17 +861,17 @@ nsScriptSecurityManager::CheckLoadURIWit
if (reportErrors) {
ReportError(nullptr, errorTag, sourceURI, aTargetURI);
}
return NS_ERROR_DOM_BAD_URI;
}
// Check for target URI pointing to a file
- rv = NS_URIChainHasFlags(targetBaseURI,
+ rv = NS_URIChainHasFlags(aTargetURI,
nsIProtocolHandler::URI_IS_LOCAL_FILE,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) {
// Allow domains that were whitelisted in the prefs. In 99.9% of cases,
// this array is empty.
for (size_t i = 0; i < mFileURIWhitelist.Length(); ++i) {
if (EqualOrSubdomain(sourceURI, mFileURIWhitelist[i])) {
@@ -1612,8 +1647,9 @@ nsScriptSecurityManager::PolicyAllowsScr
rv = superExceptions->ContainsSuperDomain(aURI, &contains);
NS_ENSURE_SUCCESS(rv, rv);
if (contains) {
*aRv = !*aRv;
}
return NS_OK;
}
+
--- a/netwerk/protocol/viewsource/nsViewSourceHandler.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceHandler.cpp
@@ -30,17 +30,17 @@ nsViewSourceHandler::GetDefaultPort(int3
{
*result = -1;
return NS_OK;
}
NS_IMETHODIMP
nsViewSourceHandler::GetProtocolFlags(uint32_t *result)
{
- *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE |
+ *result = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD |
URI_NON_PERSISTABLE;
return NS_OK;
}
NS_IMETHODIMP
nsViewSourceHandler::NewURI(const nsACString &aSpec,
const char *aCharset,
nsIURI *aBaseURI,
--- a/netwerk/test/mochitests/mochitest.ini
+++ b/netwerk/test/mochitests/mochitest.ini
@@ -19,16 +19,17 @@ support-files =
[test_rel_preconnect.html]
skip-if = e10s
[test_uri_scheme.html]
[test_user_agent_overrides.html]
skip-if = e10s
[test_user_agent_updates.html]
skip-if = e10s
[test_user_agent_updates_reset.html]
+[test_viewsource_unlinkable.html]
[test_xhr_method_case.html]
[test_signed_web_packaged_app.html]
skip-if = e10s || buildapp != 'browser'
[test_signed_web_packaged_app_origin.html]
skip-if = e10s || buildapp != 'browser'
[test_web_packaged_app.html]
[test_loadinfo_redirectchain.html]
skip-if = buildapp == 'b2g' #no ssl support
new file mode 100644
--- /dev/null
+++ b/netwerk/test/mochitests/test_viewsource_unlinkable.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+-->
+<head>
+ <title>Test for view-source linkability</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script type="text/javascript">
+function runTest() {
+ SimpleTest.doesThrow(function() {
+ window.open('view-source:' + location.href, "_blank");
+ }, "Trying to access view-source URL from unprivileged code should throw.");
+ SimpleTest.finish();
+}
+</script>
+</head>
+<body onload="runTest();">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+</pre>
+</body>
+</html>
+
+