Mypal/dom/media/gmp/GMPLoader.cpp

210 lines
5.4 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: sw=4 ts=4 et :
* 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 "GMPLoader.h"
#include <stdio.h>
#include "mozilla/Attributes.h"
#include "gmp-entrypoints.h"
#include "prlink.h"
#include "prenv.h"
#include <string>
#ifdef XP_WIN
#include "windows.h"
#endif
#include "GMPDeviceBinding.h"
namespace mozilla {
namespace gmp {
class GMPLoaderImpl : public GMPLoader {
public:
explicit GMPLoaderImpl(SandboxStarter* aStarter)
: mSandboxStarter(aStarter)
, mAdapter(nullptr)
{}
virtual ~GMPLoaderImpl() {}
bool Load(const char* aUTF8LibPath,
uint32_t aUTF8LibPathLen,
char* aOriginSalt,
uint32_t aOriginSaltLen,
const GMPPlatformAPI* aPlatformAPI,
GMPAdapter* aAdapter) override;
GMPErr GetAPI(const char* aAPIName,
void* aHostAPI,
void** aPluginAPI,
uint32_t aDecryptorId) override;
void Shutdown() override;
private:
SandboxStarter* mSandboxStarter;
UniquePtr<GMPAdapter> mAdapter;
};
UniquePtr<GMPLoader> CreateGMPLoader(SandboxStarter* aStarter) {
return MakeUnique<GMPLoaderImpl>(aStarter);
}
class PassThroughGMPAdapter : public GMPAdapter {
public:
~PassThroughGMPAdapter() {
// Ensure we're always shutdown, even if caller forgets to call GMPShutdown().
GMPShutdown();
}
void SetAdaptee(PRLibrary* aLib) override
{
mLib = aLib;
}
GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) override
{
if (!mLib) {
return GMPGenericErr;
}
GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
if (!initFunc) {
return GMPNotImplementedErr;
}
return initFunc(aPlatformAPI);
}
GMPErr GMPGetAPI(const char* aAPIName,
void* aHostAPI,
void** aPluginAPI,
uint32_t aDecryptorId) override
{
if (!mLib) {
return GMPGenericErr;
}
GMPGetAPIFunc getapiFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
if (!getapiFunc) {
return GMPNotImplementedErr;
}
return getapiFunc(aAPIName, aHostAPI, aPluginAPI);
}
void GMPShutdown() override
{
if (mLib) {
GMPShutdownFunc shutdownFunc = reinterpret_cast<GMPShutdownFunc>(PR_FindFunctionSymbol(mLib, "GMPShutdown"));
if (shutdownFunc) {
shutdownFunc();
}
PR_UnloadLibrary(mLib);
mLib = nullptr;
}
}
void GMPSetNodeId(const char* aNodeId, uint32_t aLength) override
{
if (!mLib) {
return;
}
GMPSetNodeIdFunc setNodeIdFunc = reinterpret_cast<GMPSetNodeIdFunc>(PR_FindFunctionSymbol(mLib, "GMPSetNodeId"));
if (setNodeIdFunc) {
setNodeIdFunc(aNodeId, aLength);
}
}
private:
PRLibrary* mLib = nullptr;
};
bool
GMPLoaderImpl::Load(const char* aUTF8LibPath,
uint32_t aUTF8LibPathLen,
char* aOriginSalt,
uint32_t aOriginSaltLen,
const GMPPlatformAPI* aPlatformAPI,
GMPAdapter* aAdapter)
{
std::string nodeId;
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;
}
// Load the GMP.
PRLibSpec libSpec;
#ifdef XP_WIN
int pathLen = MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, nullptr, 0);
if (pathLen == 0) {
return false;
}
auto widePath = MakeUnique<wchar_t[]>(pathLen);
if (MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, widePath.get(), pathLen) == 0) {
return false;
}
libSpec.value.pathname_u = widePath.get();
libSpec.type = PR_LibSpec_PathnameU;
#else
libSpec.value.pathname = aUTF8LibPath;
libSpec.type = PR_LibSpec_Pathname;
#endif
PRLibrary* lib = PR_LoadLibraryWithFlags(libSpec, 0);
if (!lib) {
return false;
}
GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(lib, "GMPInit"));
if ((initFunc && aAdapter) ||
(!initFunc && !aAdapter)) {
// Ensure that if we're dealing with a GMP we do *not* use an adapter
// provided from the outside world. This is important as it means we
// don't call code not covered by Adobe's plugin-container voucher
// before we pass the node Id to Adobe's GMP.
return false;
}
// Note: PassThroughGMPAdapter's code must remain in this file so that it's
// covered by Adobe's plugin-container voucher.
mAdapter.reset((!aAdapter) ? new PassThroughGMPAdapter() : aAdapter);
mAdapter->SetAdaptee(lib);
if (mAdapter->GMPInit(aPlatformAPI) != GMPNoErr) {
return false;
}
mAdapter->GMPSetNodeId(nodeId.c_str(), nodeId.size());
return true;
}
GMPErr
GMPLoaderImpl::GetAPI(const char* aAPIName,
void* aHostAPI,
void** aPluginAPI,
uint32_t aDecryptorId)
{
return mAdapter->GMPGetAPI(aAPIName, aHostAPI, aPluginAPI, aDecryptorId);
}
void
GMPLoaderImpl::Shutdown()
{
if (mAdapter) {
mAdapter->GMPShutdown();
}
}
} // namespace gmp
} // namespace mozilla