Bug 1171853 - stop treating nested URIs as same-origin except jar:, about: and wyciwyg:, r?smaug,bz
MozReview-Commit-ID: DGcWmrn7Ce4
--- 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]);