From 848e5e4245954b148ce2f16d2ffb0046d05ecf33 Mon Sep 17 00:00:00 2001 From: "jst%mozilla.org" Date: Sat, 22 Mar 2008 16:50:49 +0000 Subject: [PATCH] Landing followup fix for bug 402983 and re-enabling the new stricter file URI security policies. r+sr=bzbarsky@mit.edu git-svn-id: svn://10.0.0.236/trunk@248431 18797224-902f-48f8-a5cc-f745e15eee43 --- .../caps/include/nsScriptSecurityManager.h | 7 ++ mozilla/caps/src/nsPrincipal.cpp | 84 +++++++++++++++++++ mozilla/content/base/public/nsContentUtils.h | 5 ++ mozilla/content/base/src/nsContentUtils.cpp | 13 +++ .../base/src/nsObjectLoadingContent.cpp | 5 +- mozilla/docshell/base/nsDocShell.cpp | 69 +-------------- mozilla/docshell/base/nsDocShell.h | 1 - mozilla/layout/style/nsCSSLoader.cpp | 8 +- mozilla/modules/libpref/src/init/all.js | 2 +- 9 files changed, 123 insertions(+), 71 deletions(-) diff --git a/mozilla/caps/include/nsScriptSecurityManager.h b/mozilla/caps/include/nsScriptSecurityManager.h index fa501e58e70..c1132bc83d7 100644 --- a/mozilla/caps/include/nsScriptSecurityManager.h +++ b/mozilla/caps/include/nsScriptSecurityManager.h @@ -410,6 +410,13 @@ public: CheckSameOriginPrincipal(nsIPrincipal* aSubject, nsIPrincipal* aObject, PRBool aIsCheckConnect); + + static PRBool + GetStrictFileOriginPolicy() + { + return sStrictFileOriginPolicy; + } + private: // GetScriptSecurityManager is the only call that can make one diff --git a/mozilla/caps/src/nsPrincipal.cpp b/mozilla/caps/src/nsPrincipal.cpp index 043becdd3f3..330821245a6 100755 --- a/mozilla/caps/src/nsPrincipal.cpp +++ b/mozilla/caps/src/nsPrincipal.cpp @@ -45,6 +45,8 @@ #include "plstr.h" #include "nsCRT.h" #include "nsIURI.h" +#include "nsIFileURL.h" +#include "nsIProtocolHandler.h" #include "nsNetUtil.h" #include "nsJSPrincipals.h" #include "nsVoidArray.h" @@ -304,10 +306,92 @@ nsPrincipal::Subsumes(nsIPrincipal *aOther, PRBool *aResult) return Equals(aOther, aResult); } +static PRBool +URIIsLocalFile(nsIURI *aURI) +{ + PRBool isFile; + nsCOMPtr util = do_GetIOService(); + + return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI, + nsIProtocolHandler::URI_IS_LOCAL_FILE, + &isFile)) && + isFile; +} + NS_IMETHODIMP nsPrincipal::CheckMayLoad(nsIURI* aURI, PRBool aReport) { 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( + nsnull, 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; + PRBool 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()) || + NS_FAILED(codebaseFile->Normalize()) || + NS_FAILED(targetFile->IsDirectory(&targetIsDir)) || + targetIsDir) { + if (aReport) { + nsScriptSecurityManager::ReportError( + nsnull, 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. + // + PRBool codebaseIsDir; + PRBool contained = PR_FALSE; + nsresult rv = codebaseFile->IsDirectory(&codebaseIsDir); + if (NS_SUCCEEDED(rv) && codebaseIsDir) { + rv = codebaseFile->Contains(targetFile, PR_TRUE, &contained); + } + else { + nsCOMPtr codebaseParent; + rv = codebaseFile->GetParent(getter_AddRefs(codebaseParent)); + if (NS_SUCCEEDED(rv) && codebaseParent) { + rv = codebaseParent->Contains(targetFile, PR_TRUE, &contained); + } + } + + if (NS_SUCCEEDED(rv) && contained) { + return NS_OK; + } + } + if (aReport) { nsScriptSecurityManager::ReportError( nsnull, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI); diff --git a/mozilla/content/base/public/nsContentUtils.h b/mozilla/content/base/public/nsContentUtils.h index 24fb65c29c8..1bc58149905 100644 --- a/mozilla/content/base/public/nsContentUtils.h +++ b/mozilla/content/base/public/nsContentUtils.h @@ -1163,6 +1163,11 @@ public: */ static void HidePopupsInDocument(nsIDocument* aDocument); + /** + * Return true if aURI is a local file URI (i.e. file://). + */ + static PRBool URIIsLocalFile(nsIURI *aURI); + /** * Get the application manifest URI for this context. The manifest URI * is specified in the manifest= attribute of the root element of the diff --git a/mozilla/content/base/src/nsContentUtils.cpp b/mozilla/content/base/src/nsContentUtils.cpp index 16691969b2f..25e2ad85c23 100644 --- a/mozilla/content/base/src/nsContentUtils.cpp +++ b/mozilla/content/base/src/nsContentUtils.cpp @@ -4065,6 +4065,19 @@ nsContentUtils::HidePopupsInDocument(nsIDocument* aDocument) #endif } +/* static */ +PRBool +nsContentUtils::URIIsLocalFile(nsIURI *aURI) +{ + PRBool isFile; + nsCOMPtr util = do_QueryInterface(sIOService); + + return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI, + nsIProtocolHandler::URI_IS_LOCAL_FILE, + &isFile)) && + isFile; +} + /* static */ void nsAutoGCRoot::Shutdown() diff --git a/mozilla/content/base/src/nsObjectLoadingContent.cpp b/mozilla/content/base/src/nsObjectLoadingContent.cpp index 642bc5853d0..1ff8294886d 100644 --- a/mozilla/content/base/src/nsObjectLoadingContent.cpp +++ b/mozilla/content/base/src/nsObjectLoadingContent.cpp @@ -1245,7 +1245,10 @@ nsObjectLoadingContent::LoadObject(nsIURI* aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &inheritPrincipal); NS_ENSURE_SUCCESS(rv, rv); - if (inheritPrincipal || IsAboutBlank(aURI)) { + if (inheritPrincipal || IsAboutBlank(aURI) || + (nsContentUtils::URIIsLocalFile(aURI) && + NS_SUCCEEDED(thisContent->NodePrincipal()->CheckMayLoad(aURI, + PR_FALSE)))) { chan->SetOwner(thisContent->NodePrincipal()); } diff --git a/mozilla/docshell/base/nsDocShell.cpp b/mozilla/docshell/base/nsDocShell.cpp index 1692d5a1676..d76c7de4b53 100644 --- a/mozilla/docshell/base/nsDocShell.cpp +++ b/mozilla/docshell/base/nsDocShell.cpp @@ -290,7 +290,6 @@ nsDocShell::nsDocShell(): mObserveErrorPages(PR_TRUE), mAllowAuth(PR_TRUE), mAllowKeywordFixup(PR_FALSE), - mStrictFilePolicy(PR_TRUE), mFiredUnloadEvent(PR_FALSE), mEODForCurrentDocument(PR_FALSE), mURIResultedInDocument(PR_FALSE), @@ -3593,10 +3592,6 @@ nsDocShell::Create() } } - rv = mPrefs->GetBoolPref("security.fileuri.strict_origin_policy", &tmpbool); - if (NS_SUCCEEDED(rv)) - mStrictFilePolicy = tmpbool; - // Should we use XUL error pages instead of alerts if possible? rv = mPrefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool); if (NS_SUCCEEDED(rv)) @@ -7357,66 +7352,10 @@ nsDocShell::DoURILoad(nsIURI * aURI, // If we don't set the owner explicitly then each file: gets an owner // based on its own codebase later. // - if (mStrictFilePolicy && URIIsLocalFile(aURI)) { - nsCOMPtr fileURL(do_QueryInterface(aURI)); - nsCOMPtr ownerPrincipal(do_QueryInterface(aOwner)); - nsCOMPtr ownerURI; - if (ownerPrincipal) { - ownerPrincipal->GetURI(getter_AddRefs(ownerURI)); - } - - if (!URIIsLocalFile(ownerURI)) { - // If the owner 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 - ownerURI = nsnull; - } - - // - // pull out the internal files - // - nsCOMPtr ownerFileURL(do_QueryInterface(ownerURI)); - nsCOMPtr targetFile; - nsCOMPtr ownerFile; - if (ownerFileURL && - NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(targetFile))) && - NS_SUCCEEDED(ownerFileURL->GetFile(getter_AddRefs(ownerFile)))) { - // - // Make sure targetFile is not a directory (bug 209234) - // and that it exists w/out unescaping (bug 395343) - // - PRBool targetIsDir; - if (targetFile && ownerFile && - NS_SUCCEEDED(targetFile->Normalize()) && - NS_SUCCEEDED(ownerFile->Normalize()) && - NS_SUCCEEDED(targetFile->IsDirectory(&targetIsDir)) && - !targetIsDir) { - // - // If the file to be loaded is in a subdirectory of the owner - // (or same-dir if owner is not a directory) then it will - // inherit its owner principal and be scriptable by that owner. - // - PRBool ownerIsDir; - PRBool contained = PR_FALSE; - rv = ownerFile->IsDirectory(&ownerIsDir); - if (NS_SUCCEEDED(rv) && ownerIsDir) { - rv = ownerFile->Contains(targetFile, PR_TRUE, &contained); - } - else { - nsCOMPtr ownerParent; - rv = ownerFile->GetParent(getter_AddRefs(ownerParent)); - if (NS_SUCCEEDED(rv) && ownerParent) { - rv = ownerParent->Contains(targetFile, PR_TRUE, &contained); - } - } - - if (NS_SUCCEEDED(rv) && contained) { - channel->SetOwner(aOwner); - } - } - } + nsCOMPtr ownerPrincipal(do_QueryInterface(aOwner)); + if (URIIsLocalFile(aURI) && ownerPrincipal && + NS_SUCCEEDED(ownerPrincipal->CheckMayLoad(aURI, PR_FALSE))) { + channel->SetOwner(aOwner); } nsCOMPtr scriptChannel = do_QueryInterface(channel); diff --git a/mozilla/docshell/base/nsDocShell.h b/mozilla/docshell/base/nsDocShell.h index 0173d808663..7a419b76224 100644 --- a/mozilla/docshell/base/nsDocShell.h +++ b/mozilla/docshell/base/nsDocShell.h @@ -551,7 +551,6 @@ protected: PRPackedBool mObserveErrorPages; PRPackedBool mAllowAuth; PRPackedBool mAllowKeywordFixup; - PRPackedBool mStrictFilePolicy; // This boolean is set to true right before we fire pagehide and generally // unset when we embed a new content viewer. While it's true no navigation diff --git a/mozilla/layout/style/nsCSSLoader.cpp b/mozilla/layout/style/nsCSSLoader.cpp index dc9467cfdb9..b92f5bdf419 100644 --- a/mozilla/layout/style/nsCSSLoader.cpp +++ b/mozilla/layout/style/nsCSSLoader.cpp @@ -1436,7 +1436,10 @@ CSSLoaderImpl::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState) rv = NS_URIChainHasFlags(aLoadData->mURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &inherit); - if (NS_SUCCEEDED(rv) && inherit) { + if ((NS_SUCCEEDED(rv) && inherit) || + (nsContentUtils::URIIsLocalFile(aLoadData->mURI) && + NS_SUCCEEDED(aLoadData->mLoaderPrincipal-> + CheckMayLoad(aLoadData->mURI, PR_FALSE)))) { channel->SetOwner(aLoadData->mLoaderPrincipal); } } @@ -1445,8 +1448,7 @@ CSSLoaderImpl::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState) // model is: Necko owns the stream loader, which owns the load data, // which owns us nsCOMPtr streamLoader; - rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), - aLoadData); + rv = NS_NewUnicharStreamLoader(getter_AddRefs(streamLoader), aLoadData); if (NS_SUCCEEDED(rv)) rv = channel->AsyncOpen(streamLoader, nsnull); diff --git a/mozilla/modules/libpref/src/init/all.js b/mozilla/modules/libpref/src/init/all.js index 6878649a446..71c80cc5372 100644 --- a/mozilla/modules/libpref/src/init/all.js +++ b/mozilla/modules/libpref/src/init/all.js @@ -501,7 +501,7 @@ pref("advanced.mailftp", false); pref("image.animation_mode", "normal"); // Same-origin policy for file URIs, "false" is traditional -pref("security.fileuri.strict_origin_policy", false); +pref("security.fileuri.strict_origin_policy", true); // If there is ever a security firedrill that requires // us to block certian ports global, this is the pref