fune/dom/plugins/ipc/PluginProcessParent.cpp
David Parks e931b523a6 Bug 1185472 - Only allow NPAPI HWNDs to be adopted by an HWND in the chrome process r=jmathies
Add the set of plugin process PIDs to PluginProcessParent and, when attempting to reparent plugin windows in the chrome process, validate that those windows originated with the plugin process (by checking the window's PID against the set in the PluginProcessParent).

--HG--
extra : rebase_source : f12fabb958d64def6f57ebbbccc39f8ef47ad9f4
2016-12-18 19:58:16 -08:00

287 lines
9.5 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 "mozilla/plugins/PluginProcessParent.h"
#include "base/string_util.h"
#include "base/process_util.h"
#include "mozilla/ipc/BrowserProcessSubThread.h"
#include "mozilla/plugins/PluginMessageUtils.h"
#include "mozilla/Telemetry.h"
#include "nsThreadUtils.h"
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
#include "nsDirectoryServiceDefs.h"
#endif
using std::vector;
using std::string;
using mozilla::ipc::BrowserProcessSubThread;
using mozilla::ipc::GeckoChildProcessHost;
using mozilla::plugins::LaunchCompleteTask;
using mozilla::plugins::PluginProcessParent;
using base::ProcessArchitecture;
PluginProcessParent::PidSet* PluginProcessParent::sPidSet = nullptr;
PluginProcessParent::PluginProcessParent(const std::string& aPluginFilePath) :
GeckoChildProcessHost(GeckoProcessType_Plugin),
mPluginFilePath(aPluginFilePath),
mTaskFactory(this),
mMainMsgLoop(MessageLoop::current()),
mRunCompleteTaskImmediately(false),
mChildPid(0)
{
}
PluginProcessParent::~PluginProcessParent()
{
#ifdef XP_WIN
if (sPidSet && mChildPid) {
sPidSet->RemoveEntry(mChildPid);
if (sPidSet->IsEmpty()) {
delete sPidSet;
sPidSet = nullptr;
}
}
#endif
}
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
static void
AddSandboxAllowedFile(vector<std::wstring>& aAllowedFiles, nsIProperties* aDirSvc,
const char* aDir, const nsAString& aSuffix = EmptyString())
{
nsCOMPtr<nsIFile> userDir;
nsresult rv = aDirSvc->Get(aDir, NS_GET_IID(nsIFile), getter_AddRefs(userDir));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsAutoString userDirPath;
rv = userDir->GetPath(userDirPath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (!aSuffix.IsEmpty()) {
userDirPath.Append(aSuffix);
}
aAllowedFiles.push_back(std::wstring(userDirPath.get()));
return;
}
static void
AddSandboxAllowedFiles(int32_t aSandboxLevel,
vector<std::wstring>& aAllowedFilesRead,
vector<std::wstring>& aAllowedFilesReadWrite,
vector<std::wstring>& aAllowedDirectories)
{
if (aSandboxLevel < 2) {
return;
}
nsresult rv;
nsCOMPtr<nsIProperties> dirSvc =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
// Higher than level 2 currently removes the users own rights.
if (aSandboxLevel > 2) {
AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR);
AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR,
NS_LITERAL_STRING("\\*"));
}
// Level 2 and above is now using low integrity, so we need to give write
// access to the Flash directories.
// This should be made Flash specific (Bug 1171396).
AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
NS_LITERAL_STRING("\\Macromedia\\Flash Player\\*"));
AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_LOCAL_APPDATA_DIR,
NS_LITERAL_STRING("\\Macromedia\\Flash Player\\*"));
AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
NS_LITERAL_STRING("\\Adobe\\Flash Player\\*"));
// Access also has to be given to create the parent directories as they may
// not exist.
AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
NS_LITERAL_STRING("\\Macromedia"));
AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
NS_LITERAL_STRING("\\Macromedia\\Flash Player"));
AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_LOCAL_APPDATA_DIR,
NS_LITERAL_STRING("\\Macromedia"));
AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_LOCAL_APPDATA_DIR,
NS_LITERAL_STRING("\\Macromedia\\Flash Player"));
AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
NS_LITERAL_STRING("\\Adobe"));
AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
NS_LITERAL_STRING("\\Adobe\\Flash Player"));
}
#endif
bool
PluginProcessParent::Launch(mozilla::UniquePtr<LaunchCompleteTask> aLaunchCompleteTask,
int32_t aSandboxLevel)
{
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
mSandboxLevel = aSandboxLevel;
AddSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead,
mAllowedFilesReadWrite, mAllowedDirectories);
#else
if (aSandboxLevel != 0) {
MOZ_ASSERT(false,
"Can't enable an NPAPI process sandbox for platform/build.");
}
#endif
ProcessArchitecture currentArchitecture = base::GetCurrentProcessArchitecture();
uint32_t containerArchitectures = GetSupportedArchitecturesForProcessType(GeckoProcessType_Plugin);
uint32_t pluginLibArchitectures = currentArchitecture;
#ifdef XP_MACOSX
nsresult rv = GetArchitecturesForBinary(mPluginFilePath.c_str(), &pluginLibArchitectures);
if (NS_FAILED(rv)) {
// If the call failed just assume that we want the current architecture.
pluginLibArchitectures = currentArchitecture;
}
#endif
ProcessArchitecture selectedArchitecture = currentArchitecture;
if (!(pluginLibArchitectures & containerArchitectures & currentArchitecture)) {
// Prefererence in order: x86_64, i386, PPC. The only particularly important thing
// about this order is that we'll prefer 64-bit architectures first.
if (base::PROCESS_ARCH_X86_64 & pluginLibArchitectures & containerArchitectures) {
selectedArchitecture = base::PROCESS_ARCH_X86_64;
}
else if (base::PROCESS_ARCH_I386 & pluginLibArchitectures & containerArchitectures) {
selectedArchitecture = base::PROCESS_ARCH_I386;
}
else if (base::PROCESS_ARCH_PPC & pluginLibArchitectures & containerArchitectures) {
selectedArchitecture = base::PROCESS_ARCH_PPC;
}
else if (base::PROCESS_ARCH_ARM & pluginLibArchitectures & containerArchitectures) {
selectedArchitecture = base::PROCESS_ARCH_ARM;
}
else if (base::PROCESS_ARCH_MIPS & pluginLibArchitectures & containerArchitectures) {
selectedArchitecture = base::PROCESS_ARCH_MIPS;
}
else {
return false;
}
}
mLaunchCompleteTask = mozilla::Move(aLaunchCompleteTask);
vector<string> args;
args.push_back(MungePluginDsoPath(mPluginFilePath));
bool result = AsyncLaunch(args, selectedArchitecture);
if (!result) {
mLaunchCompleteTask = nullptr;
}
return result;
}
void
PluginProcessParent::Delete()
{
MessageLoop* currentLoop = MessageLoop::current();
MessageLoop* ioLoop = XRE_GetIOMessageLoop();
if (currentLoop == ioLoop) {
delete this;
return;
}
ioLoop->PostTask(NewNonOwningRunnableMethod(this, &PluginProcessParent::Delete));
}
void
PluginProcessParent::SetCallRunnableImmediately(bool aCallImmediately)
{
mRunCompleteTaskImmediately = aCallImmediately;
}
/**
* This function exists so that we may provide an additional level of
* indirection between the task being posted to main event loop (a
* RunnableMethod) and the launch complete task itself. This is needed
* for cases when both WaitUntilConnected or OnChannel* race to invoke the
* task.
*/
void
PluginProcessParent::RunLaunchCompleteTask()
{
if (mLaunchCompleteTask) {
mLaunchCompleteTask->Run();
mLaunchCompleteTask = nullptr;
}
}
bool
PluginProcessParent::WaitUntilConnected(int32_t aTimeoutMs)
{
bool result = GeckoChildProcessHost::WaitUntilConnected(aTimeoutMs);
if (mRunCompleteTaskImmediately && mLaunchCompleteTask) {
if (result) {
mLaunchCompleteTask->SetLaunchSucceeded();
}
RunLaunchCompleteTask();
}
return result;
}
void
PluginProcessParent::OnChannelConnected(int32_t peer_pid)
{
#ifdef XP_WIN
mChildPid = static_cast<uint32_t>(peer_pid);
if (!sPidSet) {
sPidSet = new PluginProcessParent::PidSet();
}
sPidSet->PutEntry(mChildPid);
#endif
GeckoChildProcessHost::OnChannelConnected(peer_pid);
if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
mLaunchCompleteTask->SetLaunchSucceeded();
mMainMsgLoop->PostTask(mTaskFactory.NewRunnableMethod(
&PluginProcessParent::RunLaunchCompleteTask));
}
}
void
PluginProcessParent::OnChannelError()
{
GeckoChildProcessHost::OnChannelError();
if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
mMainMsgLoop->PostTask(mTaskFactory.NewRunnableMethod(
&PluginProcessParent::RunLaunchCompleteTask));
}
}
bool
PluginProcessParent::IsConnected()
{
mozilla::MonitorAutoLock lock(mMonitor);
return mProcessState == PROCESS_CONNECTED;
}
bool
PluginProcessParent::IsPluginProcessId(base::ProcessId procId) {
#ifdef XP_WIN
MOZ_ASSERT(XRE_IsParentProcess());
return sPidSet && sPidSet->Contains(static_cast<uint32_t>(procId));
#else
NS_ERROR("IsPluginProcessId not available on this platform.");
return false;
#endif
}