Bug 1271169 - Move EME/GMP device binding code into GMPDeviceBinding.h/cpp. r=gerald
authorChris Pearce <cpearce@mozilla.com>
Wed, 11 May 2016 19:56:42 +1200
changeset 298516 200726fdfb53829ef6dd3e538964367e95196144
parent 298515 a94bd69a86169a16b818795845e8d75ab5ea2ffd
child 298517 4da6cece7b58ff4bca9dd599db0eddd3f4d78af3
push id77210
push usercpearce@mozilla.com
push dateTue, 24 May 2016 02:16:54 +0000
treeherdermozilla-inbound@57fa89168a44 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1271169
milestone49.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 1271169 - Move EME/GMP device binding code into GMPDeviceBinding.h/cpp. r=gerald I want the EME device binding/nodeId code to be callable from gtests, as well as from in plugin-container. First step is to move the device binding code into a discrete file, so I can also link that into gtests, and call it from there to compare the result with what's in the GMP process. MozReview-Commit-ID: 9xT2rp3hWW
dom/media/gmp/GMPLoader.cpp
dom/media/gmp/rlz/GMPDeviceBinding.cpp
dom/media/gmp/rlz/GMPDeviceBinding.h
dom/media/gmp/rlz/mac/lib/machine_id_mac.cc
--- a/dom/media/gmp/GMPLoader.cpp
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -10,44 +10,19 @@
 #include "gmp-entrypoints.h"
 #include "prlink.h"
 #include "prenv.h"
 
 #include <string>
 
 #ifdef XP_WIN
 #include "windows.h"
-#ifdef MOZ_SANDBOX
-#include <intrin.h>
-#include <assert.h>
-#endif
 #endif
 
-#ifdef XP_MACOSX
-#include <assert.h>
-#ifdef HASH_NODE_ID_WITH_DEVICE_ID
-#include <unistd.h>
-#include <mach/mach.h>
-#include <mach/mach_vm.h>
-#endif
-#endif
-
-#if defined(HASH_NODE_ID_WITH_DEVICE_ID)
-// In order to provide EME plugins with a "device binding" capability,
-// in the parent we generate and store some random bytes as salt for every
-// (origin, urlBarOrigin) pair that uses EME. We store these bytes so
-// that every time we revisit the same origin we get the same salt.
-// We send this salt to the child on startup. The child collects some
-// device specific data and munges that with the salt to create the
-// "node id" that we expose to EME plugins. It then overwrites the device
-// specific data, and activates the sandbox.
-#include "rlz/lib/machine_id.h"
-#include "rlz/lib/string_utils.h"
-#include "sha256.h"
-#endif
+#include "GMPDeviceBinding.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPLoaderImpl : public GMPLoader {
 public:
   explicit GMPLoaderImpl(SandboxStarter* aStarter)
     : mSandboxStarter(aStarter)
@@ -139,166 +114,27 @@ public:
       setNodeIdFunc(aNodeId, aLength);
     }
   }
 
 private:
   PRLibrary* mLib = nullptr;
 };
 
-#if defined(XP_WIN) && defined(HASH_NODE_ID_WITH_DEVICE_ID)
-MOZ_NEVER_INLINE
-static bool
-GetStackAfterCurrentFrame(uint8_t** aOutTop, uint8_t** aOutBottom)
-{
-  // "Top" of the free space on the stack is directly after the memory
-  // holding our return address.
-  uint8_t* top = (uint8_t*)_AddressOfReturnAddress();
-
-  // Look down the stack until we find the guard page...
-  MEMORY_BASIC_INFORMATION memInfo = {0};
-  uint8_t* bottom = top;
-  while (1) {
-    if (!VirtualQuery(bottom, &memInfo, sizeof(memInfo))) {
-      return false;
-    }
-    if ((memInfo.Protect & PAGE_GUARD) == PAGE_GUARD) {
-      bottom = (uint8_t*)memInfo.BaseAddress + memInfo.RegionSize;
-#ifdef DEBUG
-      if (!VirtualQuery(bottom, &memInfo, sizeof(memInfo))) {
-        return false;
-      }
-      assert(!(memInfo.Protect & PAGE_GUARD)); // Should have found boundary.
-#endif
-      break;
-    } else if (memInfo.State != MEM_COMMIT ||
-               (memInfo.AllocationProtect & PAGE_READWRITE) != PAGE_READWRITE) {
-      return false;
-    }
-    bottom = (uint8_t*)memInfo.BaseAddress - 1;
-  }
-  *aOutTop = top;
-  *aOutBottom = bottom;
-  return true;
-}
-#endif
-
-#if defined(XP_MACOSX) && defined(HASH_NODE_ID_WITH_DEVICE_ID)
-static mach_vm_address_t
-RegionContainingAddress(mach_vm_address_t aAddress)
-{
-  mach_port_t task;
-  kern_return_t kr = task_for_pid(mach_task_self(), getpid(), &task);
-  if (kr != KERN_SUCCESS) {
-    return 0;
-  }
-
-  mach_vm_address_t address = aAddress;
-  mach_vm_size_t size;
-  vm_region_basic_info_data_64_t info;
-  mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
-  mach_port_t object_name;
-  kr = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO_64,
-                      reinterpret_cast<vm_region_info_t>(&info), &count,
-                      &object_name);
-  if (kr != KERN_SUCCESS || size == 0
-      || address > aAddress || address + size <= aAddress) {
-    // mach_vm_region failed, or couldn't find region at given address.
-    return 0;
-  }
-
-  return address;
-}
-
-MOZ_NEVER_INLINE
-static bool
-GetStackAfterCurrentFrame(uint8_t** aOutTop, uint8_t** aOutBottom)
-{
-  mach_vm_address_t stackFrame =
-    reinterpret_cast<mach_vm_address_t>(__builtin_frame_address(0));
-  *aOutTop = reinterpret_cast<uint8_t*>(stackFrame);
-  // Kernel code shows that stack is always a single region.
-  *aOutBottom = reinterpret_cast<uint8_t*>(RegionContainingAddress(stackFrame));
-  return *aOutBottom && (*aOutBottom < *aOutTop);
-}
-#endif
-
-#ifdef HASH_NODE_ID_WITH_DEVICE_ID
-static void SecureMemset(void* start, uint8_t value, size_t size)
-{
-  // Inline instructions equivalent to RtlSecureZeroMemory().
-  for (size_t i = 0; i < size; ++i) {
-    volatile uint8_t* p = static_cast<volatile uint8_t*>(start) + i;
-    *p = value;
-  }
-}
-#endif
-
 bool
 GMPLoaderImpl::Load(const char* aUTF8LibPath,
                     uint32_t aUTF8LibPathLen,
                     char* aOriginSalt,
                     uint32_t aOriginSaltLen,
                     const GMPPlatformAPI* aPlatformAPI,
                     GMPAdapter* aAdapter)
 {
   std::string nodeId;
-#ifdef HASH_NODE_ID_WITH_DEVICE_ID
-  if (aOriginSaltLen > 0) {
-    std::vector<uint8_t> deviceId;
-    int volumeId;
-    if (!rlz_lib::GetRawMachineId(&deviceId, &volumeId)) {
-      return false;
-    }
-
-    SHA256Context ctx;
-    SHA256_Begin(&ctx);
-    SHA256_Update(&ctx, (const uint8_t*)aOriginSalt, aOriginSaltLen);
-    SHA256_Update(&ctx, deviceId.data(), deviceId.size());
-    SHA256_Update(&ctx, (const uint8_t*)&volumeId, sizeof(int));
-    uint8_t digest[SHA256_LENGTH] = {0};
-    unsigned int digestLen = 0;
-    SHA256_End(&ctx, digest, &digestLen, SHA256_LENGTH);
-
-    // Overwrite all data involved in calculation as it could potentially
-    // identify the user, so there's no chance a GMP can read it and use
-    // it for identity tracking.
-    SecureMemset(&ctx, 0, sizeof(ctx));
-    SecureMemset(aOriginSalt, 0, aOriginSaltLen);
-    SecureMemset(&volumeId, 0, sizeof(volumeId));
-    SecureMemset(deviceId.data(), '*', deviceId.size());
-    deviceId.clear();
-
-    if (!rlz_lib::BytesToString(digest, SHA256_LENGTH, &nodeId)) {
-      return false;
-    }
-
-    if (!PR_GetEnv("MOZ_GMP_DISABLE_NODE_ID_CLEANUP")) {
-      // We've successfully bound the origin salt to node id.
-      // rlz_lib::GetRawMachineId and/or the system functions it
-      // called could have left user identifiable data on the stack,
-      // so carefully zero the stack down to the guard page.
-      uint8_t* top;
-      uint8_t* bottom;
-      if (!GetStackAfterCurrentFrame(&top, &bottom)) {
-        return false;
-      }
-      assert(top >= bottom);
-      // Inline instructions equivalent to RtlSecureZeroMemory().
-      // We can't just use RtlSecureZeroMemory here directly, as in debug
-      // builds, RtlSecureZeroMemory() can't be inlined, and the stack
-      // memory it uses would get wiped by itself running, causing crashes.
-      for (volatile uint8_t* p = (volatile uint8_t*)bottom; p < top; p++) {
-        *p = 0;
-      }
-    }
-  } else
-#endif
-  {
-    nodeId = std::string(aOriginSalt, aOriginSalt + aOriginSaltLen);
+  if (!CalculateGMPDeviceId(aOriginSalt, aOriginSaltLen, nodeId)) {
+    return false;
   }
 
   // Start the sandbox now that we've generated the device bound node id.
   // This must happen after the node id is bound to the device id, as
   // generating the device id requires privileges.
   if (mSandboxStarter && !mSandboxStarter->Start(aUTF8LibPath)) {
     return false;
   }
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/rlz/GMPDeviceBinding.cpp
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "GMPDeviceBinding.h"
+#include "mozilla/Attributes.h"
+#include "prenv.h"
+
+#include <string>
+
+#ifdef XP_WIN
+#include "windows.h"
+#ifdef MOZ_SANDBOX
+#include <intrin.h>
+#include <assert.h>
+#endif
+#endif
+
+#if defined(HASH_NODE_ID_WITH_DEVICE_ID)
+
+// In order to provide EME plugins with a "device binding" capability,
+// in the parent we generate and store some random bytes as salt for every
+// (origin, urlBarOrigin) pair that uses EME. We store these bytes so
+// that every time we revisit the same origin we get the same salt.
+// We send this salt to the child on startup. The child collects some
+// device specific data and munges that with the salt to create the
+// "node id" that we expose to EME plugins. It then overwrites the device
+// specific data, and activates the sandbox.
+
+#include "rlz/lib/machine_id.h"
+#include "rlz/lib/string_utils.h"
+#include "sha256.h"
+
+#ifdef XP_WIN
+#include "windows.h"
+#ifdef MOZ_SANDBOX
+#include <intrin.h>
+#include <assert.h>
+#endif
+#endif
+
+#ifdef XP_MACOSX
+#include <assert.h>
+#ifdef HASH_NODE_ID_WITH_DEVICE_ID
+#include <unistd.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#endif
+#endif
+
+#endif // HASH_NODE_ID_WITH_DEVICE_ID
+
+namespace mozilla {
+namespace gmp {
+
+#if defined(XP_WIN) && defined(HASH_NODE_ID_WITH_DEVICE_ID)
+MOZ_NEVER_INLINE
+static bool
+GetStackAfterCurrentFrame(uint8_t** aOutTop, uint8_t** aOutBottom)
+{
+  // "Top" of the free space on the stack is directly after the memory
+  // holding our return address.
+  uint8_t* top = (uint8_t*)_AddressOfReturnAddress();
+
+  // Look down the stack until we find the guard page...
+  MEMORY_BASIC_INFORMATION memInfo = {0};
+  uint8_t* bottom = top;
+  while (1) {
+    if (!VirtualQuery(bottom, &memInfo, sizeof(memInfo))) {
+      return false;
+    }
+    if ((memInfo.Protect & PAGE_GUARD) == PAGE_GUARD) {
+      bottom = (uint8_t*)memInfo.BaseAddress + memInfo.RegionSize;
+#ifdef DEBUG
+      if (!VirtualQuery(bottom, &memInfo, sizeof(memInfo))) {
+        return false;
+      }
+      assert(!(memInfo.Protect & PAGE_GUARD)); // Should have found boundary.
+#endif
+      break;
+    } else if (memInfo.State != MEM_COMMIT ||
+               (memInfo.AllocationProtect & PAGE_READWRITE) != PAGE_READWRITE) {
+      return false;
+    }
+    bottom = (uint8_t*)memInfo.BaseAddress - 1;
+  }
+  *aOutTop = top;
+  *aOutBottom = bottom;
+  return true;
+}
+#endif
+
+#if defined(XP_MACOSX) && defined(HASH_NODE_ID_WITH_DEVICE_ID)
+static mach_vm_address_t
+RegionContainingAddress(mach_vm_address_t aAddress)
+{
+  mach_port_t task;
+  kern_return_t kr = task_for_pid(mach_task_self(), getpid(), &task);
+  if (kr != KERN_SUCCESS) {
+    return 0;
+  }
+
+  mach_vm_address_t address = aAddress;
+  mach_vm_size_t size;
+  vm_region_basic_info_data_64_t info;
+  mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
+  mach_port_t object_name;
+  kr = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO_64,
+                      reinterpret_cast<vm_region_info_t>(&info), &count,
+                      &object_name);
+  if (kr != KERN_SUCCESS || size == 0
+      || address > aAddress || address + size <= aAddress) {
+    // mach_vm_region failed, or couldn't find region at given address.
+    return 0;
+  }
+
+  return address;
+}
+
+MOZ_NEVER_INLINE
+static bool
+GetStackAfterCurrentFrame(uint8_t** aOutTop, uint8_t** aOutBottom)
+{
+  mach_vm_address_t stackFrame =
+    reinterpret_cast<mach_vm_address_t>(__builtin_frame_address(0));
+  *aOutTop = reinterpret_cast<uint8_t*>(stackFrame);
+  // Kernel code shows that stack is always a single region.
+  *aOutBottom = reinterpret_cast<uint8_t*>(RegionContainingAddress(stackFrame));
+  return *aOutBottom && (*aOutBottom < *aOutTop);
+}
+#endif
+
+#ifdef HASH_NODE_ID_WITH_DEVICE_ID
+static void SecureMemset(void* start, uint8_t value, size_t size)
+{
+  // Inline instructions equivalent to RtlSecureZeroMemory().
+  for (size_t i = 0; i < size; ++i) {
+    volatile uint8_t* p = static_cast<volatile uint8_t*>(start) + i;
+    *p = value;
+  }
+}
+#endif
+
+bool
+CalculateGMPDeviceId(char* aOriginSalt,
+                     uint32_t aOriginSaltLen,
+                     std::string& aOutNodeId)
+{
+#ifdef HASH_NODE_ID_WITH_DEVICE_ID
+  if (aOriginSaltLen > 0) {
+    std::vector<uint8_t> deviceId;
+    int volumeId;
+    if (!rlz_lib::GetRawMachineId(&deviceId, &volumeId)) {
+      return false;
+    }
+
+    SHA256Context ctx;
+    SHA256_Begin(&ctx);
+    SHA256_Update(&ctx, (const uint8_t*)aOriginSalt, aOriginSaltLen);
+    SHA256_Update(&ctx, deviceId.data(), deviceId.size());
+    SHA256_Update(&ctx, (const uint8_t*)&volumeId, sizeof(int));
+    uint8_t digest[SHA256_LENGTH] = {0};
+    unsigned int digestLen = 0;
+    SHA256_End(&ctx, digest, &digestLen, SHA256_LENGTH);
+
+    // Overwrite all data involved in calculation as it could potentially
+    // identify the user, so there's no chance a GMP can read it and use
+    // it for identity tracking.
+    SecureMemset(&ctx, 0, sizeof(ctx));
+    SecureMemset(aOriginSalt, 0, aOriginSaltLen);
+    SecureMemset(&volumeId, 0, sizeof(volumeId));
+    SecureMemset(deviceId.data(), '*', deviceId.size());
+    deviceId.clear();
+
+    if (!rlz_lib::BytesToString(digest, SHA256_LENGTH, &aOutNodeId)) {
+      return false;
+    }
+
+    if (!PR_GetEnv("MOZ_GMP_DISABLE_NODE_ID_CLEANUP")) {
+      // We've successfully bound the origin salt to node id.
+      // rlz_lib::GetRawMachineId and/or the system functions it
+      // called could have left user identifiable data on the stack,
+      // so carefully zero the stack down to the guard page.
+      uint8_t* top;
+      uint8_t* bottom;
+      if (!GetStackAfterCurrentFrame(&top, &bottom)) {
+        return false;
+      }
+      assert(top >= bottom);
+      // Inline instructions equivalent to RtlSecureZeroMemory().
+      // We can't just use RtlSecureZeroMemory here directly, as in debug
+      // builds, RtlSecureZeroMemory() can't be inlined, and the stack
+      // memory it uses would get wiped by itself running, causing crashes.
+      for (volatile uint8_t* p = (volatile uint8_t*)bottom; p < top; p++) {
+        *p = 0;
+      }
+    }
+  } else
+#endif
+  {
+    aOutNodeId = std::string(aOriginSalt, aOriginSalt + aOriginSaltLen);
+  }
+  return true;
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/rlz/GMPDeviceBinding.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef GMP_DEVICE_BINDING_h_
+#define GMP_DEVICE_BINDING_h_
+
+#include <string>
+
+namespace mozilla {
+namespace gmp {
+
+bool CalculateGMPDeviceId(char* aOriginSalt,
+                          uint32_t aOriginSaltLen,
+                          std::string& aOutNodeId);
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMP_DEVICE_BINDING_h_
--- a/dom/media/gmp/rlz/mac/lib/machine_id_mac.cc
+++ b/dom/media/gmp/rlz/mac/lib/machine_id_mac.cc
@@ -3,16 +3,17 @@
 // found in the LICENSE file.
 
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/IOKitLib.h>
 #include <IOKit/network/IOEthernetController.h>
 #include <IOKit/network/IOEthernetInterface.h>
 #include <IOKit/network/IONetworkInterface.h>
 #include <vector>
+#include <string>
 
 // Note: The original machine_id_mac.cc code is in namespace rlz_lib below.
 // It depends on some external files, which would bring in a log of Chromium
 // code if imported as well.
 // Instead only the necessary code has been extracted from the relevant files,
 // and further combined and reduced to limit the maintenance burden.
 
 // [Extracted from base/logging.h]