From 03e566ce2839feef82dc41eb8d073652dbc4f67f Mon Sep 17 00:00:00 2001 From: "dveditz%cruzio.com" Date: Thu, 15 Nov 2007 00:17:32 +0000 Subject: [PATCH] bug 369814 don't open jar: content unless served from a safe MIME type. patch by dcamp, r=bzbarsky, sr=dveditz, a=dveditz git-svn-id: svn://10.0.0.236/branches/MOZILLA_1_8_BRANCH@239403 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/docshell/base/Makefile.in | 1 + mozilla/docshell/base/nsDocShell.cpp | 58 ++++++++++++++++++++ mozilla/docshell/base/nsDocShell.h | 2 + mozilla/docshell/base/nsIDocShell.idl | 11 ++++ mozilla/modules/libjar/nsIJARChannel.idl | 12 +++++ mozilla/modules/libjar/nsJARChannel.cpp | 67 ++++++++++++++++++++++-- mozilla/modules/libjar/nsJARChannel.h | 5 +- mozilla/modules/libpref/src/init/all.js | 5 ++ mozilla/netwerk/base/public/nsNetError.h | 7 +++ 9 files changed, 164 insertions(+), 4 deletions(-) diff --git a/mozilla/docshell/base/Makefile.in b/mozilla/docshell/base/Makefile.in index e678919ff22..c1c99e24bc9 100644 --- a/mozilla/docshell/base/Makefile.in +++ b/mozilla/docshell/base/Makefile.in @@ -84,6 +84,7 @@ REQUIRES = xpcom \ windowwatcher \ imglib2 \ mimetype \ + jar \ $(NULL) SDK_XPIDLSRCS = \ diff --git a/mozilla/docshell/base/nsDocShell.cpp b/mozilla/docshell/base/nsDocShell.cpp index d0160f56807..cdb9787b812 100644 --- a/mozilla/docshell/base/nsDocShell.cpp +++ b/mozilla/docshell/base/nsDocShell.cpp @@ -152,6 +152,8 @@ #include "nsITextToSubURI.h" +#include "nsIJARChannel.h" + #include "prlog.h" #include "prmem.h" @@ -382,6 +384,7 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell) NS_INTERFACE_MAP_ENTRY(nsIDocShell) NS_INTERFACE_MAP_ENTRY(nsIDocShell_MOZILLA_1_8_BRANCH) NS_INTERFACE_MAP_ENTRY(nsIDocShell_MOZILLA_1_8_BRANCH2) + NS_INTERFACE_MAP_ENTRY(nsIDocShell_MOZILLA_1_8_BRANCH3) NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem) NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeNode) NS_INTERFACE_MAP_ENTRY(nsIDocShellHistory) @@ -1381,12 +1384,37 @@ nsDocShell::SetDocumentCharsetInfo(nsIDocumentCharsetInfo * return NS_OK; } +NS_IMETHODIMP +nsDocShell::GetChannelIsUnsafe(PRBool *aUnsafe) +{ + *aUnsafe = PR_FALSE; + + nsCOMPtr channel; + GetCurrentDocumentChannel(getter_AddRefs(channel)); + if (!channel) { + return NS_OK; + } + + nsCOMPtr jarChannel = do_QueryInterface(channel); + if (!jarChannel) { + return NS_OK; + } + + return jarChannel->GetIsUnsafe(aUnsafe); +} + NS_IMETHODIMP nsDocShell::GetAllowPlugins(PRBool * aAllowPlugins) { NS_ENSURE_ARG_POINTER(aAllowPlugins); *aAllowPlugins = mAllowPlugins; + if (!mAllowPlugins) { + return NS_OK; + } + + PRBool unsafe; + *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe; return NS_OK; } @@ -1404,6 +1432,12 @@ nsDocShell::GetAllowJavascript(PRBool * aAllowJavascript) NS_ENSURE_ARG_POINTER(aAllowJavascript); *aAllowJavascript = mAllowJavascript; + if (!mAllowJavascript) { + return NS_OK; + } + + PRBool unsafe; + *aAllowJavascript = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe; return NS_OK; } @@ -1419,6 +1453,12 @@ NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(PRBool * aReturn) NS_ENSURE_ARG_POINTER(aReturn); *aReturn = mAllowMetaRedirects; + if (!mAllowMetaRedirects) { + return NS_OK; + } + + PRBool unsafe; + *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe; return NS_OK; } @@ -6385,6 +6425,24 @@ nsDocShell::InternalLoad(nsIURI * aURI, (NS_SUCCEEDED(aURI->SchemeIs("data", &isScheme)) && isScheme); if (inherits) { + // Don't allow loads that would inherit our security context + // if this document came from an unsafe channel. + nsCOMPtr treeItem = this; + do { + nsCOMPtr itemDocShell = + do_QueryInterface(treeItem); + PRBool isUnsafe; + if (itemDocShell && + NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) && + isUnsafe) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + nsCOMPtr parent; + treeItem->GetSameTypeParent(getter_AddRefs(parent)); + parent.swap(treeItem); + } while (treeItem); + owner = GetInheritedPrincipal(PR_TRUE); } } diff --git a/mozilla/docshell/base/nsDocShell.h b/mozilla/docshell/base/nsDocShell.h index 1b03a9662bf..c939bd6be8b 100644 --- a/mozilla/docshell/base/nsDocShell.h +++ b/mozilla/docshell/base/nsDocShell.h @@ -143,6 +143,7 @@ class nsDocShell : public nsDocLoader, public nsIDocShell, public nsIDocShell_MOZILLA_1_8_BRANCH, public nsIDocShell_MOZILLA_1_8_BRANCH2, + public nsIDocShell_MOZILLA_1_8_BRANCH3, public nsIDocShellTreeItem, public nsIDocShellTreeNode, public nsIDocShellHistory, @@ -172,6 +173,7 @@ public: NS_DECL_NSIDOCSHELL NS_DECL_NSIDOCSHELL_MOZILLA_1_8_BRANCH NS_DECL_NSIDOCSHELL_MOZILLA_1_8_BRANCH2 + NS_DECL_NSIDOCSHELL_MOZILLA_1_8_BRANCH3 NS_DECL_NSIDOCSHELLTREEITEM NS_DECL_NSIDOCSHELLTREENODE NS_DECL_NSIDOCSHELLHISTORY diff --git a/mozilla/docshell/base/nsIDocShell.idl b/mozilla/docshell/base/nsIDocShell.idl index ee6a78fb360..6c637d2ff0a 100644 --- a/mozilla/docshell/base/nsIDocShell.idl +++ b/mozilla/docshell/base/nsIDocShell.idl @@ -449,3 +449,14 @@ interface nsIDocShell_MOZILLA_1_8_BRANCH2 : nsISupports */ readonly attribute boolean isInUnload; }; + +[scriptable, uuid(833fdff8-e0ca-4987-bf13-e10e631e504d)] +interface nsIDocShell_MOZILLA_1_8_BRANCH3 : nsISupports +{ + /** + * Find out if the currently loaded document came from a suspicious channel + * (such as a JAR channel where the server-returned content type isn't a + * known JAR type). + */ + readonly attribute boolean channelIsUnsafe; +}; diff --git a/mozilla/modules/libjar/nsIJARChannel.idl b/mozilla/modules/libjar/nsIJARChannel.idl index a234dabc493..153f20f8ad2 100644 --- a/mozilla/modules/libjar/nsIJARChannel.idl +++ b/mozilla/modules/libjar/nsIJARChannel.idl @@ -41,3 +41,15 @@ interface nsIJARChannel : nsIChannel { }; + +[scriptable, uuid(2ee6d5ab-248b-47d0-aac3-3c29417e2164)] +interface nsIJARChannel_MOZILLA_1_8_BRANCH : nsISupports +{ + /** + * Returns TRUE if the JAR file is not safe (if the content type reported + * by the server for a remote JAR is not of an expected type). Scripting, + * redirects, and plugins should be disabled when loading from this + * channel. + */ + readonly attribute boolean isUnsafe; +}; diff --git a/mozilla/modules/libjar/nsJARChannel.cpp b/mozilla/modules/libjar/nsJARChannel.cpp index 079a7020f0d..ba91320c131 100644 --- a/mozilla/modules/libjar/nsJARChannel.cpp +++ b/mozilla/modules/libjar/nsJARChannel.cpp @@ -41,6 +41,8 @@ #include "nsMimeTypes.h" #include "nsNetUtil.h" #include "nsInt64.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" #include "nsIScriptSecurityManager.h" #include "nsIPrincipal.h" @@ -193,6 +195,7 @@ nsJARChannel::nsJARChannel() , mLoadFlags(LOAD_NORMAL) , mStatus(NS_OK) , mIsPending(PR_FALSE) + , mIsUnsafe(PR_TRUE) , mJarInput(nsnull) { #if defined(PR_LOGGING) @@ -214,13 +217,14 @@ nsJARChannel::~nsJARChannel() NS_RELEASE(handler); // NULL parameter } -NS_IMPL_ISUPPORTS6(nsJARChannel, +NS_IMPL_ISUPPORTS7(nsJARChannel, nsIRequest, nsIChannel, nsIStreamListener, nsIRequestObserver, nsIDownloadObserver, - nsIJARChannel) + nsIJARChannel, + nsIJARChannel_MOZILLA_1_8_BRANCH) nsresult nsJARChannel::Init(nsIURI *uri) @@ -288,6 +292,8 @@ nsJARChannel::EnsureJarInput(PRBool blocking) } if (mJarFile) { + mIsUnsafe = PR_FALSE; + // NOTE: we do not need to deal with mSecurityInfo here, // because we're loading from a local file rv = CreateJarInput(gJarHandler->JarCache()); @@ -302,7 +308,7 @@ nsJARChannel::EnsureJarInput(PRBool blocking) if (NS_SUCCEEDED(rv)) rv = NS_OpenURI(mDownloader, nsnull, mJarBaseURI, nsnull, mLoadGroup, mCallbacks, - mLoadFlags & ~LOAD_DOCUMENT_URI); + mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS)); } return rv; @@ -604,6 +610,9 @@ nsJARChannel::Open(nsIInputStream **stream) NS_ENSURE_TRUE(!mJarInput, NS_ERROR_IN_PROGRESS); NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + mJarFile = nsnull; + mIsUnsafe = PR_TRUE; + nsresult rv = EnsureJarInput(PR_TRUE); if (NS_FAILED(rv)) return rv; @@ -625,6 +634,9 @@ nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx) NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + mJarFile = nsnull; + mIsUnsafe = PR_TRUE; + // Initialize mProgressSink NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink); @@ -649,6 +661,16 @@ nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx) return NS_OK; } +//----------------------------------------------------------------------------- +// nsIJARChannel +//----------------------------------------------------------------------------- +NS_IMETHODIMP +nsJARChannel::GetIsUnsafe(PRBool *isUnsafe) +{ + *isUnsafe = mIsUnsafe; + return NS_OK; +} + //----------------------------------------------------------------------------- // nsIDownloadObserver //----------------------------------------------------------------------------- @@ -690,6 +712,44 @@ nsJARChannel::OnDownloadComplete(nsIDownloader *downloader, } status = rv; } + + nsCOMPtr httpChannel(do_QueryInterface(channel)); + if (httpChannel) { + // We only want to run scripts if the server really intended to + // send us a JAR file. Check the server-supplied content type for + // a JAR type. + nsCAutoString header; + httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"), + header); + + nsCAutoString contentType; + nsCAutoString charset; + NS_ParseContentType(header, contentType, charset); + + mIsUnsafe = !contentType.EqualsLiteral("application/java-archive") && + !contentType.EqualsLiteral("application/x-jar"); + } else { + nsCOMPtr innerJARChannel(do_QueryInterface(channel)); + if (innerJARChannel) { + PRBool unsafe; + innerJARChannel->GetIsUnsafe(&unsafe); + mIsUnsafe = unsafe; + } + } + } + + if (mIsUnsafe) { + PRBool allowUnpack = PR_FALSE; + + nsCOMPtr prefs = + do_GetService(NS_PREFSERVICE_CONTRACTID); + if (prefs) { + prefs->GetBoolPref("network.jar.open-unsafe-types", &allowUnpack); + } + + if (!allowUnpack) { + status = NS_ERROR_UNSAFE_CONTENT_TYPE; + } } if (NS_SUCCEEDED(status)) { @@ -706,6 +766,7 @@ nsJARChannel::OnDownloadComplete(nsIDownloader *downloader, } if (NS_FAILED(status)) { + mStatus = status; OnStartRequest(nsnull, nsnull); OnStopRequest(nsnull, nsnull, status); } diff --git a/mozilla/modules/libjar/nsJARChannel.h b/mozilla/modules/libjar/nsJARChannel.h index a015c5d3d54..6f067d604b8 100644 --- a/mozilla/modules/libjar/nsJARChannel.h +++ b/mozilla/modules/libjar/nsJARChannel.h @@ -58,6 +58,7 @@ class nsJARInputThunk; //----------------------------------------------------------------------------- class nsJARChannel : public nsIJARChannel + , public nsIJARChannel_MOZILLA_1_8_BRANCH , public nsIDownloadObserver , public nsIStreamListener { @@ -66,6 +67,7 @@ public: NS_DECL_NSIREQUEST NS_DECL_NSICHANNEL NS_DECL_NSIJARCHANNEL + NS_DECL_NSIJARCHANNEL_MOZILLA_1_8_BRANCH NS_DECL_NSIDOWNLOADOBSERVER NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER @@ -97,7 +99,8 @@ private: PRInt32 mContentLength; PRUint32 mLoadFlags; nsresult mStatus; - PRBool mIsPending; + PRPackedBool mIsPending; + PRPackedBool mIsUnsafe; nsJARInputThunk *mJarInput; nsCOMPtr mDownloader; diff --git a/mozilla/modules/libpref/src/init/all.js b/mozilla/modules/libpref/src/init/all.js index fa208b5ffb4..3bc0ce26cc4 100644 --- a/mozilla/modules/libpref/src/init/all.js +++ b/mozilla/modules/libpref/src/init/all.js @@ -583,6 +583,11 @@ pref("network.http.pipelining.maxrequests" , 4); // +// If false, remote JAR files that are served with a content type other than +// application/java-archive or application/x-jar will not be opened +// by the jar channel. +pref("network.jar.open-unsafe-types", false); + // This preference controls whether or not internationalized domain names (IDN) // are handled. IDN requires a nsIIDNService implementation. pref("network.enableIDN", true); diff --git a/mozilla/netwerk/base/public/nsNetError.h b/mozilla/netwerk/base/public/nsNetError.h index 1987225442a..37c76f26e24 100644 --- a/mozilla/netwerk/base/public/nsNetError.h +++ b/mozilla/netwerk/base/public/nsNetError.h @@ -219,6 +219,13 @@ #define NS_ERROR_REDIRECT_LOOP \ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 31) +/** + * The request failed because the content type returned by the server was + * not a type expected by the channel (for nested channels such as the JAR + * channel). + */ +#define NS_ERROR_UNSAFE_CONTENT_TYPE \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 74) /****************************************************************************** * FTP specific error codes: