1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/nacl/browser/nacl_host_message_filter.h"
6
7#include "base/sys_info.h"
8#include "components/nacl/browser/nacl_browser.h"
9#include "components/nacl/browser/nacl_file_host.h"
10#include "components/nacl/browser/nacl_process_host.h"
11#include "components/nacl/browser/pnacl_host.h"
12#include "components/nacl/common/nacl_host_messages.h"
13#include "content/public/browser/browser_thread.h"
14#include "content/public/browser/plugin_service.h"
15#include "content/public/browser/render_process_host.h"
16#include "content/public/browser/web_contents.h"
17#include "ipc/ipc_platform_file.h"
18#include "native_client/src/public/nacl_file_info.h"
19#include "net/url_request/url_request_context.h"
20#include "net/url_request/url_request_context_getter.h"
21#include "ppapi/shared_impl/ppapi_permissions.h"
22#include "url/gurl.h"
23
24namespace nacl {
25
26namespace {
27
28ppapi::PpapiPermissions GetNaClPermissions(
29    uint32 permission_bits,
30    content::BrowserContext* browser_context,
31    const GURL& document_url) {
32  // Only allow NaCl plugins to request certain permissions. We don't want
33  // a compromised renderer to be able to start a nacl plugin with e.g. Flash
34  // permissions which may expand the surface area of the sandbox.
35  uint32 masked_bits = permission_bits & ppapi::PERMISSION_DEV;
36  if (content::PluginService::GetInstance()->PpapiDevChannelSupported(
37          browser_context, document_url))
38    masked_bits |= ppapi::PERMISSION_DEV_CHANNEL;
39  return ppapi::PpapiPermissions::GetForCommandLine(masked_bits);
40}
41
42
43ppapi::PpapiPermissions GetPpapiPermissions(uint32 permission_bits,
44                                            int render_process_id,
45                                            int render_view_id) {
46  // We get the URL from WebContents from the RenderViewHost, since we don't
47  // have a BrowserPpapiHost yet.
48  content::RenderProcessHost* host =
49      content::RenderProcessHost::FromID(render_process_id);
50  content::RenderViewHost* view_host =
51      content::RenderViewHost::FromID(render_process_id, render_view_id);
52  if (!view_host)
53    return ppapi::PpapiPermissions();
54  GURL document_url;
55  content::WebContents* contents =
56      content::WebContents::FromRenderViewHost(view_host);
57  if (contents)
58    document_url = contents->GetLastCommittedURL();
59  return GetNaClPermissions(permission_bits,
60                            host->GetBrowserContext(),
61                            document_url);
62}
63
64}  // namespace
65
66NaClHostMessageFilter::NaClHostMessageFilter(
67    int render_process_id,
68    bool is_off_the_record,
69    const base::FilePath& profile_directory,
70    net::URLRequestContextGetter* request_context)
71    : BrowserMessageFilter(NaClHostMsgStart),
72      render_process_id_(render_process_id),
73      off_the_record_(is_off_the_record),
74      profile_directory_(profile_directory),
75      request_context_(request_context),
76      weak_ptr_factory_(this) {
77}
78
79NaClHostMessageFilter::~NaClHostMessageFilter() {
80}
81
82void NaClHostMessageFilter::OnChannelClosing() {
83  pnacl::PnaclHost::GetInstance()->RendererClosing(render_process_id_);
84}
85
86bool NaClHostMessageFilter::OnMessageReceived(const IPC::Message& message) {
87  bool handled = true;
88  IPC_BEGIN_MESSAGE_MAP(NaClHostMessageFilter, message)
89#if !defined(DISABLE_NACL)
90    IPC_MESSAGE_HANDLER_DELAY_REPLY(NaClHostMsg_LaunchNaCl, OnLaunchNaCl)
91    IPC_MESSAGE_HANDLER_DELAY_REPLY(NaClHostMsg_GetReadonlyPnaclFD,
92                                    OnGetReadonlyPnaclFd)
93    IPC_MESSAGE_HANDLER_DELAY_REPLY(NaClHostMsg_NaClCreateTemporaryFile,
94                                    OnNaClCreateTemporaryFile)
95    IPC_MESSAGE_HANDLER(NaClHostMsg_NexeTempFileRequest,
96                        OnGetNexeFd)
97    IPC_MESSAGE_HANDLER(NaClHostMsg_ReportTranslationFinished,
98                        OnTranslationFinished)
99    IPC_MESSAGE_HANDLER(NaClHostMsg_MissingArchError,
100                        OnMissingArchError)
101    IPC_MESSAGE_HANDLER_DELAY_REPLY(NaClHostMsg_OpenNaClExecutable,
102                                    OnOpenNaClExecutable)
103    IPC_MESSAGE_HANDLER(NaClHostMsg_NaClGetNumProcessors,
104                        OnNaClGetNumProcessors)
105    IPC_MESSAGE_HANDLER(NaClHostMsg_NaClDebugEnabledForURL,
106                        OnNaClDebugEnabledForURL)
107#endif
108    IPC_MESSAGE_UNHANDLED(handled = false)
109  IPC_END_MESSAGE_MAP()
110
111  return handled;
112}
113
114net::HostResolver* NaClHostMessageFilter::GetHostResolver() {
115  return request_context_->GetURLRequestContext()->host_resolver();
116}
117
118void NaClHostMessageFilter::OnLaunchNaCl(
119    const nacl::NaClLaunchParams& launch_params,
120    IPC::Message* reply_msg) {
121  // If we're running llc or ld for the PNaCl translator, we don't need to look
122  // up permissions, and we don't have the right browser state to look up some
123  // of the whitelisting parameters anyway.
124  if (!launch_params.uses_irt) {
125    uint32 perms = launch_params.permission_bits & ppapi::PERMISSION_DEV;
126    LaunchNaClContinuation(
127        launch_params,
128        reply_msg,
129        ppapi::PpapiPermissions(perms));
130    return;
131  }
132  content::BrowserThread::PostTaskAndReplyWithResult(
133      content::BrowserThread::UI,
134      FROM_HERE,
135      base::Bind(&GetPpapiPermissions,
136                 launch_params.permission_bits,
137                 render_process_id_,
138                 launch_params.render_view_id),
139      base::Bind(&NaClHostMessageFilter::LaunchNaClContinuation,
140                 this,
141                 launch_params,
142                 reply_msg));
143}
144
145void NaClHostMessageFilter::LaunchNaClContinuation(
146    const nacl::NaClLaunchParams& launch_params,
147    IPC::Message* reply_msg,
148    ppapi::PpapiPermissions permissions) {
149  NaClFileToken nexe_token = {
150      launch_params.nexe_token_lo,  // lo
151      launch_params.nexe_token_hi   // hi
152  };
153
154  base::PlatformFile nexe_file;
155#if defined(OS_WIN)
156  // Duplicate the nexe file handle from the renderer process into the browser
157  // process.
158  if (!::DuplicateHandle(PeerHandle(),
159                         launch_params.nexe_file,
160                         base::GetCurrentProcessHandle(),
161                         &nexe_file,
162                         0,  // Unused, given DUPLICATE_SAME_ACCESS.
163                         FALSE,
164                         DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
165    NaClHostMsg_LaunchNaCl::WriteReplyParams(
166        reply_msg,
167        NaClLaunchResult(),
168        std::string("Failed to duplicate nexe file handle"));
169    Send(reply_msg);
170    return;
171  }
172#elif defined(OS_POSIX)
173  nexe_file =
174      IPC::PlatformFileForTransitToPlatformFile(launch_params.nexe_file);
175#else
176#error Unsupported platform.
177#endif
178
179  NaClProcessHost* host = new NaClProcessHost(
180      GURL(launch_params.manifest_url),
181      base::File(nexe_file),
182      nexe_token,
183      permissions,
184      launch_params.render_view_id,
185      launch_params.permission_bits,
186      launch_params.uses_irt,
187      launch_params.uses_nonsfi_mode,
188      launch_params.enable_dyncode_syscalls,
189      launch_params.enable_exception_handling,
190      launch_params.enable_crash_throttling,
191      off_the_record_,
192      profile_directory_);
193  GURL manifest_url(launch_params.manifest_url);
194  base::FilePath manifest_path;
195  // We're calling MapUrlToLocalFilePath with the non-blocking API
196  // because we're running in the I/O thread. Ideally we'd use the other path,
197  // which would cover more cases.
198  nacl::NaClBrowser::GetDelegate()->MapUrlToLocalFilePath(
199      manifest_url,
200      false /* use_blocking_api */,
201      profile_directory_,
202      &manifest_path);
203  host->Launch(this, reply_msg, manifest_path);
204}
205
206void NaClHostMessageFilter::OnGetReadonlyPnaclFd(
207    const std::string& filename, bool is_executable, IPC::Message* reply_msg) {
208  // This posts a task to another thread, but the renderer will
209  // block until the reply is sent.
210  nacl_file_host::GetReadonlyPnaclFd(this, filename, is_executable, reply_msg);
211
212  // This is the first message we receive from the renderer once it knows we
213  // want to use PNaCl, so start the translation cache initialization here.
214  pnacl::PnaclHost::GetInstance()->Init();
215}
216
217// Return the temporary file via a reply to the
218// NaClHostMsg_NaClCreateTemporaryFile sync message.
219void NaClHostMessageFilter::SyncReturnTemporaryFile(
220    IPC::Message* reply_msg,
221    base::File file) {
222  if (file.IsValid()) {
223    NaClHostMsg_NaClCreateTemporaryFile::WriteReplyParams(
224        reply_msg,
225        IPC::TakeFileHandleForProcess(file.Pass(), PeerHandle()));
226  } else {
227    reply_msg->set_reply_error();
228  }
229  Send(reply_msg);
230}
231
232void NaClHostMessageFilter::OnNaClCreateTemporaryFile(
233    IPC::Message* reply_msg) {
234  pnacl::PnaclHost::GetInstance()->CreateTemporaryFile(
235      base::Bind(&NaClHostMessageFilter::SyncReturnTemporaryFile,
236                 this,
237                 reply_msg));
238}
239
240void NaClHostMessageFilter::AsyncReturnTemporaryFile(
241    int pp_instance,
242    const base::File& file,
243    bool is_hit) {
244  IPC::PlatformFileForTransit fd = IPC::InvalidPlatformFileForTransit();
245  if (file.IsValid()) {
246    // Don't close our copy of the handle, because PnaclHost will use it
247    // when the translation finishes.
248    fd = IPC::GetFileHandleForProcess(file.GetPlatformFile(), PeerHandle(),
249                                      false);
250  }
251  Send(new NaClViewMsg_NexeTempFileReply(pp_instance, is_hit, fd));
252}
253
254void NaClHostMessageFilter::OnNaClGetNumProcessors(int* num_processors) {
255  *num_processors = base::SysInfo::NumberOfProcessors();
256}
257
258void NaClHostMessageFilter::OnGetNexeFd(
259    int render_view_id,
260    int pp_instance,
261    const nacl::PnaclCacheInfo& cache_info) {
262  if (!cache_info.pexe_url.is_valid()) {
263    LOG(ERROR) << "Bad URL received from GetNexeFd: " <<
264        cache_info.pexe_url.possibly_invalid_spec();
265    BadMessageReceived();
266    return;
267  }
268
269  pnacl::PnaclHost::GetInstance()->GetNexeFd(
270      render_process_id_,
271      render_view_id,
272      pp_instance,
273      off_the_record_,
274      cache_info,
275      base::Bind(&NaClHostMessageFilter::AsyncReturnTemporaryFile,
276                 this,
277                 pp_instance));
278}
279
280void NaClHostMessageFilter::OnTranslationFinished(int instance, bool success) {
281  pnacl::PnaclHost::GetInstance()->TranslationFinished(
282      render_process_id_, instance, success);
283}
284
285void NaClHostMessageFilter::OnMissingArchError(int render_view_id) {
286  nacl::NaClBrowser::GetDelegate()->
287      ShowMissingArchInfobar(render_process_id_, render_view_id);
288}
289
290void NaClHostMessageFilter::OnOpenNaClExecutable(int render_view_id,
291                                                 const GURL& file_url,
292                                                 IPC::Message* reply_msg) {
293  nacl_file_host::OpenNaClExecutable(this, render_view_id, file_url,
294                                     reply_msg);
295}
296
297void NaClHostMessageFilter::OnNaClDebugEnabledForURL(const GURL& nmf_url,
298                                                     bool* should_debug) {
299  *should_debug =
300      nacl::NaClBrowser::GetDelegate()->URLMatchesDebugPatterns(nmf_url);
301}
302
303}  // namespace nacl
304