Bug 1172165 - part 0: make view-source dangerous to load, check nested URI schemes, r?bz draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Wed, 05 Aug 2015 14:56:28 +0100
changeset 315064 db0d623ea76a48774d1e90a682939ea038f6df91
parent 314878 412e4d7ce98ca4dbc37de133d0f26d7e1a59946f
child 315065 cfee597d74fc034a066e1435bb3c37d275e1e60c
push id8328
push usergijskruitbosch@gmail.com
push dateFri, 11 Dec 2015 16:25:24 +0000
reviewersbz
bugs1172165
milestone45.0a1
Bug 1172165 - part 0: make view-source dangerous to load, check nested URI schemes, r?bz
caps/nsScriptSecurityManager.cpp
netwerk/protocol/viewsource/nsViewSourceHandler.cpp
netwerk/test/mochitests/mochitest.ini
netwerk/test/mochitests/test_viewsource_unlinkable.html
--- 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>
+
+