Bug 1049273 - Expose GMP voucher to GMP at runtime if its present. r=jesup
authorChris Pearce <cpearce@mozilla.com>
Fri, 14 Nov 2014 21:39:39 +1300
changeset 240117 85ccd5fb5cfddf905089316283d92f65adac90ef
parent 240116 278691ca4f3eb36ba3bf8d2811a93777cfdbdfb7
child 240118 1c42f7b5b39c6233de1f2da7decb83bbaeb3842c
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs1049273
milestone36.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 1049273 - Expose GMP voucher to GMP at runtime if its present. r=jesup
dom/media/gmp-plugin/Makefile.in
dom/media/gmp-plugin/fake.voucher
dom/media/gmp-plugin/gmp-fake.cpp
dom/media/gmp-plugin/gmp-test-decryptor.cpp
dom/media/gmp-plugin/gmp-test-decryptor.h
dom/media/gmp/GMPChild.cpp
dom/media/gmp/GMPChild.h
dom/media/gmp/GMPDecryptorChild.cpp
dom/media/gmp/GMPDecryptorChild.h
dom/media/gmp/gmp-api/gmp-decryption.h
dom/media/gtest/TestGMPCrossOrigin.cpp
--- a/dom/media/gmp-plugin/Makefile.in
+++ b/dom/media/gmp-plugin/Makefile.in
@@ -2,11 +2,12 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 INSTALL_TARGETS += FAKE_GMP_PLUGIN
 FAKE_GMP_PLUGIN_DEST = $(DEPTH)/dist/bin/gmp-fake/1.0
 FAKE_GMP_PLUGIN_FILES = \
   $(SHARED_LIBRARY) \
-  $(srcdir)/fake.info
+  $(srcdir)/fake.info \
+  $(srcdir)/fake.voucher
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp-plugin/fake.voucher
@@ -0,0 +1,1 @@
+gmp-fake placeholder voucher
\ No newline at end of file
--- a/dom/media/gmp-plugin/gmp-fake.cpp
+++ b/dom/media/gmp-plugin/gmp-fake.cpp
@@ -397,17 +397,17 @@ extern "C" {
   GMPGetAPI (const char* aApiName, void* aHostAPI, void** aPluginApi) {
     if (!strcmp (aApiName, "decode-video")) {
       *aPluginApi = new FakeVideoDecoder (static_cast<GMPVideoHost*> (aHostAPI));
       return GMPNoErr;
     } else if (!strcmp (aApiName, "encode-video")) {
       *aPluginApi = new FakeVideoEncoder (static_cast<GMPVideoHost*> (aHostAPI));
       return GMPNoErr;
     } else if (!strcmp (aApiName, "eme-decrypt")) {
-      *aPluginApi = new FakeDecryptor();
+      *aPluginApi = new FakeDecryptor(static_cast<GMPDecryptorHost*> (aHostAPI));
       return GMPNoErr;
     } else if (!strcmp (aApiName, "async-shutdown")) {
       *aPluginApi = new TestAsyncShutdown(static_cast<GMPAsyncShutdownHost*> (aHostAPI));
       return GMPNoErr;
     }
     return GMPGenericErr;
   }
 
--- a/dom/media/gmp-plugin/gmp-test-decryptor.cpp
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.cpp
@@ -29,18 +29,19 @@ static bool sMultiClientTest = false;
 void
 MaybeFinish()
 {
   if (sFinishedTruncateTest && sFinishedReplaceTest && sMultiClientTest) {
     FakeDecryptor::Message("test-storage complete");
   }
 }
 
-FakeDecryptor::FakeDecryptor()
+FakeDecryptor::FakeDecryptor(GMPDecryptorHost* aHost)
   : mCallback(nullptr)
+  , mHost(aHost)
 {
   MOZ_ASSERT(!sInstance);
   sInstance = this;
 }
 
 void FakeDecryptor::DecryptingComplete()
 {
   sInstance = nullptr;
@@ -323,16 +324,22 @@ FakeDecryptor::UpdateSession(uint32_t aP
       sShutdownMode = ShutdownStoreToken;
       sShutdownToken = tokens[2];
       Message("shutdown-token received " + sShutdownToken);
     }
   } else if (task == "retrieve-shutdown-token") {
     ReadRecord("shutdown-token", new ReportReadRecordContinuation("shutdown-token"));
   } else if (task == "test-op-apis") {
     mozilla::gmptest::TestOuputProtectionAPIs();
+  } else if (task == "retrieve-plugin-voucher") {
+    const uint8_t* rawVoucher = nullptr;
+    uint32_t length = 0;
+    mHost->GetPluginVoucher(&rawVoucher, &length);
+    std::string voucher((const char*)rawVoucher, (const char*)(rawVoucher + length));
+    Message("retrieved plugin-voucher: " + voucher);
   }
 }
 
 class CompleteShutdownTask : public GMPTask {
 public:
   explicit CompleteShutdownTask(GMPAsyncShutdownHost* aHost)
     : mHost(aHost)
   {
--- a/dom/media/gmp-plugin/gmp-test-decryptor.h
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.h
@@ -9,17 +9,17 @@
 #include "gmp-decryption.h"
 #include "gmp-async-shutdown.h"
 #include <string>
 #include "mozilla/Attributes.h"
 
 class FakeDecryptor : public GMPDecryptor {
 public:
 
-  FakeDecryptor();
+  FakeDecryptor(GMPDecryptorHost* aHost);
 
   virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE {
     mCallback = aCallback;
   }
 
   virtual void CreateSession(uint32_t aPromiseId,
                              const char* aInitDataType,
                              uint32_t aInitDataTypeSize,
@@ -71,16 +71,17 @@ public:
 private:
 
   virtual ~FakeDecryptor() {}
   static FakeDecryptor* sInstance;
 
   void TestStorage();
 
   GMPDecryptorCallback* mCallback;
+  GMPDecryptorHost* mHost;
 };
 
 class TestAsyncShutdown : public GMPAsyncShutdown {
 public:
   explicit TestAsyncShutdown(GMPAsyncShutdownHost* aHost)
     : mHost(aHost)
   {
   }
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -14,22 +14,24 @@
 #include "nsDebugImpl.h"
 #include "nsIFile.h"
 #include "nsXULAppAPI.h"
 #include "gmp-video-decode.h"
 #include "gmp-video-encode.h"
 #include "GMPPlatform.h"
 #include "mozilla/dom/CrashReporterChild.h"
 #ifdef XP_WIN
-#include <fstream>
 #include "nsCRT.h"
 #endif
+#include <fstream>
 
 using mozilla::dom::CrashReporterChild;
 
+static const int MAX_PLUGIN_VOUCHER_LENGTH = 50000;
+
 #ifdef XP_WIN
 #include <stdlib.h> // for _exit()
 #else
 #include <unistd.h> // for _exit()
 #endif
 
 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
@@ -402,16 +404,17 @@ GMPChild::GetLibPath(nsACString& aOutLib
 }
 
 bool
 GMPChild::RecvStartPlugin()
 {
 #if defined(XP_WIN)
   PreLoadLibraries(mPluginPath);
 #endif
+  PreLoadPluginVoucher(mPluginPath);
 
   nsCString libPath;
   if (!GetLibPath(libPath)) {
     return false;
   }
 
   auto platformAPI = new GMPPlatformAPI();
   InitPlatformAPI(*platformAPI, this);
@@ -533,17 +536,17 @@ GMPChild::DeallocPGMPVideoDecoderChild(P
 {
   delete aActor;
   return true;
 }
 
 PGMPDecryptorChild*
 GMPChild::AllocPGMPDecryptorChild()
 {
-  GMPDecryptorChild* actor = new GMPDecryptorChild(this);
+  GMPDecryptorChild* actor = new GMPDecryptorChild(this, mPluginVoucher);
   actor->AddRef();
   return actor;
 }
 
 bool
 GMPChild::DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor)
 {
   static_cast<GMPDecryptorChild*>(aActor)->Release();
@@ -704,10 +707,61 @@ GMPChild::RecvBeginAsyncShutdown()
 
 void
 GMPChild::ShutdownComplete()
 {
   MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
   SendAsyncShutdownComplete();
 }
 
+static bool
+GetPluginVoucherFile(const std::string& aPluginPath,
+                     nsCOMPtr<nsIFile>& aOutVoucherFile)
+{
+  nsAutoString baseName;
+#if defined(XP_MACOSX)
+  nsCOMPtr<nsIFile> libDir;
+  GetFileBase(aPluginPath, aOutVoucherFile, libDir, baseName);
+#else
+  GetFileBase(aPluginPath, aOutVoucherFile, baseName);
+#endif
+  nsAutoString infoFileName = baseName + NS_LITERAL_STRING(".voucher");
+  aOutVoucherFile->AppendRelativePath(infoFileName);
+  return true;
+}
+
+bool
+GMPChild::PreLoadPluginVoucher(const std::string& aPluginPath)
+{
+  nsCOMPtr<nsIFile> voucherFile;
+  GetPluginVoucherFile(aPluginPath, voucherFile);
+
+  nsString path;
+  voucherFile->GetPath(path);
+
+  std::ifstream stream;
+  stream.open(NS_ConvertUTF16toUTF8(path).get(), std::ios::binary);
+  if (!stream.good()) {
+    return false;
+  }
+
+  std::streampos start = stream.tellg();
+  stream.seekg (0, std::ios::end);
+  std::streampos end = stream.tellg();
+  stream.seekg (0, std::ios::beg);
+  auto length = end - start;
+  if (length > MAX_PLUGIN_VOUCHER_LENGTH) {
+    NS_WARNING("Plugin voucher file too big!");
+    return false;
+  }
+
+  mPluginVoucher.SetLength(length);
+  stream.read((char*)mPluginVoucher.Elements(), length);
+  if (!stream) {
+    NS_WARNING("Failed to read plugin voucher file!");
+    return false;
+  }
+
+  return true;
+}
+
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/GMPChild.h
+++ b/dom/media/gmp/GMPChild.h
@@ -46,16 +46,18 @@ public:
   void ShutdownComplete() MOZ_OVERRIDE;
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   void StartMacSandbox();
 #endif
 
 private:
 
+  bool PreLoadPluginVoucher(const std::string& aPluginPath);
+
   bool GetLibPath(nsACString& aOutLibPath);
 
   virtual bool RecvSetNodeId(const nsCString& aNodeId) MOZ_OVERRIDE;
   virtual bool RecvStartPlugin() MOZ_OVERRIDE;
 
   virtual PCrashReporterChild* AllocPCrashReporterChild(const NativeThreadId& aThread) MOZ_OVERRIDE;
   virtual bool DeallocPCrashReporterChild(PCrashReporterChild*) MOZ_OVERRIDE;
 
@@ -95,14 +97,15 @@ private:
 
   MessageLoop* mGMPMessageLoop;
   std::string mPluginPath;
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   nsCString mPluginBinaryPath;
 #endif
   std::string mNodeId;
   GMPLoader* mGMPLoader;
+  nsTArray<uint8_t> mPluginVoucher;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPChild_h_
--- a/dom/media/gmp/GMPDecryptorChild.cpp
+++ b/dom/media/gmp/GMPDecryptorChild.cpp
@@ -21,19 +21,21 @@
         FROM_HERE, NewRunnableMethod(this, &GMPDecryptorChild::_func, __VA_ARGS__) \
       ); \
     } \
   } while(false)
 
 namespace mozilla {
 namespace gmp {
 
-GMPDecryptorChild::GMPDecryptorChild(GMPChild* aPlugin)
+GMPDecryptorChild::GMPDecryptorChild(GMPChild* aPlugin,
+                                     const nsTArray<uint8_t>& aPluginVoucher)
   : mSession(nullptr)
   , mPlugin(aPlugin)
+  , mPluginVoucher(aPluginVoucher)
 {
   MOZ_ASSERT(mPlugin);
 }
 
 GMPDecryptorChild::~GMPDecryptorChild()
 {
 }
 
@@ -169,30 +171,35 @@ GMPDecryptorChild::Decrypted(GMPBuffer* 
 void
 GMPDecryptorChild::SetCapabilities(uint64_t aCaps)
 {
   CALL_ON_GMP_THREAD(SendSetCaps, aCaps);
 }
 
 void
 GMPDecryptorChild::GetSandboxVoucher(const uint8_t** aVoucher,
-                                     uint8_t* aVoucherLength)
+                                     uint32_t* aVoucherLength)
 {
+  if (!aVoucher || !aVoucherLength) {
+    return;
+  }
   const char* voucher = "placeholder_sandbox_voucher.";
   *aVoucher = (uint8_t*)voucher;
   *aVoucherLength = strlen(voucher);
 }
 
 void
 GMPDecryptorChild::GetPluginVoucher(const uint8_t** aVoucher,
-                                    uint8_t* aVoucherLength)
+                                    uint32_t* aVoucherLength)
 {
-  const char* voucher = "placeholder_plugin_voucher.";
-  *aVoucher = (uint8_t*)voucher;
-  *aVoucherLength = strlen(voucher);
+  if (!aVoucher || !aVoucherLength) {
+    return;
+  }
+  *aVoucher = mPluginVoucher.Elements();
+  *aVoucherLength = mPluginVoucher.Length();
 }
 
 bool
 GMPDecryptorChild::RecvInit()
 {
   if (!mSession) {
     return false;
   }
--- a/dom/media/gmp/GMPDecryptorChild.h
+++ b/dom/media/gmp/GMPDecryptorChild.h
@@ -19,17 +19,18 @@ class GMPChild;
 
 class GMPDecryptorChild : public GMPDecryptorCallback
                         , public GMPDecryptorHost
                         , public PGMPDecryptorChild
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPDecryptorChild);
 
-  explicit GMPDecryptorChild(GMPChild* aPlugin);
+  explicit GMPDecryptorChild(GMPChild* aPlugin,
+                             const nsTArray<uint8_t>& aPluginVoucher);
 
   void Init(GMPDecryptor* aSession);
 
   // GMPDecryptorCallback
   virtual void ResolveNewSessionPromise(uint32_t aPromiseId,
                                         const char* aSessionId,
                                         uint32_t aSessionIdLength) MOZ_OVERRIDE;
   virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
@@ -73,20 +74,20 @@ public:
                               uint32_t aKeyIdLength) MOZ_OVERRIDE;
 
   virtual void SetCapabilities(uint64_t aCaps) MOZ_OVERRIDE;
 
   virtual void Decrypted(GMPBuffer* aBuffer, GMPErr aResult) MOZ_OVERRIDE;
 
   // GMPDecryptorHost
   virtual void GetSandboxVoucher(const uint8_t** aVoucher,
-                                 uint8_t* aVoucherLength) MOZ_OVERRIDE;
+                                 uint32_t* aVoucherLength) MOZ_OVERRIDE;
 
   virtual void GetPluginVoucher(const uint8_t** aVoucher,
-                                uint8_t* aVoucherLength) MOZ_OVERRIDE;
+                                uint32_t* aVoucherLength) MOZ_OVERRIDE;
 private:
   ~GMPDecryptorChild();
 
   // GMPDecryptorChild
   virtual bool RecvInit() MOZ_OVERRIDE;
 
   virtual bool RecvCreateSession(const uint32_t& aPromiseId,
                                  const nsCString& aInitDataType,
@@ -115,14 +116,17 @@ private:
                                         const nsTArray<uint8_t>& aServerCert) MOZ_OVERRIDE;
 
   virtual bool RecvDecryptingComplete() MOZ_OVERRIDE;
 
   // GMP's GMPDecryptor implementation.
   // Only call into this on the (GMP process) main thread.
   GMPDecryptor* mSession;
   GMPChild* mPlugin;
+
+  // Reference to the voucher owned by the GMPChild.
+  const nsTArray<uint8_t>& mPluginVoucher;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPDecryptorChild_h_
--- a/dom/media/gmp/gmp-api/gmp-decryption.h
+++ b/dom/media/gmp/gmp-api/gmp-decryption.h
@@ -178,20 +178,20 @@ public:
   // Returns decrypted buffer to Gecko, or reports failure.
   virtual void Decrypted(GMPBuffer* aBuffer, GMPErr aResult) = 0;
 };
 
 // Host interface, passed to GetAPIFunc(), with "decrypt".
 class GMPDecryptorHost {
 public:
   virtual void GetSandboxVoucher(const uint8_t** aVoucher,
-                                 uint8_t* aVoucherLength) = 0;
+                                 uint32_t* aVoucherLength) = 0;
 
   virtual void GetPluginVoucher(const uint8_t** aVoucher,
-                                uint8_t* aVoucherLength) = 0;
+                                uint32_t* aVoucherLength) = 0;
 };
 
 enum GMPSessionType {
   kGMPTemporySession = 0,
   kGMPPersistentSession = 1,
   kGMPSessionInvalid = 2 // Must always be last.
 };
 
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -566,16 +566,25 @@ class GMPStorageTest : public GMPDecrypt
                     false);
 
     Expect(NS_LITERAL_CSTRING("OP tests completed"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
     Update(NS_LITERAL_CSTRING("test-op-apis"));
   }
 #endif
 
+  void TestPluginVoucher() {
+    CreateDecryptor(NS_LITERAL_STRING("example17.com"),
+                    NS_LITERAL_STRING("example18.com"),
+                    false);
+    Expect(NS_LITERAL_CSTRING("retrieved plugin-voucher: gmp-fake placeholder voucher"),
+           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
+    Update(NS_LITERAL_CSTRING("retrieve-plugin-voucher"));
+  }
+
   void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
     mExpected.AppendElement(ExpectedMessage(aMessage, aContinuation));
   }
 
   void AwaitFinished() {
     while (!mFinished) {
       NS_ProcessNextEvent(nullptr, true);
     }
@@ -721,16 +730,21 @@ TEST(GeckoMediaPlugins, GMPStorageAsyncS
   runner->DoTest(&GMPStorageTest::TestAsyncShutdownTimeout);
 }
 
 TEST(GeckoMediaPlugins, GMPStorageAsyncShutdownStorage) {
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
   runner->DoTest(&GMPStorageTest::TestAsyncShutdownStorage);
 }
 
+TEST(GeckoMediaPlugins, GMPPluginVoucher) {
+  nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
+  runner->DoTest(&GMPStorageTest::TestPluginVoucher);
+}
+
 #if defined(XP_WIN)
 TEST(GeckoMediaPlugins, GMPOutputProtection) {
   // Output Protection is not available pre-Vista.
   if (!IsVistaOrLater()) {
     return;
   }
 
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();