From 9c711e8fb47f337470103456e12a02c44b060181 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Wed, 11 May 2016 19:56:42 +1200 Subject: [PATCH] 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 --HG-- extra : rebase_source : 824c7a9841bce83c438decad48ce210f6c2a5571 --- dom/media/gmp/GMPLoader.cpp | 170 +--------------- dom/media/gmp/rlz/GMPDeviceBinding.cpp | 209 ++++++++++++++++++++ dom/media/gmp/rlz/GMPDeviceBinding.h | 22 +++ dom/media/gmp/rlz/mac/lib/machine_id_mac.cc | 1 + 4 files changed, 235 insertions(+), 167 deletions(-) create mode 100644 dom/media/gmp/rlz/GMPDeviceBinding.cpp create mode 100644 dom/media/gmp/rlz/GMPDeviceBinding.h diff --git a/dom/media/gmp/GMPLoader.cpp b/dom/media/gmp/GMPLoader.cpp index 8d9fd96f8b79..f3af6835076d 100644 --- a/dom/media/gmp/GMPLoader.cpp +++ b/dom/media/gmp/GMPLoader.cpp @@ -15,34 +15,9 @@ #ifdef XP_WIN #include "windows.h" -#ifdef MOZ_SANDBOX -#include -#include -#endif #endif -#ifdef XP_MACOSX -#include -#ifdef HASH_NODE_ID_WITH_DEVICE_ID -#include -#include -#include -#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 { @@ -144,94 +119,6 @@ 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(&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(__builtin_frame_address(0)); - *aOutTop = reinterpret_cast(stackFrame); - // Kernel code shows that stack is always a single region. - *aOutBottom = reinterpret_cast(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(start) + i; - *p = value; - } -} -#endif - bool GMPLoaderImpl::Load(const char* aUTF8LibPath, uint32_t aUTF8LibPathLen, @@ -241,59 +128,8 @@ GMPLoaderImpl::Load(const char* aUTF8LibPath, GMPAdapter* aAdapter) { std::string nodeId; -#ifdef HASH_NODE_ID_WITH_DEVICE_ID - if (aOriginSaltLen > 0) { - std::vector 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. diff --git a/dom/media/gmp/rlz/GMPDeviceBinding.cpp b/dom/media/gmp/rlz/GMPDeviceBinding.cpp new file mode 100644 index 000000000000..c8aee2bcd898 --- /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 + +#ifdef XP_WIN +#include "windows.h" +#ifdef MOZ_SANDBOX +#include +#include +#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 +#include +#endif +#endif + +#ifdef XP_MACOSX +#include +#ifdef HASH_NODE_ID_WITH_DEVICE_ID +#include +#include +#include +#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(&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(__builtin_frame_address(0)); + *aOutTop = reinterpret_cast(stackFrame); + // Kernel code shows that stack is always a single region. + *aOutBottom = reinterpret_cast(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(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 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 diff --git a/dom/media/gmp/rlz/GMPDeviceBinding.h b/dom/media/gmp/rlz/GMPDeviceBinding.h new file mode 100644 index 000000000000..835704054a8d --- /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 + +namespace mozilla { +namespace gmp { + +bool CalculateGMPDeviceId(char* aOriginSalt, + uint32_t aOriginSaltLen, + std::string& aOutNodeId); + +} // namespace gmp +} // namespace mozilla + +#endif // GMP_DEVICE_BINDING_h_ diff --git a/dom/media/gmp/rlz/mac/lib/machine_id_mac.cc b/dom/media/gmp/rlz/mac/lib/machine_id_mac.cc index 4b76a305438f..2bea0f55fe81 100644 --- a/dom/media/gmp/rlz/mac/lib/machine_id_mac.cc +++ b/dom/media/gmp/rlz/mac/lib/machine_id_mac.cc @@ -8,6 +8,7 @@ #include #include #include +#include // 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