From 115f9ae25d318e04a71a1180a3e8fb4dcf0c11b1 Mon Sep 17 00:00:00 2001 From: John Schoenick Date: Fri, 31 May 2013 14:33:51 -0700 Subject: [PATCH] Bug 406541 - Split NS_CheckStrictFileOriginPolicy out of nsPrincipal::CheckMayLoad. r=bs --- caps/src/nsPrincipal.cpp | 111 +++++--------------------------- netwerk/base/public/nsNetUtil.h | 90 +++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 95 deletions(-) diff --git a/caps/src/nsPrincipal.cpp b/caps/src/nsPrincipal.cpp index 70d95f74e772..fd06eaac5207 100644 --- a/caps/src/nsPrincipal.cpp +++ b/caps/src/nsPrincipal.cpp @@ -46,7 +46,7 @@ static bool URIIsImmutable(nsIURI* aURI) return mutableObj && NS_SUCCEEDED(mutableObj->GetMutable(&isMutable)) && - !isMutable; + !isMutable; } // Static member variables @@ -344,18 +344,6 @@ nsPrincipal::GetURI(nsIURI** aURI) return NS_EnsureSafeToReturn(mCodebase, aURI); } -static bool -URIIsLocalFile(nsIURI *aURI) -{ - bool isFile; - nsCOMPtr util = do_GetNetUtil(); - - return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI, - nsIProtocolHandler::URI_IS_LOCAL_FILE, - &isFile)) && - isFile; -} - NS_IMETHODIMP nsPrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsPrincipal) { @@ -367,88 +355,23 @@ nsPrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsPrinc } } - if (!nsScriptSecurityManager::SecurityCompareURIs(mCodebase, aURI)) { - if (nsScriptSecurityManager::GetStrictFileOriginPolicy() && - URIIsLocalFile(aURI)) { - nsCOMPtr fileURL(do_QueryInterface(aURI)); - - if (!URIIsLocalFile(mCodebase)) { - // If the codebase is not also a file: uri then forget it - // (don't want resource: principals in a file: doc) - // - // note: we're not de-nesting jar: uris here, we want to - // keep archive content bottled up in its own little island - - if (aReport) { - nsScriptSecurityManager::ReportError( - nullptr, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI); - } - - return NS_ERROR_DOM_BAD_URI; - } - - // - // pull out the internal files - // - nsCOMPtr codebaseFileURL(do_QueryInterface(mCodebase)); - nsCOMPtr targetFile; - nsCOMPtr codebaseFile; - bool targetIsDir; - - // Make sure targetFile is not a directory (bug 209234) - // and that it exists w/out unescaping (bug 395343) - - if (!codebaseFileURL || !fileURL || - NS_FAILED(fileURL->GetFile(getter_AddRefs(targetFile))) || - NS_FAILED(codebaseFileURL->GetFile(getter_AddRefs(codebaseFile))) || - !targetFile || !codebaseFile || - NS_FAILED(targetFile->Normalize()) || -#ifndef MOZ_WIDGET_ANDROID - NS_FAILED(codebaseFile->Normalize()) || -#endif - NS_FAILED(targetFile->IsDirectory(&targetIsDir)) || - targetIsDir) { - if (aReport) { - nsScriptSecurityManager::ReportError( - nullptr, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI); - } - - return NS_ERROR_DOM_BAD_URI; - } - - // - // If the file to be loaded is in a subdirectory of the codebase - // (or same-dir if codebase is not a directory) then it will - // inherit its codebase principal and be scriptable by that codebase. - // - bool codebaseIsDir; - bool contained = false; - nsresult rv = codebaseFile->IsDirectory(&codebaseIsDir); - if (NS_SUCCEEDED(rv) && codebaseIsDir) { - rv = codebaseFile->Contains(targetFile, true, &contained); - } - else { - nsCOMPtr codebaseParent; - rv = codebaseFile->GetParent(getter_AddRefs(codebaseParent)); - if (NS_SUCCEEDED(rv) && codebaseParent) { - rv = codebaseParent->Contains(targetFile, true, &contained); - } - } - - if (NS_SUCCEEDED(rv) && contained) { - return NS_OK; - } - } - - if (aReport) { - nsScriptSecurityManager::ReportError( - nullptr, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI); - } - - return NS_ERROR_DOM_BAD_URI; + if (nsScriptSecurityManager::SecurityCompareURIs(mCodebase, aURI)) { + return NS_OK; } - return NS_OK; + // If strict file origin policy is in effect, local files will always fail + // SecurityCompareURIs unless they are identical. Explicitly check file origin + // policy, in that case. + if (nsScriptSecurityManager::GetStrictFileOriginPolicy() && + NS_URIIsLocalFile(aURI) && + NS_RelaxStrictFileOriginPolicy(aURI, mCodebase)) { + return NS_OK; + } + + if (aReport) { + nsScriptSecurityManager::ReportError(nullptr, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI); + } + return NS_ERROR_DOM_BAD_URI; } void @@ -562,7 +485,7 @@ NS_IMETHODIMP nsPrincipal::GetBaseDomain(nsACString& aBaseDomain) { // For a file URI, we return the file path. - if (URIIsLocalFile(mCodebase)) { + if (NS_URIIsLocalFile(mCodebase)) { nsCOMPtr url = do_QueryInterface(mCodebase); if (url) { diff --git a/netwerk/base/public/nsNetUtil.h b/netwerk/base/public/nsNetUtil.h index 60a84d478f8d..1949c5e42441 100644 --- a/netwerk/base/public/nsNetUtil.h +++ b/netwerk/base/public/nsNetUtil.h @@ -1838,7 +1838,8 @@ NS_SecurityCompareURIs(nsIURI* aSourceURI, return false; } - // special handling for file: URIs + // For file scheme, reject unless the files are identical. See + // NS_RelaxStrictFileOriginPolicy for enforcing file same-origin checking if (targetScheme.EqualsLiteral("file")) { // in traditional unsafe behavior all files are the same origin @@ -1907,6 +1908,93 @@ NS_SecurityCompareURIs(nsIURI* aSourceURI, return NS_GetRealPort(targetBaseURI) == NS_GetRealPort(sourceBaseURI); } +inline bool +NS_URIIsLocalFile(nsIURI *aURI) +{ + nsCOMPtr util = do_GetNetUtil(); + + bool isFile; + return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI, + nsIProtocolHandler::URI_IS_LOCAL_FILE, + &isFile)) && + isFile; +} + +// When strict file origin policy is enabled, SecurityCompareURIs will fail for +// file URIs that do not point to the same local file. This call provides an +// alternate file-specific origin check that allows target files that are +// contained in the same directory as the source. +// +// https://developer.mozilla.org/en-US/docs/Same-origin_policy_for_file:_URIs +inline bool +NS_RelaxStrictFileOriginPolicy(nsIURI *aTargetURI, + nsIURI *aSourceURI, + bool aAllowDirectoryTarget = false) +{ + if (!NS_URIIsLocalFile(aTargetURI)) { + // This is probably not what the caller intended + NS_NOTREACHED("NS_RelaxStrictFileOriginPolicy called with non-file URI"); + return false; + } + + if (!NS_URIIsLocalFile(aSourceURI)) { + // If the source is not also a file: uri then forget it + // (don't want resource: principals in a file: doc) + // + // note: we're not de-nesting jar: uris here, we want to + // keep archive content bottled up in its own little island + return false; + } + + // + // pull out the internal files + // + nsCOMPtr targetFileURL(do_QueryInterface(aTargetURI)); + nsCOMPtr sourceFileURL(do_QueryInterface(aSourceURI)); + nsCOMPtr targetFile; + nsCOMPtr sourceFile; + bool targetIsDir; + + // Make sure targetFile is not a directory (bug 209234) + // and that it exists w/out unescaping (bug 395343) + if (!sourceFileURL || !targetFileURL || + NS_FAILED(targetFileURL->GetFile(getter_AddRefs(targetFile))) || + NS_FAILED(sourceFileURL->GetFile(getter_AddRefs(sourceFile))) || + !targetFile || !sourceFile || + NS_FAILED(targetFile->Normalize()) || +#ifndef MOZ_WIDGET_ANDROID + NS_FAILED(sourceFile->Normalize()) || +#endif + (!aAllowDirectoryTarget && + (NS_FAILED(targetFile->IsDirectory(&targetIsDir)) || targetIsDir))) { + return false; + } + + // + // If the file to be loaded is in a subdirectory of the source + // (or same-dir if source is not a directory) then it will + // inherit its source principal and be scriptable by that source. + // + bool sourceIsDir; + bool contained = false; + nsresult rv = sourceFile->IsDirectory(&sourceIsDir); + if (NS_SUCCEEDED(rv) && sourceIsDir) { + rv = sourceFile->Contains(targetFile, true, &contained); + } else { + nsCOMPtr sourceParent; + rv = sourceFile->GetParent(getter_AddRefs(sourceParent)); + if (NS_SUCCEEDED(rv) && sourceParent) { + rv = sourceParent->Contains(targetFile, true, &contained); + } + } + + if (NS_SUCCEEDED(rv) && contained) { + return true; + } + + return false; +} + inline bool NS_IsInternalSameURIRedirect(nsIChannel *aOldChannel, nsIChannel *aNewChannel,