Bug 1171853 - stop treating nested URIs as same-origin except jar:, about: and wyciwyg:, r?smaug,bz draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Thu, 28 Dec 2017 21:07:45 +0000
changeset 716026 2bc1cde6a641ba874d0238385619725aad9c04bb
parent 715663 f78a83244fbebe8a469ae3512fce7f638cab7e1f
child 716027 a0dae0cdc633baae78e5499cf4b0bdb5be2f3d91
push id94305
push usergijskruitbosch@gmail.com
push dateThu, 04 Jan 2018 23:59:22 +0000
reviewerssmaug, bz
bugs1171853
milestone59.0a1
Bug 1171853 - stop treating nested URIs as same-origin except jar:, about: and wyciwyg:, r?smaug,bz MozReview-Commit-ID: DGcWmrn7Ce4
caps/ContentPrincipal.cpp
netwerk/base/nsNetUtil.cpp
netwerk/base/nsNetUtil.h
netwerk/test/unit/test_compareURIs.js
--- a/caps/ContentPrincipal.cpp
+++ b/caps/ContentPrincipal.cpp
@@ -7,16 +7,17 @@
 #include "ContentPrincipal.h"
 
 #include "mozIThirdPartyUtil.h"
 #include "nscore.h"
 #include "nsScriptSecurityManager.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "pratom.h"
+#include "nsINestedURI.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsIStandardURL.h"
 #include "nsIURIWithPrincipal.h"
 #include "nsJSPrincipals.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIObjectInputStream.h"
@@ -124,17 +125,17 @@ ContentPrincipal::GetScriptLocation(nsAC
 /* static */ nsresult
 ContentPrincipal::GenerateOriginNoSuffixFromURI(nsIURI* aURI,
                                                 nsACString& aOriginNoSuffix)
 {
   if (!aURI) {
     return NS_ERROR_FAILURE;
   }
 
-  nsCOMPtr<nsIURI> origin = NS_GetInnermostURI(aURI);
+  nsCOMPtr<nsIURI> origin = NS_GetSameOriginInnermostURI(aURI);
   if (!origin) {
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(!NS_IsAboutBlank(origin),
              "The inner URI for about:blank must be moz-safe-about:blank");
 
   // Handle non-strict file:// uris.
@@ -195,16 +196,30 @@ ContentPrincipal::GenerateOriginNoSuffix
     rv = uriWithPrincipal->GetPrincipal(getter_AddRefs(uriPrincipal));
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (uriPrincipal) {
       return uriPrincipal->GetOriginNoSuffix(aOriginNoSuffix);
     }
   }
 
+  // Now unnest any remaining nested URIs and prefix the origin with their protocols.
+  // This will ensure we hit an nsIStandardURL lower down.
+  aOriginNoSuffix.Truncate();
+  nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(origin);
+  while (nestedURI) {
+    nsAutoCString scheme;
+    rv = origin->GetScheme(scheme);
+    NS_ENSURE_SUCCESS(rv, rv);
+    aOriginNoSuffix.Append(scheme);
+    aOriginNoSuffix.AppendLiteral(":");
+    nestedURI->GetInnerURI(getter_AddRefs(origin));
+    nestedURI = do_QueryInterface(origin);
+  }
+
   // If we reached this branch, we can only create an origin if we have a
   // nsIStandardURL.  So, we query to a nsIStandardURL, and fail if we aren't
   // an instance of an nsIStandardURL nsIStandardURLs have the good property
   // of escaping the '^' character in their specs, which means that we can be
   // sure that the caret character (which is reserved for delimiting the end
   // of the spec, and the beginning of the origin attributes) is not present
   // in the origin string
   nsCOMPtr<nsIStandardURL> standardURL = do_QueryInterface(origin);
@@ -217,25 +232,29 @@ ContentPrincipal::GenerateOriginNoSuffix
   bool isChrome = false;
   rv = origin->SchemeIs("chrome", &isChrome);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!isChrome) {
     rv = origin->GetAsciiHostPort(hostPort);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   if (!hostPort.IsEmpty()) {
-    rv = origin->GetScheme(aOriginNoSuffix);
+    nsAutoCString scheme;
+    rv = origin->GetScheme(scheme);
     NS_ENSURE_SUCCESS(rv, rv);
+    aOriginNoSuffix.Append(scheme);
     aOriginNoSuffix.AppendLiteral("://");
     aOriginNoSuffix.Append(hostPort);
     return NS_OK;
   }
 
-  rv = aURI->GetAsciiSpec(aOriginNoSuffix);
+  nsAutoCString asciiSpec;
+  rv = aURI->GetAsciiSpec(asciiSpec);
   NS_ENSURE_SUCCESS(rv, rv);
+  aOriginNoSuffix.Append(asciiSpec);
 
   // The origin, when taken from the spec, should not contain the ref part of
   // the URL.
 
   int32_t pos = aOriginNoSuffix.FindChar('?');
   int32_t hashPos = aOriginNoSuffix.FindChar('#');
 
   if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) {
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2199,39 +2199,87 @@ NS_SecurityHashURI(nsIURI *aURI)
     nsAutoCString host;
     uint32_t hostHash = 0;
     if (NS_SUCCEEDED(baseURI->GetAsciiHost(host)))
         hostHash = mozilla::HashString(host);
 
     return mozilla::AddToHash(schemeHash, hostHash, NS_GetRealPort(baseURI));
 }
 
+// Strip 1 layer of wyciwyg (if present), and any jar nesting.
+already_AddRefed<nsIURI>
+NS_GetSameOriginInnermostURI(nsIURI *aURI)
+{
+    if (!aURI) {
+      return nullptr;
+    }
+    const char* JAR_SCHEME = "jar";
+    const char* ABOUT_SCHEME = "about";
+    const char* WYCIWYG_SCHEME = "wyciwyg";
+    nsCOMPtr<nsIURI> uri(aURI);
+    nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri);
+
+    bool isWyciwyg = false;
+    if (nestedURI && NS_SUCCEEDED(uri->SchemeIs(WYCIWYG_SCHEME, &isWyciwyg)) &&
+        isWyciwyg) {
+      nsresult rv = nestedURI->GetInnerURI(getter_AddRefs(uri));
+      if (NS_FAILED(rv) || !uri) {
+        return nullptr;
+      }
+      nestedURI = do_QueryInterface(uri);
+    }
+
+    bool isAbout = false;
+    if (nestedURI && NS_SUCCEEDED(uri->SchemeIs(ABOUT_SCHEME, &isAbout)) && isAbout) {
+      nsresult rv = nestedURI->GetInnerURI(getter_AddRefs(uri));
+      if (NS_FAILED(rv) || !uri) {
+        return nullptr;
+      }
+      nestedURI = do_QueryInterface(uri);
+    }
+
+    bool isJAR = false;
+    while (nestedURI && NS_SUCCEEDED(uri->SchemeIs(JAR_SCHEME, &isJAR)) && isJAR) {
+      nsresult rv = nestedURI->GetInnerURI(getter_AddRefs(uri));
+      if (NS_FAILED(rv) || !uri) {
+        return nullptr;
+      }
+      isJAR = false;
+      nestedURI = do_QueryInterface(uri);
+    }
+
+    return uri ? uri.forget() : nullptr;
+}
+
 bool
 NS_SecurityCompareURIs(nsIURI *aSourceURI,
                        nsIURI *aTargetURI,
                        bool    aStrictFileOriginPolicy)
 {
     // Note that this is not an Equals() test on purpose -- for URIs that don't
     // support host/port, we want equality to basically be object identity, for
     // security purposes.  Otherwise, for example, two javascript: URIs that
     // are otherwise unrelated could end up "same origin", which would be
     // unfortunate.
     if (aSourceURI && aSourceURI == aTargetURI)
     {
         return true;
     }
 
-    if (!aTargetURI || !aSourceURI)
-    {
-        return false;
-    }
-
-    // If either URI is a nested URI, get the base URI
-    nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(aSourceURI);
-    nsCOMPtr<nsIURI> targetBaseURI = NS_GetInnermostURI(aTargetURI);
+    // For nested URIs, remove 'safe' scheme nesting.
+    // Note that target/source URIs can be null.
+    nsCOMPtr<nsIURI> targetBaseURI = NS_GetSameOriginInnermostURI(aTargetURI);
+    nsCOMPtr<nsIURI> sourceBaseURI = NS_GetSameOriginInnermostURI(aSourceURI);
+    // If the target or source is still nested, they're never same origin:
+    nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(targetBaseURI);
+    if (nestedURI)
+      return false;
+    nestedURI = do_QueryInterface(sourceBaseURI);
+    if (nestedURI)
+      return false;
 
     // If either uri is an nsIURIWithPrincipal
     nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(sourceBaseURI);
     if (uriPrinc) {
         uriPrinc->GetPrincipalUri(getter_AddRefs(sourceBaseURI));
     }
 
     uriPrinc = do_QueryInterface(targetBaseURI);
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -763,16 +763,22 @@ nsresult NS_URIChainHasFlags(nsIURI   *u
 
 /**
  * Helper function for getting the innermost URI for a given URI.  The return
  * value could be just the object passed in if it's not a nested URI.
  */
 already_AddRefed<nsIURI> NS_GetInnermostURI(nsIURI *aURI);
 
 /**
+ * Helper function for getting the same-origin innermost URI for a given URI.
+ * The return value could be just the object passed in if it's not a nested URI.
+ */
+already_AddRefed<nsIURI> NS_GetSameOriginInnermostURI(nsIURI *aURI);
+
+/**
  * Get the "final" URI for a channel.  This is either channel's load info
  * resultPrincipalURI, if set, or GetOriginalURI.  In most cases (but not all) load
  * info resultPrincipalURI, if set, corresponds to URI of the channel if it's required
  * to represent the actual principal for the channel.
  */
 nsresult NS_GetFinalChannelURI(nsIChannel *channel, nsIURI **uri);
 
 // NS_SecurityHashURI must return the same hash value for any two URIs that
--- a/netwerk/test/unit/test_compareURIs.js
+++ b/netwerk/test/unit/test_compareURIs.js
@@ -21,17 +21,17 @@ function run_test()
 	[ "https://mozilla.org:443", "https://mozilla.org/somewhere/", true ],
 	[ "about:", "about:", false ],
 	[ "data:text/plain,text", "data:text/plain,text", false ],
 	[ "about:blank", "about:blank", false ],
 	[ "about:", "http://mozilla.org/", false ],
 	[ "about:", "about:config", false ],
 	[ "about:text/plain,text", "data:text/plain,text", false ],
 	[ "jar:http://mozilla.org/!/", "http://mozilla.org/", true ],
-	[ "view-source:http://mozilla.org/", "http://mozilla.org/", true ]
+	[ "view-source:http://mozilla.org/", "http://mozilla.org/", false ]
     ];
 
     var secman = Components.classes["@mozilla.org/scriptsecuritymanager;1"].getService(Components.interfaces.nsIScriptSecurityManager);
 
     tests.forEach(function(aTest) {
         do_info("Comparing " + aTest[0] + " to " + aTest[1]);
 
 	var uri1 = NetUtil.newURI(aTest[0]);