Bug 1382883 - Pass paths and open file handles to CDM host binaries on CDM startup. r=gerald

MozReview-Commit-ID: 9IhRqlFrNJf

--HG--
extra : source : de04ea0a90ae935bbc1d0f730332b034b5514f17
extra : intermediate-source : b18daff94ad3d832fcbd601d219c6db2eae46f90
This commit is contained in:
Chris Pearce 2017-07-27 12:50:46 +12:00
Родитель 93ac0cd0e3
Коммит 0637427899
5 изменённых файлов: 351 добавлений и 3 удалений

Просмотреть файл

@ -5,6 +5,7 @@
#include "ChromiumCDMAdapter.h"
#include "content_decryption_module.h"
#include "content_decryption_module_ext.h"
#include "VideoUtils.h"
#include "gmp-api/gmp-entrypoints.h"
#include "gmp-api/gmp-decryption.h"
@ -12,12 +13,25 @@
#include "gmp-api/gmp-platform.h"
#include "WidevineUtils.h"
#include "GMPLog.h"
#include "mozilla/Move.h"
#if defined(XP_MACOSX) || defined(XP_LINUX)
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#endif
// Declared in WidevineAdapter.cpp.
extern const GMPPlatformAPI* sPlatform;
namespace mozilla {
ChromiumCDMAdapter::ChromiumCDMAdapter(nsTArray<nsCString>&& aHostFilePaths)
{
PopulateHostFiles(Move(aHostFilePaths));
}
void
ChromiumCDMAdapter::SetAdaptee(PRLibrary* aLib)
{
@ -37,6 +51,14 @@ ChromiumCdmHost(int aHostInterfaceVersion, void* aUserData)
#define STRINGIFY(s) _STRINGIFY(s)
#define _STRINGIFY(s) #s
static cdm::HostFile
TakeToCDMHostFile(HostFileData& aHostFileData)
{
return cdm::HostFile(aHostFileData.mBinary.Path().get(),
aHostFileData.mBinary.TakePlatformFile(),
aHostFileData.mSig.TakePlatformFile());
}
GMPErr
ChromiumCDMAdapter::GMPInit(const GMPPlatformAPI* aPlatformAPI)
{
@ -46,6 +68,19 @@ ChromiumCDMAdapter::GMPInit(const GMPPlatformAPI* aPlatformAPI)
return GMPGenericErr;
}
// Note: we must call the VerifyCdmHost_0 function if it's present before
// we call the initialize function.
auto verify = reinterpret_cast<decltype(::VerifyCdmHost_0)*>(
PR_FindFunctionSymbol(mLib, STRINGIFY(VerifyCdmHost_0)));
if (verify) {
nsTArray<cdm::HostFile> files;
for (HostFileData& hostFile : mHostFiles) {
files.AppendElement(TakeToCDMHostFile(hostFile));
}
bool result = verify(files.Elements(), files.Length());
GMP_LOG("%s VerifyCdmHost_0 returned %d", __func__, result);
}
auto init = reinterpret_cast<decltype(::INITIALIZE_CDM_MODULE)*>(
PR_FindFunctionSymbol(mLib, STRINGIFY(INITIALIZE_CDM_MODULE)));
if (!init) {
@ -130,4 +165,64 @@ ChromiumCDMAdapter::Supports(int32_t aModuleVersion,
aHostVersion == cdm::Host_8::kVersion;
}
HostFile::HostFile(HostFile&& aOther)
: mPath(aOther.mPath)
, mFile(aOther.TakePlatformFile())
{
}
HostFile::~HostFile()
{
if (mFile != cdm::kInvalidPlatformFile) {
#ifdef XP_WIN
CloseHandle(mFile);
#else
close(mFile);
#endif
mFile = cdm::kInvalidPlatformFile;
}
}
#ifdef XP_WIN
HostFile::HostFile(const nsCString& aPath)
: mPath(NS_ConvertUTF8toUTF16(aPath))
{
HANDLE handle = CreateFileW(mPath.get(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
mFile = (handle == INVALID_HANDLE_VALUE) ? cdm::kInvalidPlatformFile : handle;
}
#endif
#if defined(XP_MACOSX) || defined(XP_LINUX)
HostFile::HostFile(const nsCString& aPath)
: mPath(aPath)
{
// Note: open() returns -1 on failure; i.e. kInvalidPlatformFile.
mFile = open(aPath.get(), O_RDONLY);
}
#endif
cdm::PlatformFile
HostFile::TakePlatformFile()
{
cdm::PlatformFile f = mFile;
mFile = cdm::kInvalidPlatformFile;
return f;
}
void
ChromiumCDMAdapter::PopulateHostFiles(nsTArray<nsCString>&& aHostFilePaths)
{
for (const nsCString& path : aHostFilePaths) {
mHostFiles.AppendElement(
HostFileData(mozilla::HostFile(path),
mozilla::HostFile(path + NS_LITERAL_CSTRING(".sig"))));
}
}
} // namespace mozilla

Просмотреть файл

@ -9,14 +9,59 @@
#include "GMPLoader.h"
#include "prlink.h"
#include "GMPUtils.h"
#include "nsTArray.h"
#include "content_decryption_module_ext.h"
#include "nsString.h"
struct GMPPlatformAPI;
namespace mozilla {
#if defined(OS_WIN)
typedef nsString HostFileString;
#else
typedef nsCString HostFileString;
#endif
class HostFile
{
public:
explicit HostFile(const nsCString& aPath);
HostFile(HostFile&& aOther);
~HostFile();
const HostFileString& Path() const { return mPath; }
cdm::PlatformFile TakePlatformFile();
private:
const HostFileString mPath;
cdm::PlatformFile mFile = cdm::kInvalidPlatformFile;
};
struct HostFileData
{
HostFileData(HostFile&& aBinary, HostFile&& aSig)
: mBinary(Move(aBinary))
, mSig(Move(aSig))
{
}
HostFileData(HostFileData&& aOther)
: mBinary(Move(aOther.mBinary))
, mSig(Move(aOther.mSig))
{
}
~HostFileData() {}
HostFile mBinary;
HostFile mSig;
};
class ChromiumCDMAdapter : public gmp::GMPAdapter
{
public:
explicit ChromiumCDMAdapter(nsTArray<nsCString>&& aHostFilePaths);
void SetAdaptee(PRLibrary* aLib) override;
@ -33,7 +78,10 @@ public:
int32_t aHostVersion);
private:
void PopulateHostFiles(nsTArray<nsCString>&& aHostFilePaths);
PRLibrary* mLib = nullptr;
nsTArray<HostFileData> mHostFiles;
};
} // namespace mozilla

Просмотреть файл

@ -22,13 +22,16 @@
#include "GMPUtils.h"
#include "prio.h"
#include "base/task.h"
#include "base/command_line.h"
#include "widevine-adapter/WidevineAdapter.h"
#include "ChromiumCDMAdapter.h"
#include "GMPLog.h"
using namespace mozilla::ipc;
#ifdef XP_WIN
#include <stdlib.h> // for _exit()
#include "WinUtils.h"
#else
#include <unistd.h> // for _exit()
#endif
@ -118,7 +121,6 @@ GetPluginFile(const nsAString& aPluginPath,
return true;
}
#if !defined(XP_MACOSX) || !defined(MOZ_GMP_SANDBOX)
static bool
GetPluginFile(const nsAString& aPluginPath,
nsCOMPtr<nsIFile>& aLibFile)
@ -126,7 +128,6 @@ GetPluginFile(const nsAString& aPluginPath,
nsCOMPtr<nsIFile> unusedlibDir;
return GetPluginFile(aPluginPath, unusedlibDir, aLibFile);
}
#endif
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
static nsCString
@ -296,6 +297,25 @@ GMPChild::RecvPreloadLibs(const nsCString& aLibs)
return IPC_OK();
}
bool
GMPChild::ResolveLinks(nsCOMPtr<nsIFile>& aPath)
{
#if defined(XP_WIN)
return widget::WinUtils::ResolveJunctionPointsAndSymLinks(aPath);
#elif defined(XP_MACOSX)
nsCString targetPath = GetNativeTarget(aPath);
nsCOMPtr<nsIFile> newFile;
if (NS_FAILED(
NS_NewNativeLocalFile(targetPath, true, getter_AddRefs(newFile)))) {
return false;
}
aPath = newFile;
return true;
#else
return true;
#endif
}
bool
GMPChild::GetUTF8LibPath(nsACString& aOutLibPath)
{
@ -325,6 +345,117 @@ GMPChild::GetUTF8LibPath(nsACString& aOutLibPath)
#endif
}
#if defined(XP_MACOSX)
#define FIREFOX_FILE NS_LITERAL_STRING("firefox")
#define XUL_LIB_FILE NS_LITERAL_STRING("XUL")
#elif defined(XP_LINUX)
#define FIREFOX_FILE NS_LITERAL_STRING("firefox")
#define XUL_LIB_FILE NS_LITERAL_STRING("libxul.so")
#elif defined(OS_WIN)
#define FIREFOX_FILE NS_LITERAL_STRING("firefox.exe")
#define XUL_LIB_FILE NS_LITERAL_STRING("xul.dll")
#endif
#if defined(XP_MACOSX)
static bool
GetFirefoxAppPath(nsCOMPtr<nsIFile> aPluginContainerPath,
nsCOMPtr<nsIFile>& aOutFirefoxAppPath)
{
// aPluginContainerPath will end with something like:
// xxxx/NightlyDebug.app/Contents/MacOS/plugin-container.app/Contents/MacOS/plugin-container
MOZ_ASSERT(aPluginContainerPath);
nsCOMPtr<nsIFile> path = aPluginContainerPath;
for (int i = 0; i < 4; i++) {
nsCOMPtr<nsIFile> parent;
if (NS_FAILED(path->GetParent(getter_AddRefs(parent)))) {
return false;
}
path = parent;
}
MOZ_ASSERT(path);
aOutFirefoxAppPath = path;
return true;
}
#endif
nsTArray<nsCString>
GMPChild::MakeCDMHostVerificationPaths()
{
nsTArray<nsCString> paths;
// Plugin binary path.
nsCOMPtr<nsIFile> path;
nsString str;
if (GetPluginFile(mPluginPath, path) && FileExists(path) &&
ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
paths.AppendElement(NS_ConvertUTF16toUTF8(str));
}
// Plugin-container binary path.
// Note: clang won't let us initialize an nsString from a wstring, so we
// need to go through UTF8 to get to an nsString.
const std::string pluginContainer =
WideToUTF8(CommandLine::ForCurrentProcess()->program());
path = nullptr;
str = NS_ConvertUTF8toUTF16(nsDependentCString(pluginContainer.c_str()));
if (NS_SUCCEEDED(NS_NewLocalFile(str,
true, /* aFollowLinks */
getter_AddRefs(path))) &&
FileExists(path) && ResolveLinks(path) &&
NS_SUCCEEDED(path->GetPath(str))) {
paths.AppendElement(nsCString(NS_ConvertUTF16toUTF8(str)));
} else {
// Without successfully determining plugin-container's path, we can't
// determine libxul's or Firefox's. So give up.
return paths;
}
// Firefox application binary path.
nsCOMPtr<nsIFile> appDir;
#if defined(XP_WIN) || defined(XP_LINUX)
// Note: re-using 'path' var here, as on Windows/Linux we assume Firefox
// executable is in the same directory as plugin-container.
if (NS_SUCCEEDED(path->GetParent(getter_AddRefs(appDir))) &&
NS_SUCCEEDED(appDir->Clone(getter_AddRefs(path))) &&
NS_SUCCEEDED(path->Append(FIREFOX_FILE)) && FileExists(path) &&
ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
paths.AppendElement(NS_ConvertUTF16toUTF8(str));
}
#elif defined(XP_MACOSX)
// On MacOS the firefox binary is a few parent directories up from
// plugin-container.
if (GetFirefoxAppPath(path, appDir) &&
NS_SUCCEEDED(appDir->Clone(getter_AddRefs(path))) &&
NS_SUCCEEDED(path->Append(FIREFOX_FILE)) && FileExists(path) &&
ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
paths.AppendElement(NS_ConvertUTF16toUTF8(str));
}
#endif
// Libxul path. Note: re-using 'path' var here, as we assume libxul is in
// the same directory as Firefox executable.
appDir->GetPath(str);
if (NS_SUCCEEDED(appDir->Clone(getter_AddRefs(path))) &&
NS_SUCCEEDED(path->Append(XUL_LIB_FILE)) && FileExists(path) &&
ResolveLinks(path) && NS_SUCCEEDED(path->GetPath(str))) {
paths.AppendElement(NS_ConvertUTF16toUTF8(str));
}
return paths;
}
static nsCString
ToCString(const nsTArray<nsCString>& aStrings)
{
nsCString result;
for (const nsCString& s : aStrings) {
if (!result.IsEmpty()) {
result.AppendLiteral(",");
}
result.Append(s);
}
return result;
}
mozilla::ipc::IPCResult
GMPChild::AnswerStartPlugin(const nsString& aAdapter)
{
@ -365,7 +496,9 @@ GMPChild::AnswerStartPlugin(const nsString& aAdapter)
if (isWidevine) {
adapter = new WidevineAdapter();
} else if (isChromium) {
adapter = new ChromiumCDMAdapter();
nsTArray<nsCString> paths(MakeCDMHostVerificationPaths());
GMP_LOG("%s CDM host paths=%s", __func__, ToCString(paths).get());
adapter = new ChromiumCDMAdapter(Move(paths));
}
if (!mGMPLoader->Load(libPath.get(),

Просмотреть файл

@ -41,6 +41,8 @@ public:
private:
friend class GMPContentChild;
bool ResolveLinks(nsCOMPtr<nsIFile>& aPath);
bool GetUTF8LibPath(nsACString& aOutLibPath);
mozilla::ipc::IPCResult AnswerStartPlugin(const nsString& aAdapter) override;
@ -64,6 +66,8 @@ private:
GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI, uint32_t aDecryptorId = 0);
nsTArray<nsCString> MakeCDMHostVerificationPaths();
nsTArray<UniquePtr<GMPContentChild>> mGMPContentChildren;
RefPtr<GMPTimerChild> mTimerChild;

Просмотреть файл

@ -17,6 +17,7 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include "ClearKeyCDM.h"
#include "ClearKeySessionManager.h"
@ -24,6 +25,13 @@
// on Unix systems.
#include "stddef.h"
#include "content_decryption_module.h"
#include "content_decryption_module_ext.h"
#if defined(XP_MACOSX) || defined(XP_LINUX)
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#ifdef ENABLE_WMF
#include "WMFUtils.h"
@ -36,6 +44,8 @@ void INITIALIZE_CDM_MODULE() {
}
static bool sCanReadHostVerificationFiles = false;
CDM_API
void* CreateCdmInstance(int cdm_interface_version,
const char* key_system,
@ -53,6 +63,11 @@ void* CreateCdmInstance(int cdm_interface_version,
}
#endif
// Test that we're able to read the host files.
if (!sCanReadHostVerificationFiles) {
return nullptr;
}
cdm::Host_8* host = static_cast<cdm::Host_8*>(
get_cdm_host_func(cdm_interface_version, user_data));
ClearKeyCDM* clearKey = new ClearKeyCDM(host);
@ -61,4 +76,57 @@ void* CreateCdmInstance(int cdm_interface_version,
return clearKey;
}
const size_t TEST_READ_SIZE = 16 * 1024;
bool
CanReadSome(cdm::PlatformFile aFile)
{
vector<uint8_t> data;
data.resize(TEST_READ_SIZE);
#ifdef XP_WIN
DWORD bytesRead = 0;
return ReadFile(aFile, &data.front(), TEST_READ_SIZE, &bytesRead, nullptr) &&
bytesRead > 0;
#elif defined(XP_MACOSX) || defined(XP_LINUX)
return read(aFile, &data.front(), TEST_READ_SIZE) > 0;
#else
return false;
#endif
}
void
ClosePlatformFile(cdm::PlatformFile aFile)
{
#ifdef XP_WIN
CloseHandle(aFile);
#elif defined(XP_MACOSX) || defined(XP_LINUX)
close(aFile);
#endif
}
CDM_API
bool
VerifyCdmHost_0(const cdm::HostFile* aHostFiles, uint32_t aNumFiles)
{
// We expect 4 binaries: clearkey, libxul, plugin-container, and Firefox.
bool rv = (aNumFiles == 4);
// Verify that each binary is readable inside the sandbox,
// and close the handle.
for (uint32_t i = 0; i < aNumFiles; i++) {
const cdm::HostFile& hostFile = aHostFiles[i];
if (hostFile.file != cdm::kInvalidPlatformFile) {
if (!CanReadSome(hostFile.file)) {
rv = false;
}
ClosePlatformFile(hostFile.file);
}
if (hostFile.sig_file != cdm::kInvalidPlatformFile) {
ClosePlatformFile(hostFile.sig_file);
}
}
sCanReadHostVerificationFiles = rv;
return rv;
}
} // extern "C".