blob: 4aeee002b25eb55924b96aab1a4427b789e6f74b [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/nacl/browser/nacl_host_message_filter.h"
#include <stddef.h>
#include <stdint.h>
#include <utility>
#include "base/bind.h"
#include "base/system/sys_info.h"
#include "base/task/post_task.h"
#include "build/build_config.h"
#include "components/nacl/browser/bad_message.h"
#include "components/nacl/browser/nacl_browser.h"
#include "components/nacl/browser/nacl_file_host.h"
#include "components/nacl/browser/nacl_process_host.h"
#include "components/nacl/browser/pnacl_host.h"
#include "components/nacl/common/buildflags.h"
#include "components/nacl/common/nacl_host_messages.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "ipc/ipc_platform_file.h"
#include "ppapi/shared_impl/ppapi_permissions.h"
#include "url/gurl.h"
namespace nacl {
namespace {
// The maximum number of resource file handles the browser process accepts. Use
// 200 because ARC's nmf has ~128 resource files as of May 2015. This prevents
// untrusted code filling the FD/handle table.
const size_t kMaxPreOpenResourceFiles = 200;
ppapi::PpapiPermissions GetNaClPermissions(
uint32_t permission_bits,
content::BrowserContext* browser_context,
const GURL& document_url) {
// Don't grant any special permissions to NaCl plugins. We don't want
// a compromised renderer to be able to start a NaCl plugin with Dev or Flash
// permissions which may expand the surface area of the sandbox.
uint32_t nacl_permissions = ppapi::PERMISSION_NONE;
if (content::PluginService::GetInstance()->PpapiDevChannelSupported(
browser_context, document_url))
nacl_permissions |= ppapi::PERMISSION_DEV_CHANNEL;
return ppapi::PpapiPermissions::GetForCommandLine(nacl_permissions);
}
ppapi::PpapiPermissions GetPpapiPermissions(uint32_t permission_bits,
int render_process_id,
int render_view_id) {
// We get the URL from WebContents from the RenderViewHost, since we don't
// have a BrowserPpapiHost yet.
content::RenderProcessHost* host =
content::RenderProcessHost::FromID(render_process_id);
content::RenderViewHost* view_host =
content::RenderViewHost::FromID(render_process_id, render_view_id);
if (!view_host)
return ppapi::PpapiPermissions();
GURL document_url;
content::WebContents* contents =
content::WebContents::FromRenderViewHost(view_host);
if (contents)
document_url = contents->GetLastCommittedURL();
return GetNaClPermissions(permission_bits,
host->GetBrowserContext(),
document_url);
}
} // namespace
NaClHostMessageFilter::NaClHostMessageFilter(
int render_process_id,
bool is_off_the_record,
const base::FilePath& profile_directory)
: BrowserMessageFilter(NaClHostMsgStart),
render_process_id_(render_process_id),
off_the_record_(is_off_the_record),
profile_directory_(profile_directory),
weak_ptr_factory_(this) {}
NaClHostMessageFilter::~NaClHostMessageFilter() {
}
void NaClHostMessageFilter::OnChannelClosing() {
pnacl::PnaclHost::GetInstance()->RendererClosing(render_process_id_);
}
bool NaClHostMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(NaClHostMessageFilter, message)
#if BUILDFLAG(ENABLE_NACL)
IPC_MESSAGE_HANDLER_DELAY_REPLY(NaClHostMsg_LaunchNaCl, OnLaunchNaCl)
IPC_MESSAGE_HANDLER_DELAY_REPLY(NaClHostMsg_GetReadonlyPnaclFD,
OnGetReadonlyPnaclFd)
IPC_MESSAGE_HANDLER_DELAY_REPLY(NaClHostMsg_NaClCreateTemporaryFile,
OnNaClCreateTemporaryFile)
IPC_MESSAGE_HANDLER(NaClHostMsg_NexeTempFileRequest,
OnGetNexeFd)
IPC_MESSAGE_HANDLER(NaClHostMsg_ReportTranslationFinished,
OnTranslationFinished)
IPC_MESSAGE_HANDLER(NaClHostMsg_MissingArchError,
OnMissingArchError)
IPC_MESSAGE_HANDLER_DELAY_REPLY(NaClHostMsg_OpenNaClExecutable,
OnOpenNaClExecutable)
IPC_MESSAGE_HANDLER(NaClHostMsg_NaClGetNumProcessors,
OnNaClGetNumProcessors)
IPC_MESSAGE_HANDLER(NaClHostMsg_NaClDebugEnabledForURL,
OnNaClDebugEnabledForURL)
#endif
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void NaClHostMessageFilter::OnLaunchNaCl(
const nacl::NaClLaunchParams& launch_params,
IPC::Message* reply_msg) {
// If we're running llc or ld for the PNaCl translator, we don't need to look
// up permissions, and we don't have the right browser state to look up some
// of the whitelisting parameters anyway.
if (launch_params.process_type == kPNaClTranslatorProcessType) {
uint32_t perms = launch_params.permission_bits & ppapi::PERMISSION_DEV;
LaunchNaClContinuationOnIOThread(
launch_params,
reply_msg,
std::vector<NaClResourcePrefetchResult>(),
ppapi::PpapiPermissions(perms));
return;
}
base::PostTaskWithTraits(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&NaClHostMessageFilter::LaunchNaClContinuation, this,
launch_params, reply_msg));
}
void NaClHostMessageFilter::LaunchNaClContinuation(
const nacl::NaClLaunchParams& launch_params,
IPC::Message* reply_msg) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
ppapi::PpapiPermissions permissions =
GetPpapiPermissions(launch_params.permission_bits,
render_process_id_,
launch_params.render_view_id);
content::RenderViewHost* rvh = content::RenderViewHost::FromID(
render_process_id(), launch_params.render_view_id);
if (!rvh) {
bad_message::ReceivedBadMessage(
this, bad_message::NHMF_LAUNCH_CONTINUATION_BAD_ROUTING_ID);
delete reply_msg;
return;
}
nacl::NaClLaunchParams safe_launch_params(launch_params);
safe_launch_params.resource_prefetch_request_list.clear();
// TODO(yusukes): Fix NaClProcessHost::~NaClProcessHost() and remove the
// ifdef.
#if !defined(OS_WIN)
const std::vector<NaClResourcePrefetchRequest>& original_request_list =
launch_params.resource_prefetch_request_list;
content::SiteInstance* site_instance = rvh->GetSiteInstance();
for (size_t i = 0; i < original_request_list.size(); ++i) {
GURL gurl(original_request_list[i].resource_url);
// Important security check: Do the same check as OpenNaClExecutable()
// in nacl_file_host.cc.
if (!site_instance->IsSameSiteWithURL(gurl))
continue;
safe_launch_params.resource_prefetch_request_list.push_back(
original_request_list[i]);
}
#endif
// Process a list of resource file URLs in
// |launch_params.resource_files_to_prefetch|.
base::PostTaskWithTraits(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&NaClHostMessageFilter::BatchOpenResourceFiles, this,
safe_launch_params, reply_msg, permissions));
}
void NaClHostMessageFilter::BatchOpenResourceFiles(
const nacl::NaClLaunchParams& launch_params,
IPC::Message* reply_msg,
ppapi::PpapiPermissions permissions) {
std::vector<NaClResourcePrefetchResult> prefetched_resource_files;
const std::vector<NaClResourcePrefetchRequest>& request_list =
launch_params.resource_prefetch_request_list;
for (size_t i = 0; i < request_list.size(); ++i) {
GURL gurl(request_list[i].resource_url);
base::FilePath file_path_metadata;
if (!nacl::NaClBrowser::GetDelegate()->MapUrlToLocalFilePath(
gurl,
true, // use_blocking_api
profile_directory_,
&file_path_metadata)) {
continue;
}
base::File file = nacl::OpenNaClReadExecImpl(
file_path_metadata, true /* is_executable */);
if (!file.IsValid())
continue;
prefetched_resource_files.push_back(NaClResourcePrefetchResult(
IPC::TakePlatformFileForTransit(std::move(file)), file_path_metadata,
request_list[i].file_key));
if (prefetched_resource_files.size() >= kMaxPreOpenResourceFiles)
break;
}
base::PostTaskWithTraits(
FROM_HERE, {content::BrowserThread::IO},
base::BindOnce(&NaClHostMessageFilter::LaunchNaClContinuationOnIOThread,
this, launch_params, reply_msg, prefetched_resource_files,
permissions));
}
void NaClHostMessageFilter::LaunchNaClContinuationOnIOThread(
const nacl::NaClLaunchParams& launch_params,
IPC::Message* reply_msg,
const std::vector<NaClResourcePrefetchResult>& prefetched_resource_files,
ppapi::PpapiPermissions permissions) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
NaClFileToken nexe_token = {
launch_params.nexe_token_lo, // lo
launch_params.nexe_token_hi // hi
};
base::PlatformFile nexe_file =
IPC::PlatformFileForTransitToPlatformFile(launch_params.nexe_file);
NaClProcessHost* host = new NaClProcessHost(
GURL(launch_params.manifest_url),
base::File(nexe_file),
nexe_token,
prefetched_resource_files,
permissions,
launch_params.render_view_id,
launch_params.permission_bits,
launch_params.uses_nonsfi_mode,
off_the_record_,
launch_params.process_type,
profile_directory_);
GURL manifest_url(launch_params.manifest_url);
base::FilePath manifest_path;
// We're calling MapUrlToLocalFilePath with the non-blocking API
// because we're running in the I/O thread. Ideally we'd use the other path,
// which would cover more cases.
nacl::NaClBrowser::GetDelegate()->MapUrlToLocalFilePath(
manifest_url,
false /* use_blocking_api */,
profile_directory_,
&manifest_path);
host->Launch(this, reply_msg, manifest_path);
}
void NaClHostMessageFilter::OnGetReadonlyPnaclFd(
const std::string& filename, bool is_executable, IPC::Message* reply_msg) {
// This posts a task to another thread, but the renderer will
// block until the reply is sent.
nacl_file_host::GetReadonlyPnaclFd(this, filename, is_executable, reply_msg);
// This is the first message we receive from the renderer once it knows we
// want to use PNaCl, so start the translation cache initialization here.
pnacl::PnaclHost::GetInstance()->Init();
}
// Return the temporary file via a reply to the
// NaClHostMsg_NaClCreateTemporaryFile sync message.
void NaClHostMessageFilter::SyncReturnTemporaryFile(
IPC::Message* reply_msg,
base::File file) {
if (file.IsValid()) {
NaClHostMsg_NaClCreateTemporaryFile::WriteReplyParams(
reply_msg, IPC::TakePlatformFileForTransit(std::move(file)));
} else {
reply_msg->set_reply_error();
}
Send(reply_msg);
}
void NaClHostMessageFilter::OnNaClCreateTemporaryFile(
IPC::Message* reply_msg) {
pnacl::PnaclHost::GetInstance()->CreateTemporaryFile(
base::Bind(&NaClHostMessageFilter::SyncReturnTemporaryFile,
this,
reply_msg));
}
void NaClHostMessageFilter::AsyncReturnTemporaryFile(
int pp_instance,
const base::File& file,
bool is_hit) {
IPC::PlatformFileForTransit fd = IPC::InvalidPlatformFileForTransit();
if (file.IsValid()) {
// Don't close our copy of the handle, because PnaclHost will use it
// when the translation finishes.
fd = IPC::GetPlatformFileForTransit(file.GetPlatformFile(), false);
}
Send(new NaClViewMsg_NexeTempFileReply(pp_instance, is_hit, fd));
}
void NaClHostMessageFilter::OnNaClGetNumProcessors(int* num_processors) {
*num_processors = base::SysInfo::NumberOfProcessors();
}
void NaClHostMessageFilter::OnGetNexeFd(
int render_view_id,
int pp_instance,
const nacl::PnaclCacheInfo& cache_info) {
if (!cache_info.pexe_url.is_valid()) {
LOG(ERROR) << "Bad URL received from GetNexeFd: " <<
cache_info.pexe_url.possibly_invalid_spec();
bad_message::ReceivedBadMessage(this,
bad_message::NHMF_GET_NEXE_FD_BAD_URL);
return;
}
pnacl::PnaclHost::GetInstance()->GetNexeFd(
render_process_id_,
render_view_id,
pp_instance,
off_the_record_,
cache_info,
base::Bind(&NaClHostMessageFilter::AsyncReturnTemporaryFile,
this,
pp_instance));
}
void NaClHostMessageFilter::OnTranslationFinished(int instance, bool success) {
pnacl::PnaclHost::GetInstance()->TranslationFinished(
render_process_id_, instance, success);
}
void NaClHostMessageFilter::OnMissingArchError(int render_view_id) {
nacl::NaClBrowser::GetDelegate()->
ShowMissingArchInfobar(render_process_id_, render_view_id);
}
void NaClHostMessageFilter::OnOpenNaClExecutable(
int render_view_id,
const GURL& file_url,
bool enable_validation_caching,
IPC::Message* reply_msg) {
nacl_file_host::OpenNaClExecutable(this,
render_view_id,
file_url,
enable_validation_caching,
reply_msg);
}
void NaClHostMessageFilter::OnNaClDebugEnabledForURL(const GURL& nmf_url,
bool* should_debug) {
*should_debug =
nacl::NaClBrowser::GetDelegate()->URLMatchesDebugPatterns(nmf_url);
}
} // namespace nacl