/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: sw=2 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 #include "mozilla/Attributes.h" #include "nsExceptionHandler.h" #include "gmp-entrypoints.h" #include "prlink.h" #include "prenv.h" #include "prerror.h" #if defined(XP_WIN) && defined(MOZ_SANDBOX) # include "mozilla/sandboxTarget.h" # include "mozilla/sandboxing/SandboxInitialization.h" # include "mozilla/sandboxing/sandboxLogging.h" #endif #if defined(XP_LINUX) && defined(MOZ_SANDBOX) # include "mozilla/Sandbox.h" # include "mozilla/SandboxInfo.h" #endif #include #ifdef XP_WIN # include #endif namespace mozilla::gmp { class PassThroughGMPAdapter : public GMPAdapter { public: ~PassThroughGMPAdapter() override { // 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 (NS_WARN_IF(!mLib)) { MOZ_CRASH("Missing library!"); return GMPGenericErr; } GMPInitFunc initFunc = reinterpret_cast(PR_FindFunctionSymbol(mLib, "GMPInit")); if (!initFunc) { MOZ_CRASH("Missing init method!"); return GMPNotImplementedErr; } return initFunc(aPlatformAPI); } GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI, const nsACString& /* aKeySystem */) override { if (!mLib) { return GMPGenericErr; } GMPGetAPIFunc getapiFunc = reinterpret_cast( PR_FindFunctionSymbol(mLib, "GMPGetAPI")); if (!getapiFunc) { return GMPNotImplementedErr; } return getapiFunc(aAPIName, aHostAPI, aPluginAPI); } void GMPShutdown() override { if (mLib) { GMPShutdownFunc shutdownFunc = reinterpret_cast( PR_FindFunctionSymbol(mLib, "GMPShutdown")); if (shutdownFunc) { shutdownFunc(); } PR_UnloadLibrary(mLib); mLib = nullptr; } } private: PRLibrary* mLib = nullptr; }; bool GMPLoader::Load(const char* aUTF8LibPath, uint32_t aUTF8LibPathLen, const GMPPlatformAPI* aPlatformAPI, GMPAdapter* aAdapter) { CrashReporter::AutoAnnotateCrashReport autoLibPath( CrashReporter::Annotation::GMPLibraryPath, nsDependentCString(aUTF8LibPath)); if (!getenv("MOZ_DISABLE_GMP_SANDBOX") && mSandboxStarter && !mSandboxStarter->Start(aUTF8LibPath)) { MOZ_CRASH("Cannot start sandbox!"); return false; } // Load the GMP. PRLibSpec libSpec; #ifdef XP_WIN int pathLen = MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, nullptr, 0); if (pathLen == 0) { MOZ_CRASH("Cannot get path length as wide char!"); return false; } auto widePath = MakeUnique(pathLen); if (MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, widePath.get(), pathLen) == 0) { MOZ_CRASH("Cannot convert path to wide char!"); 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) { MOZ_CRASH_UNSAFE_PRINTF("Cannot load plugin as library %d %d", PR_GetError(), PR_GetOSError()); return false; } mAdapter.reset((!aAdapter) ? new PassThroughGMPAdapter() : aAdapter); mAdapter->SetAdaptee(lib); if (mAdapter->GMPInit(aPlatformAPI) != GMPNoErr) { MOZ_CRASH("Cannot initialize plugin adapter!"); return false; } return true; } GMPErr GMPLoader::GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI, const nsACString& aKeySystem) { return mAdapter->GMPGetAPI(aAPIName, aHostAPI, aPluginAPI, aKeySystem); } void GMPLoader::Shutdown() { if (mAdapter) { mAdapter->GMPShutdown(); } } #if defined(XP_WIN) && defined(MOZ_SANDBOX) class WinSandboxStarter : public mozilla::gmp::SandboxStarter { public: bool Start(const char* aLibPath) override { // Cause advapi32 to load before the sandbox is turned on, as // Widevine version 970 and later require it and the sandbox // blocks it on Win7. unsigned int dummy_rand; rand_s(&dummy_rand); mozilla::SandboxTarget::Instance()->StartSandbox(); return true; } }; #endif #if defined(XP_LINUX) && defined(MOZ_SANDBOX) namespace { class LinuxSandboxStarter : public mozilla::gmp::SandboxStarter { private: LinuxSandboxStarter() = default; friend mozilla::detail::UniqueSelector::SingleObject mozilla::MakeUnique(); public: static UniquePtr Make() { if (mozilla::SandboxInfo::Get().CanSandboxMedia()) { return MakeUnique(); } // Sandboxing isn't possible, but the parent has already // checked that this plugin doesn't require it. (Bug 1074561) return nullptr; } bool Start(const char* aLibPath) override { mozilla::SetMediaPluginSandbox(aLibPath); return true; } }; } // anonymous namespace #endif // XP_LINUX && MOZ_SANDBOX static UniquePtr MakeSandboxStarter() { #if defined(XP_WIN) && defined(MOZ_SANDBOX) return mozilla::MakeUnique(); #elif defined(XP_LINUX) && defined(MOZ_SANDBOX) return LinuxSandboxStarter::Make(); #else return nullptr; #endif } GMPLoader::GMPLoader() : mSandboxStarter(MakeSandboxStarter()) {} bool GMPLoader::CanSandbox() const { return !!mSandboxStarter; } } // namespace mozilla::gmp