Bug 1175138 P5 Make CacheStorage reject on untrusted origins. r=ehsan

This commit is contained in:
Ben Kelly 2015-06-27 23:19:24 -04:00
parent a045d1505b
commit 3f01155885
5 changed files with 123 additions and 40 deletions

View File

@ -10495,9 +10495,17 @@ already_AddRefed<CacheStorage>
nsGlobalWindow::GetCaches(ErrorResult& aRv)
{
if (!mCacheStorage) {
bool forceTrustedOrigin = false;
if (IsOuterWindow()) {
forceTrustedOrigin = GetServiceWorkersTestingEnabled();
} else {
nsRefPtr<nsGlobalWindow> outer = GetOuterWindowInternal();
forceTrustedOrigin = outer->GetServiceWorkersTestingEnabled();
}
mCacheStorage = CacheStorage::CreateOnMainThread(cache::DEFAULT_NAMESPACE,
this, GetPrincipal(),
IsPrivateBrowsing(), aRv);
IsPrivateBrowsing(),
forceTrustedOrigin, aRv);
}
nsRefPtr<CacheStorage> ref = mCacheStorage;

View File

@ -25,6 +25,7 @@
#include "nsIDocument.h"
#include "nsIGlobalObject.h"
#include "nsIScriptSecurityManager.h"
#include "nsURLParsers.h"
#include "WorkerPrivate.h"
namespace mozilla {
@ -62,11 +63,90 @@ struct CacheStorage::Entry final
nsRefPtr<InternalRequest> mRequest;
};
namespace {
bool
IsTrusted(const PrincipalInfo& aPrincipalInfo, bool aTestingPrefEnabled)
{
// Can happen on main thread or worker thread
if (aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
return true;
}
// Require a ContentPrincipal to avoid null principal, etc.
//
// Also, an unknown appId means that this principal was created for the
// codebase without all the security information from the end document or
// worker. We require exact knowledge of this information before allowing
// the caller to touch the disk using the Cache API.
if (NS_WARN_IF(aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo ||
aPrincipalInfo.get_ContentPrincipalInfo().appId() ==
nsIScriptSecurityManager::UNKNOWN_APP_ID)) {
return false;
}
// If we're in testing mode, then don't do any more work to determing if
// the origin is trusted. We have to run some tests as http.
if (aTestingPrefEnabled) {
return true;
}
// Now parse the scheme of the principal's origin. This is a short term
// method for determining "trust". In the long term we need to implement
// the full algorithm here:
//
// https://w3c.github.io/webappsec/specs/powerfulfeatures/#settings-secure
//
// TODO: Implement full secure setting algorithm. (bug 1177856)
const nsCString& flatURL = aPrincipalInfo.get_ContentPrincipalInfo().spec();
const char* url = flatURL.get();
// off the main thread URL parsing using nsStdURLParser.
nsCOMPtr<nsIURLParser> urlParser = new nsStdURLParser();
uint32_t schemePos;
int32_t schemeLen;
uint32_t authPos;
int32_t authLen;
nsresult rv = urlParser->ParseURL(url, flatURL.Length(),
&schemePos, &schemeLen,
&authPos, &authLen,
nullptr, nullptr); // ignore path
if (NS_WARN_IF(NS_FAILED(rv))) { return false; }
nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen));
if (scheme.LowerCaseEqualsLiteral("https") ||
scheme.LowerCaseEqualsLiteral("app") ||
scheme.LowerCaseEqualsLiteral("file")) {
return true;
}
uint32_t hostPos;
int32_t hostLen;
rv = urlParser->ParseAuthority(url + authPos, authLen,
nullptr, nullptr, // ignore username
nullptr, nullptr, // ignore password
&hostPos, &hostLen,
nullptr); // ignore port
if (NS_WARN_IF(NS_FAILED(rv))) { return false; }
nsDependentCSubstring hostname(url + authPos + hostPos, hostLen);
return hostname.EqualsLiteral("localhost") ||
hostname.EqualsLiteral("127.0.0.1") ||
hostname.EqualsLiteral("::1");
}
} // anonymous namespace
// static
already_AddRefed<CacheStorage>
CacheStorage::CreateOnMainThread(Namespace aNamespace, nsIGlobalObject* aGlobal,
nsIPrincipal* aPrincipal, bool aPrivateBrowsing,
ErrorResult& aRv)
bool aForceTrustedOrigin, ErrorResult& aRv)
{
MOZ_ASSERT(aGlobal);
MOZ_ASSERT(aPrincipal);
@ -78,38 +158,23 @@ CacheStorage::CreateOnMainThread(Namespace aNamespace, nsIGlobalObject* aGlobal,
return ref.forget();
}
bool nullPrincipal;
nsresult rv = aPrincipal->GetIsNullPrincipal(&nullPrincipal);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
if (nullPrincipal) {
NS_WARNING("CacheStorage not supported on null principal.");
nsRefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR);
return ref.forget();
}
// An unknown appId means that this principal was created for the codebase
// without all the security information from the end document or worker.
// We require exact knowledge of this information before allowing the
// caller to touch the disk using the Cache API.
bool unknownAppId = false;
aPrincipal->GetUnknownAppId(&unknownAppId);
if (unknownAppId) {
NS_WARNING("CacheStorage not supported on principal with unknown appId.");
nsRefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR);
return ref.forget();
}
PrincipalInfo principalInfo;
rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
bool testingEnabled = aForceTrustedOrigin ||
Preferences::GetBool("dom.caches.testing.enabled", false) ||
Preferences::GetBool("dom.serviceWorkers.testing.enabled", false);
if (!IsTrusted(principalInfo, testingEnabled)) {
NS_WARNING("CacheStorage not supported on untrusted origins.");
nsRefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR);
return ref.forget();
}
nsRefPtr<CacheStorage> ref = new CacheStorage(aNamespace, aGlobal,
principalInfo, nullptr);
return ref.forget();
@ -138,16 +203,13 @@ CacheStorage::CreateOnWorker(Namespace aNamespace, nsIGlobalObject* aGlobal,
}
const PrincipalInfo& principalInfo = aWorkerPrivate->GetPrincipalInfo();
if (principalInfo.type() == PrincipalInfo::TNullPrincipalInfo) {
NS_WARNING("CacheStorage not supported on null principal.");
nsRefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR);
return ref.forget();
}
if (principalInfo.type() == PrincipalInfo::TContentPrincipalInfo &&
principalInfo.get_ContentPrincipalInfo().appId() ==
nsIScriptSecurityManager::UNKNOWN_APP_ID) {
NS_WARNING("CacheStorage not supported on principal with unknown appId.");
bool testingEnabled = aWorkerPrivate->DOMCachesTestingEnabled() ||
aWorkerPrivate->ServiceWorkersTestingEnabled() ||
aWorkerPrivate->ServiceWorkersTestingInWindow();
if (!IsTrusted(principalInfo, testingEnabled)) {
NS_WARNING("CacheStorage not supported on untrusted origins.");
nsRefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR);
return ref.forget();
}
@ -368,7 +430,10 @@ CacheStorage::Constructor(const GlobalObject& aGlobal,
}
}
return CreateOnMainThread(ns, global, aPrincipal, privateBrowsing, aRv);
// Create a CacheStorage object bypassing the trusted origin checks
// since this is a chrome-only constructor.
return CreateOnMainThread(ns, global, aPrincipal, privateBrowsing,
true /* force trusted origin */, aRv);
}
nsISupports*

View File

@ -50,7 +50,7 @@ public:
static already_AddRefed<CacheStorage>
CreateOnMainThread(Namespace aNamespace, nsIGlobalObject* aGlobal,
nsIPrincipal* aPrincipal, bool aPrivateBrowsing,
ErrorResult& aRv);
bool aForceTrustedOrigin, ErrorResult& aRv);
static already_AddRefed<CacheStorage>
CreateOnWorker(Namespace aNamespace, nsIGlobalObject* aGlobal,

View File

@ -1250,11 +1250,15 @@ CacheCreator::CreateCacheStorage(nsIPrincipal* aPrincipal)
return NS_ERROR_DOM_SECURITY_ERR;
}
// Create a CacheStorage bypassing its trusted origin checks. The
// ServiceWorker has already performed its own checks before getting
// to this point.
ErrorResult error;
mCacheStorage =
CacheStorage::CreateOnMainThread(cache::CHROME_ONLY_NAMESPACE,
mSandboxGlobalObject,
aPrincipal, mPrivateBrowsing,
true /* force trusted origin */,
error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();

View File

@ -59,9 +59,15 @@ CreateCacheStorage(nsIPrincipal* aPrincipal, ErrorResult& aRv,
// a service worker running in private browsing mode. Therefore if
// we are purging scripts or running a comparison algorithm we cannot
// be in private browing.
//
// Also, bypass the CacheStorage trusted origin checks. The ServiceWorker
// has validated the origin prior to this point. All the information
// to revalidate is not available now.
return CacheStorage::CreateOnMainThread(cache::CHROME_ONLY_NAMESPACE,
sandboxGlobalObject, aPrincipal,
false /* private browsing */, aRv);
false /* private browsing */,
true /* force trusted origin */,
aRv);
}
class CompareManager;