Bug 558184 - Part 9 - Optionally load a script in a sandbox in the content process for every plugin instance. r=bz.
authorPeter Van der Beken <peterv@propagandism.org>
Tue, 27 Sep 2016 18:28:36 +0800
changeset 412276 d04876d5e3c6859b0224f36b8f7e1090cba3d56e
parent 412275 b31942dbee848f90041776eb0da2867b8ae9f59e
child 412277 afc28100c299bca3d7ee88121c815d9dccf9ae52
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs558184
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 558184 - Part 9 - Optionally load a script in a sandbox in the content process for every plugin instance. r=bz.
dom/base/nsObjectLoadingContent.cpp
dom/plugins/base/nsIPluginTag.idl
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginTags.cpp
dom/plugins/base/nsPluginTags.h
dom/plugins/ipc/PluginTypes.ipdlh
dom/webidl/FakePluginTagInit.webidl
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -2399,16 +2399,52 @@ nsObjectLoadingContent::LoadObject(bool 
                  "given out this id.");
 
       SetupFrameLoader(int32_t(id));
       if (!mFrameLoader) {
         rv = NS_ERROR_FAILURE;
         break;
       }
 
+
+      nsString sandboxScript;
+      tag->GetSandboxScript(sandboxScript);
+      if (!sandboxScript.IsEmpty()) {
+        // Create a sandbox.
+        AutoJSAPI jsapi;
+        jsapi.Init();
+        JS::Rooted<JSObject*> sandbox(jsapi.cx());
+        rv = nsContentUtils::XPConnect()->
+          CreateSandbox(jsapi.cx(), nsContentUtils::GetSystemPrincipal(),
+                        sandbox.address());
+        if (NS_FAILED(rv)) {
+          break;
+        }
+
+        AutoEntryScript aes(sandbox, "JS plugin sandbox code");
+
+        JS::Rooted<JS::Value> element(aes.cx());
+        if (!ToJSValue(aes.cx(), thisContent, &element)) {
+          rv = NS_ERROR_FAILURE;
+          break;
+        }
+
+        if (!JS_DefineProperty(aes.cx(), sandbox, "pluginElement", element, JSPROP_ENUMERATE)) {
+          rv = NS_ERROR_FAILURE;
+          break;
+        }
+
+        JS::Rooted<JS::Value> rval(aes.cx());
+        // If the eval'ed code throws we won't load and do fallback instead.
+        rv = nsContentUtils::XPConnect()->EvalInSandboxObject(sandboxScript, nullptr, aes.cx(), sandbox, &rval);
+        if (NS_FAILED(rv)) {
+          break;
+        }
+      }
+
       nsCOMPtr<nsIURI> handlerURI;
       if (tag) {
         tag->GetHandlerURI(getter_AddRefs(handlerURI));
       }
 
       if (!handlerURI) {
         NS_NOTREACHED("Selected type is not a proper fake plugin handler");
         rv = NS_ERROR_FAILURE;
--- a/dom/plugins/base/nsIPluginTag.idl
+++ b/dom/plugins/base/nsIPluginTag.idl
@@ -64,18 +64,30 @@ interface nsIPluginTag : nsISupports
 /**
  * An interface representing a "fake" plugin: one implemented in JavaScript, not
  * as a NPAPI plug-in.  See nsIPluginHost.registerFakePlugin and the
  * documentation for the FakePluginTagInit dictionary.
  */
 [scriptable, uuid(6d22c968-226d-4156-b230-da6ad6bbf6e8)]
 interface nsIFakePluginTag : nsIPluginTag
 {
-  // The URI that should be loaded into the tag (as a frame) to handle the
-  // plugin. Note that the original data/src value for the plugin is not loaded
-  // and will need to be requested by the handler via XHR or similar if desired.
+  /**
+   * The URI that should be loaded into the tag (as a frame) to handle the
+   * plugin. Note that the original data/src value for the plugin is not loaded
+   * and will need to be requested by the handler via XHR or similar if desired.
+   */
   readonly attribute nsIURI handlerURI;
 
   /**
+   * Optional script to run in a sandbox when instantiating a plugin. If this
+   * value is an empty string then no such script will be run.
+   * The script runs in a sandbox with system principal in the process that
+   * contains the element that instantiates the plugin (ie the EMBED or OBJECT
+   * element). The sandbox global has a 'pluginElement' property that the script
+   * can use to access the element that instantiates the plugin.
+   */
+  readonly attribute AString sandboxScript;
+
+  /**
    * A unique id for this JS-implemented plugin. 0 is a valid id.
    */
   readonly attribute unsigned long id;
 };
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -2337,17 +2337,18 @@ nsPluginHost::FindPluginsInContent(bool 
 
       mFakePlugins.AppendElement(new nsFakePluginTag(tag.id(),
                                                      mozilla::ipc::DeserializeURI(tag.handlerURI()),
                                                      tag.name().get(),
                                                      tag.description().get(),
                                                      tag.mimeTypes(),
                                                      tag.mimeDescriptions(),
                                                      tag.extensions(),
-                                                     tag.niceName()));
+                                                     tag.niceName(),
+                                                     tag.sandboxScript()));
     }
   }
 
   mPluginsLoaded = true;
   return NS_OK;
 }
 
 // if aCreatePluginList is false we will just scan for plugins
@@ -2536,17 +2537,18 @@ nsPluginHost::FindPluginsForContent(uint
       SerializeURI(tag->HandlerURI(), handlerURI);
       aFakePlugins->AppendElement(FakePluginTag(tag->Id(),
                                                 handlerURI,
                                                 tag->Name(),
                                                 tag->Description(),
                                                 tag->MimeTypes(),
                                                 tag->MimeDescriptions(),
                                                 tag->Extensions(),
-                                                tag->GetNiceFileName()));
+                                                tag->GetNiceFileName(),
+                                                tag->SandboxScript()));
       continue;
     }
 
     /// FIXME-jsplugins - We need to cleanup the various plugintag classes
     /// to be more sane and avoid this dance
     nsPluginTag *tag = static_cast<nsPluginTag *>(basetag.get());
 
     aPlugins->AppendElement(PluginTag(tag->mId,
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -828,22 +828,24 @@ nsFakePluginTag::nsFakePluginTag()
 
 nsFakePluginTag::nsFakePluginTag(uint32_t aId,
                                  already_AddRefed<nsIURI>&& aHandlerURI,
                                  const char* aName,
                                  const char* aDescription,
                                  const nsTArray<nsCString>& aMimeTypes,
                                  const nsTArray<nsCString>& aMimeDescriptions,
                                  const nsTArray<nsCString>& aExtensions,
-                                 const nsCString& aNiceName)
+                                 const nsCString& aNiceName,
+                                 const nsString& aSandboxScript)
   : nsIInternalPluginTag(aName, aDescription, nullptr, nullptr,
                          aMimeTypes, aMimeDescriptions, aExtensions),
     mId(aId),
     mHandlerURI(aHandlerURI),
     mNiceName(aNiceName),
+    mSandboxScript(aSandboxScript),
     mState(nsPluginTag::ePluginState_Enabled)
 {}
 
 nsFakePluginTag::~nsFakePluginTag()
 {
 }
 
 NS_IMPL_ADDREF(nsFakePluginTag)
@@ -873,16 +875,17 @@ nsFakePluginTag::Create(const FakePlugin
   NS_ENSURE_SUCCESS(rv, rv);
 
   CopyUTF16toUTF8(aInitDictionary.mNiceName, tag->mNiceName);
   CopyUTF16toUTF8(aInitDictionary.mFullPath, tag->mFullPath);
   CopyUTF16toUTF8(aInitDictionary.mName, tag->mName);
   CopyUTF16toUTF8(aInitDictionary.mDescription, tag->mDescription);
   CopyUTF16toUTF8(aInitDictionary.mFileName, tag->mFileName);
   CopyUTF16toUTF8(aInitDictionary.mVersion, tag->mVersion);
+  tag->mSandboxScript = aInitDictionary.mSandboxScript;
 
   for (const FakePluginMimeEntry& mimeEntry : aInitDictionary.mMimeEntries) {
     CopyUTF16toUTF8(mimeEntry.mType, *tag->mMimeTypes.AppendElement());
     CopyUTF16toUTF8(mimeEntry.mDescription,
                     *tag->mMimeDescriptions.AppendElement());
     CopyUTF16toUTF8(mimeEntry.mExtension, *tag->mExtensions.AppendElement());
   }
 
@@ -900,16 +903,23 @@ nsFakePluginTag::HandlerURIMatches(nsIUR
 NS_IMETHODIMP
 nsFakePluginTag::GetHandlerURI(nsIURI **aResult)
 {
   NS_IF_ADDREF(*aResult = mHandlerURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsFakePluginTag::GetSandboxScript(nsAString& aSandboxScript)
+{
+  aSandboxScript = mSandboxScript;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsFakePluginTag::GetDescription(/* utf-8 */ nsACString& aResult)
 {
   aResult = mDescription;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFakePluginTag::GetFilename(/* utf-8 */ nsACString& aResult)
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -213,27 +213,30 @@ public:
                          nsFakePluginTag** aPluginTag);
   nsFakePluginTag(uint32_t aId,
                   already_AddRefed<nsIURI>&& aHandlerURI,
                   const char* aName,
                   const char* aDescription,
                   const nsTArray<nsCString>& aMimeTypes,
                   const nsTArray<nsCString>& aMimeDescriptions,
                   const nsTArray<nsCString>& aExtensions,
-                  const nsCString& aNiceName);
+                  const nsCString& aNiceName,
+                  const nsString& aSandboxScript);
 
   bool IsEnabled() override;
   const nsCString& GetNiceFileName() override;
 
   bool HandlerURIMatches(nsIURI* aURI);
 
   nsIURI* HandlerURI() const { return mHandlerURI; }
 
   uint32_t Id() const { return mId; }
 
+  const nsString& SandboxScript() const { return mSandboxScript; }
+
   static const int32_t NOT_JSPLUGIN = -1;
 
 private:
   nsFakePluginTag();
   virtual ~nsFakePluginTag();
 
   // A unique id for this JS-implemented plugin. Registering a plugin through
   // nsPluginHost::RegisterFakePlugin assigns a new id. The id is transferred
@@ -244,16 +247,18 @@ private:
 
   // The URI of the handler for our fake plugin.
   // FIXME-jsplugins do we need to sanity check these?
   nsCOMPtr<nsIURI>    mHandlerURI;
 
   nsCString     mFullPath;
   nsCString     mNiceName;
 
+  nsString      mSandboxScript;
+
   nsPluginTag::PluginState mState;
 
   // Stores the id to use for the JS-implemented plugin that gets registered
   // next through nsPluginHost::RegisterFakePlugin.
   static uint32_t sNextId;
 };
 
 #endif // nsPluginTags_h_
--- a/dom/plugins/ipc/PluginTypes.ipdlh
+++ b/dom/plugins/ipc/PluginTypes.ipdlh
@@ -32,16 +32,17 @@ struct FakePluginTag
   uint32_t id;
   URIParams handlerURI;
   nsCString name;
   nsCString description;
   nsCString[] mimeTypes;
   nsCString[] mimeDescriptions;
   nsCString[] extensions;
   nsCString niceName;
+  nsString sandboxScript;
 };
 
 union PluginIdentifier
 {
   nsCString;
   int32_t;
 };
 
--- a/dom/webidl/FakePluginTagInit.webidl
+++ b/dom/webidl/FakePluginTagInit.webidl
@@ -21,16 +21,25 @@ dictionary FakePluginTagInit {
   DOMString niceName = "";
 
   // Other things can be provided but don't really matter that much.
   DOMString fullPath = "";
   DOMString name = "";
   DOMString description = "";
   DOMString fileName = "";
   DOMString version = "";
+
+  /**
+   * Optional script to run in a sandbox when instantiating a plugin. The script
+   * runs in a sandbox with system principal in the process that contains the
+   * element that instantiates the plugin (ie the EMBED or OBJECT element). The
+   * sandbox global has a 'pluginElement' property that the script can use to
+   * access the element that instantiates the plugin.
+   */
+  DOMString sandboxScript = "";
 };
 
 /**
  * A single MIME entry for the fake plugin.
  */
 dictionary FakePluginMimeEntry {
   required DOMString type;
   DOMString description = "";