Bug 611910 - r=josh a=blocking2.0BetaN+
☠☠ backed out by 0f7432f2cb5d ☠ ☠
authorSteven Michaud <smichaud@pobox.com>
Fri, 19 Nov 2010 12:14:15 -0600
changeset 57916 16c2e141d4182698d46ea33a020192a4153c5f8a
parent 57915 cc5a70c9e1e8388069322f2748b30356914ad435
child 57917 61fbdf66e57974ce792bea262728c0fdeae219fa
child 57920 0f7432f2cb5d04c74f9745fef9f1b6a8cefd1560
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersjosh, blocking2
bugs611910
milestone2.0b8pre
Bug 611910 - r=josh a=blocking2.0BetaN+
modules/plugin/base/src/nsNPAPIPlugin.cpp
modules/plugin/base/src/nsNPAPIPluginInstance.h
modules/plugin/base/src/nsPluginHost.h
netwerk/base/public/nsNetUtil.h
--- a/modules/plugin/base/src/nsNPAPIPlugin.cpp
+++ b/modules/plugin/base/src/nsNPAPIPlugin.cpp
@@ -101,16 +101,18 @@
 #define INCL_DOSERRORS
 #include <os2.h>
 #endif
 
 #include "nsJSNPRuntime.h"
 #include "nsIHttpAuthManager.h"
 #include "nsICookieService.h"
 
+#include "nsNetUtil.h"
+
 #include "mozilla/PluginLibrary.h"
 using mozilla::PluginLibrary;
 
 #include "mozilla/PluginPRLibrary.h"
 using mozilla::PluginPRLibrary;
 
 #ifdef MOZ_IPC
 #include "mozilla/plugins/PluginModuleParent.h"
@@ -1701,17 +1703,101 @@ bool NP_CALLBACK
 
   NPPExceptionAutoHolder nppExceptionHolder;
   NPPAutoPusher nppPusher(npp);
 
   NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
                  ("NPN_GetProperty(npp %p, npobj %p, property %p) called\n",
                   npp, npobj, property));
 
-  return npobj->_class->getProperty(npobj, property, result);
+  if (!npobj->_class->getProperty(npobj, property, result))
+    return false;
+
+  // If a Java plugin tries to get the document.URL or document.documentURI
+  // property from us, don't pass back a value that Java won't be able to
+  // understand -- one that will make the URL(String) constructor throw a
+  // MalformedURL exception.  Passing such a value causes Java Plugin2 to
+  // crash (to throw a RuntimeException in Plugin2Manager.getDocumentBase()).
+  // Also don't pass back a value that Java is likely to mishandle.
+
+  nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*) npp->ndata;
+  if (!inst)
+    return false;
+  nsNPAPIPlugin* plugin = inst->GetPlugin();
+  if (!plugin)
+    return false;
+  nsPluginTag* pluginTag = nsPluginHost::GetInst()->TagForPlugin(plugin);
+  if (!pluginTag->mIsJavaPlugin)
+    return true;
+
+  if (!NPVARIANT_IS_STRING(*result))
+    return true;
+
+  NPUTF8* propertyName = _utf8fromidentifier(property);
+  if (!propertyName)
+    return true;
+  bool notURL =
+    (PL_strcasecmp(propertyName, "URL") &&
+     PL_strcasecmp(propertyName, "documentURI"));
+  _memfree(propertyName);
+  if (notURL)
+    return true;
+
+  NPObject* window_obj = _getwindowobject(npp);
+  if (!window_obj)
+    return true;
+
+  NPVariant doc_v;
+  NPObject* document_obj = nsnull;
+  NPIdentifier doc_id = _getstringidentifier("document");
+  bool ok = npobj->_class->getProperty(window_obj, doc_id, &doc_v);
+  _releaseobject(window_obj);
+  if (ok) {
+    if (NPVARIANT_IS_OBJECT(doc_v)) {
+      document_obj = NPVARIANT_TO_OBJECT(doc_v);
+    } else {
+      _releasevariantvalue(&doc_v);
+      return true;
+    }
+  } else {
+    return true;
+  }
+  _releaseobject(document_obj);
+  if (document_obj != npobj)
+    return true;
+
+  NPString urlnp = NPVARIANT_TO_STRING(*result);
+  nsXPIDLCString url;
+  url.Assign(urlnp.UTF8Characters, urlnp.UTF8Length);
+
+  PRBool javaCompatible = PR_FALSE;
+  if (NS_FAILED(NS_CheckIsJavaCompatibleURLString(url, &javaCompatible)))
+    javaCompatible = PR_FALSE;
+  if (javaCompatible)
+    return true;
+
+  // If Java won't be able to interpret the original value of document.URL or
+  // document.documentURI, or is likely to mishandle it, pass back something
+  // that Java will understand but won't be able to use to access the network,
+  // and for which same-origin checks will always fail.
+
+  if (inst->mFakeURL.IsVoid()) {
+    // Abort (do an error return) if NS_MakeRandomInvalidURLString() fails.
+    if (NS_FAILED(NS_MakeRandomInvalidURLString(inst->mFakeURL))) {
+      _releasevariantvalue(result);
+      return false;
+    }
+  }
+
+  _releasevariantvalue(result);
+  char* fakeurl = (char *) _memalloc(inst->mFakeURL.Length() + 1);
+  strcpy(fakeurl, inst->mFakeURL);
+  STRINGZ_TO_NPVARIANT(fakeurl, *result);
+
+  return true;
 }
 
 bool NP_CALLBACK
 _setproperty(NPP npp, NPObject* npobj, NPIdentifier property,
              const NPVariant *value)
 {
   if (!NS_IsMainThread()) {
     NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_setproperty called from the wrong thread\n"));
--- a/modules/plugin/base/src/nsNPAPIPluginInstance.h
+++ b/modules/plugin/base/src/nsNPAPIPluginInstance.h
@@ -172,16 +172,18 @@ protected:
   PRPackedBool mTransparent;
   PRPackedBool mCached;
   PRPackedBool mWantsAllNetworkStreams;
 
 public:
   // True while creating the plugin, or calling NPP_SetWindow() on it.
   PRPackedBool mInPluginInitCall;
 
+  nsXPIDLCString mFakeURL;
+
 private:
   nsNPAPIPlugin* mPlugin;
 
   // array of plugin-initiated stream listeners
   nsTArray<nsNPAPIPluginStreamListener*> mPStreamListeners;
   
   // array of browser-initiated stream listeners
   nsTArray<nsPluginStreamListenerPeer*> mBStreamListeners;
--- a/modules/plugin/base/src/nsPluginHost.h
+++ b/modules/plugin/base/src/nsPluginHost.h
@@ -176,16 +176,19 @@ public:
 
   // The guts of InstantiateEmbeddedPlugin.  The last argument should
   // be false if we already have an in-flight stream and don't need to
   // set up a new stream.
   nsresult DoInstantiateEmbeddedPlugin(const char *aMimeType, nsIURI* aURL,
                                        nsIPluginInstanceOwner* aOwner,
                                        PRBool aAllowOpeningStreams);
 
+  // Does not accept NULL and should never fail.
+  nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin);
+
 private:
   nsresult
   TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsIPluginInstanceOwner *aOwner);
 
   nsresult
   NewEmbeddedPluginStreamListener(nsIURI* aURL, nsIPluginInstanceOwner *aOwner,
                                   nsNPAPIPluginInstance* aInstance,
                                   nsIStreamListener** aListener);
@@ -201,19 +204,16 @@ private:
   // Return an nsPluginTag for this type, if any.  If aCheckEnabled is
   // true, only enabled plugins will be returned.
   nsPluginTag*
   FindPluginForType(const char* aMimeType, PRBool aCheckEnabled);
 
   nsPluginTag*
   FindPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
 
-  // Does not accept NULL and should never fail.
-  nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin);
-
   nsresult
   FindStoppedPluginForURL(nsIURI* aURL, nsIPluginInstanceOwner *aOwner);
 
   nsresult
   FindPlugins(PRBool aCreatePluginList, PRBool * aPluginsChanged);
 
   nsresult
   ScanPluginsDirectory(nsIFile * pluginsDir, 
--- a/netwerk/base/public/nsNetUtil.h
+++ b/netwerk/base/public/nsNetUtil.h
@@ -47,16 +47,18 @@
 #include "nsStringGlue.h"
 #include "nsMemory.h"
 #include "nsCOMPtr.h"
 #include "prio.h" // for read/write flags, permissions, etc.
 
 #include "nsCRT.h"
 #include "nsIURI.h"
 #include "nsIStandardURL.h"
+#include "nsIURLParser.h"
+#include "nsIUUIDGenerator.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsISafeOutputStream.h"
 #include "nsIStreamListener.h"
 #include "nsIRequestObserverProxy.h"
 #include "nsISimpleStreamListener.h"
 #include "nsILoadGroup.h"
 #include "nsIInterfaceRequestor.h"
@@ -1771,9 +1773,86 @@ NS_IsInternalSameURIRedirect(nsIChannel 
   if (!oldURI || !newURI) {
     return PR_FALSE;
   }
 
   PRBool res;
   return NS_SUCCEEDED(oldURI->Equals(newURI, &res)) && res;
 }
 
+/**
+ * Helper function to create a random URL string that's properly formed
+ * but guaranteed to be invalid.
+ */  
+#define NS_FAKE_SCHEME "http://"
+#define NS_FAKE_TLD ".invalid"
+inline nsresult
+NS_MakeRandomInvalidURLString(nsCString& result)
+{
+  nsresult rv;
+  nsCOMPtr<nsIUUIDGenerator> uuidgen =
+    do_GetService("@mozilla.org/uuid-generator;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsID idee;
+  rv = uuidgen->GenerateUUIDInPlace(&idee);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  char chars[NSID_LENGTH];
+  idee.ToProvidedString(chars);
+
+  result.AssignLiteral(NS_FAKE_SCHEME);
+  // Strip off the '{' and '}' at the beginning and end of the UUID
+  result.Append(chars + 1, NSID_LENGTH - 3);
+  result.AppendLiteral(NS_FAKE_TLD);
+
+  return NS_OK;
+}
+#undef NS_FAKE_SCHEME
+#undef NS_FAKE_TLD
+
+/**
+ * Helper function to determine whether urlString is Java-compatible --
+ * whether it can be passed to the Java URL(String) constructor without the
+ * latter throwing a MalformedURLException, or without Java otherwise
+ * mishandling it.
+ */  
+inline nsresult
+NS_CheckIsJavaCompatibleURLString(nsCString& urlString, PRBool *result)
+{
+  *result = PR_FALSE; // Default to "no"
+
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsIURLParser> urlParser =
+    do_GetService(NS_STDURLPARSER_CONTRACTID, &rv);
+  if (NS_FAILED(rv) || !urlParser)
+    return NS_ERROR_FAILURE;
+
+  PRBool compatible = PR_TRUE;
+  PRUint32 schemePos = 0;
+  PRInt32 schemeLen = 0;
+  urlParser->ParseURL(urlString.get(), -1, &schemePos, &schemeLen,
+                      nsnull, nsnull, nsnull, nsnull);
+  if (schemeLen != -1) {
+    nsCString scheme;
+    scheme.Assign(urlString.get() + schemePos, schemeLen);
+    // By default Java only understands a small number of URL schemes, and of
+    // these only some are likely to represent user input (for example from a
+    // link or the location bar) that Java can legitimately be expected to
+    // handle.  (Besides those listed below, Java also understands the "jar",
+    // "mailto" and "netdoc" schemes.  But it probably doesn't expect these
+    // from a browser, and is therefore likely to mishandle them.)
+    if (PL_strcasecmp(scheme.get(), "http") &&
+        PL_strcasecmp(scheme.get(), "https") &&
+        PL_strcasecmp(scheme.get(), "file") &&
+        PL_strcasecmp(scheme.get(), "ftp") &&
+        PL_strcasecmp(scheme.get(), "gopher"))
+      compatible = PR_FALSE;
+  } else {
+    compatible = PR_FALSE;
+  }
+
+  *result = compatible;
+
+  return NS_OK;
+}
+
 #endif // !nsNetUtil_h__