Bug 1376459 - Prevent Offline Application Cache fallback namespace and fallback page be from a parent directory relative to the manifest. r=jduell

This commit is contained in:
Honza Bambas 2017-07-12 16:58:19 -04:00
parent eb211c43fa
commit f008a7d471
2 changed files with 111 additions and 0 deletions

View File

@ -196,6 +196,50 @@ Hash(const char *buf, nsACString &hash)
return NS_OK;
}
bool
IsInSubpathOfAppCacheManifest(nsIApplicationCache *cache, nsACString const& uriSpec)
{
MOZ_ASSERT(cache);
nsresult rv;
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), uriSpec);
if (NS_FAILED(rv)) {
return false;
}
nsCOMPtr<nsIURL> url(do_QueryInterface(uri, &rv));
if (NS_FAILED(rv)) {
return false;
}
nsAutoCString directory;
rv = url->GetDirectory(directory);
if (NS_FAILED(rv)) {
return false;
}
nsCOMPtr<nsIURI> manifestURI;
rv = cache->GetManifestURI(getter_AddRefs(manifestURI));
if (NS_FAILED(rv)) {
return false;
}
nsCOMPtr<nsIURL> manifestURL(do_QueryInterface(manifestURI, &rv));
if (NS_FAILED(rv)) {
return false;
}
nsAutoCString manifestDirectory;
rv = manifestURL->GetDirectory(manifestDirectory);
if (NS_FAILED(rv)) {
return false;
}
return StringBeginsWith(directory, manifestDirectory);
}
} // unnamed namespace
// We only treat 3xx responses as redirects if they have a Location header and
@ -3503,6 +3547,12 @@ nsHttpChannel::ProcessFallback(bool *waitingForRedirectCallback)
return NS_OK;
}
if (!IsInSubpathOfAppCacheManifest(mApplicationCache, mFallbackKey)) {
// Refuse to fallback if the fallback key is not contained in the same
// path as the cache manifest.
return NS_OK;
}
MOZ_ASSERT(fallbackEntryType & nsIApplicationCache::ITEM_FALLBACK,
"Fallback entry not marked correctly!");
@ -4602,6 +4652,17 @@ nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntry *aEntry,
if (namespaceType &
nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
nsAutoCString namespaceSpec;
rv = namespaceEntry->GetNamespaceSpec(namespaceSpec);
NS_ENSURE_SUCCESS(rv, rv);
// This prevents fallback attacks injected by an insecure subdirectory
// for the whole origin (or a parent directory).
if (!IsInSubpathOfAppCacheManifest(mApplicationCache, namespaceSpec)) {
return NS_OK;
}
rv = namespaceEntry->GetData(mFallbackKey);
NS_ENSURE_SUCCESS(rv, rv);
}

View File

@ -801,6 +801,37 @@ nsOfflineManifestItem::AddNamespace(uint32_t namespaceType,
return NS_OK;
}
static nsresult
GetURIDirectory(nsIURI* uri, nsACString &directory)
{
nsresult rv;
nsCOMPtr<nsIURL> url(do_QueryInterface(uri, &rv));
NS_ENSURE_SUCCESS(rv, rv);
rv = url->GetDirectory(directory);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
static nsresult
CheckFileContainedInPath(nsIURI* file, nsACString const &masterDirectory)
{
nsresult rv;
nsAutoCString directory;
rv = GetURIDirectory(file, directory);
NS_ENSURE_SUCCESS(rv, rv);
bool contains = StringBeginsWith(directory, masterDirectory);
if (!contains) {
return NS_ERROR_DOM_BAD_URI;
}
return NS_OK;
}
nsresult
nsOfflineManifestItem::HandleManifestLine(const nsCString::const_iterator &aBegin,
const nsCString::const_iterator &aEnd)
@ -945,6 +976,25 @@ nsOfflineManifestItem::HandleManifestLine(const nsCString::const_iterator &aBegi
if (NS_FAILED(rv))
break;
// The following set of checks is preventing a website under
// a subdirectory to add fallback pages for the whole origin
// (or a parent directory) to prevent fallback attacks.
nsAutoCString manifestDirectory;
rv = GetURIDirectory(mURI, manifestDirectory);
if (NS_FAILED(rv)) {
break;
}
rv = CheckFileContainedInPath(namespaceURI, manifestDirectory);
if (NS_FAILED(rv)) {
break;
}
rv = CheckFileContainedInPath(fallbackURI, manifestDirectory);
if (NS_FAILED(rv)) {
break;
}
// Manifest and namespace must be same origin
if (!NS_SecurityCompareURIs(mURI, namespaceURI,
mStrictFileOriginPolicy))