Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2012 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_file_host.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/file_util.h"
      9 #include "base/files/file.h"
     10 #include "base/files/file_path.h"
     11 #include "base/path_service.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/threading/sequenced_worker_pool.h"
     14 #include "components/nacl/browser/nacl_browser.h"
     15 #include "components/nacl/browser/nacl_browser_delegate.h"
     16 #include "components/nacl/browser/nacl_host_message_filter.h"
     17 #include "components/nacl/common/nacl_host_messages.h"
     18 #include "content/public/browser/browser_thread.h"
     19 #include "content/public/browser/render_view_host.h"
     20 #include "content/public/browser/site_instance.h"
     21 #include "ipc/ipc_platform_file.h"
     22 
     23 using content::BrowserThread;
     24 
     25 namespace {
     26 
     27 // Force a prefix to prevent user from opening "magic" files.
     28 const char* kExpectedFilePrefix = "pnacl_public_";
     29 
     30 // Restrict PNaCl file lengths to reduce likelyhood of hitting bugs
     31 // in file name limit error-handling-code-paths, etc.
     32 const size_t kMaxFileLength = 40;
     33 
     34 void NotifyRendererOfError(
     35     nacl::NaClHostMessageFilter* nacl_host_message_filter,
     36     IPC::Message* reply_msg) {
     37   reply_msg->set_reply_error();
     38   nacl_host_message_filter->Send(reply_msg);
     39 }
     40 
     41 base::File PnaclDoOpenFile(const base::FilePath& file_to_open) {
     42   return base::File(file_to_open,
     43                     base::File::FLAG_OPEN | base::File::FLAG_READ);
     44 }
     45 
     46 void DoOpenPnaclFile(
     47     scoped_refptr<nacl::NaClHostMessageFilter> nacl_host_message_filter,
     48     const std::string& filename,
     49     IPC::Message* reply_msg) {
     50   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
     51   base::FilePath full_filepath;
     52 
     53   // PNaCl must be installed.
     54   base::FilePath pnacl_dir;
     55   if (!nacl::NaClBrowser::GetDelegate()->GetPnaclDirectory(&pnacl_dir) ||
     56       !base::PathExists(pnacl_dir)) {
     57     NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg);
     58     return;
     59   }
     60 
     61   // Do some validation.
     62   if (!nacl_file_host::PnaclCanOpenFile(filename, &full_filepath)) {
     63     NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg);
     64     return;
     65   }
     66 
     67   base::File file_to_open = PnaclDoOpenFile(full_filepath);
     68   if (!file_to_open.IsValid()) {
     69     NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg);
     70     return;
     71   }
     72 
     73   // Send the reply!
     74   // Do any DuplicateHandle magic that is necessary first.
     75   IPC::PlatformFileForTransit target_desc =
     76       IPC::TakeFileHandleForProcess(file_to_open.Pass(),
     77                                     nacl_host_message_filter->PeerHandle());
     78   if (target_desc == IPC::InvalidPlatformFileForTransit()) {
     79     NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg);
     80     return;
     81   }
     82   NaClHostMsg_GetReadonlyPnaclFD::WriteReplyParams(
     83       reply_msg, target_desc);
     84   nacl_host_message_filter->Send(reply_msg);
     85 }
     86 
     87 void DoRegisterOpenedNaClExecutableFile(
     88     scoped_refptr<nacl::NaClHostMessageFilter> nacl_host_message_filter,
     89     base::File file,
     90     base::FilePath file_path,
     91     IPC::Message* reply_msg) {
     92   // IO thread owns the NaClBrowser singleton.
     93   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     94 
     95   nacl::NaClBrowser* nacl_browser = nacl::NaClBrowser::GetInstance();
     96   uint64 file_token_lo = 0;
     97   uint64 file_token_hi = 0;
     98   nacl_browser->PutFilePath(file_path, &file_token_lo, &file_token_hi);
     99 
    100   IPC::PlatformFileForTransit file_desc = IPC::TakeFileHandleForProcess(
    101       file.Pass(),
    102       nacl_host_message_filter->PeerHandle());
    103 
    104   NaClHostMsg_OpenNaClExecutable::WriteReplyParams(
    105       reply_msg, file_desc, file_token_lo, file_token_hi);
    106   nacl_host_message_filter->Send(reply_msg);
    107 }
    108 
    109 // Convert the file URL into a file descriptor.
    110 // This function is security sensitive.  Be sure to check with a security
    111 // person before you modify it.
    112 void DoOpenNaClExecutableOnThreadPool(
    113     scoped_refptr<nacl::NaClHostMessageFilter> nacl_host_message_filter,
    114     const GURL& file_url,
    115     IPC::Message* reply_msg) {
    116   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
    117 
    118   base::FilePath file_path;
    119   if (!nacl::NaClBrowser::GetDelegate()->MapUrlToLocalFilePath(
    120           file_url,
    121           true /* use_blocking_api */,
    122           nacl_host_message_filter->profile_directory(),
    123           &file_path)) {
    124     NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg);
    125     return;
    126   }
    127 
    128   base::File file = nacl::OpenNaClExecutableImpl(file_path);
    129   if (file.IsValid()) {
    130     // This function is running on the blocking pool, but the path needs to be
    131     // registered in a structure owned by the IO thread.
    132     BrowserThread::PostTask(
    133         BrowserThread::IO, FROM_HERE,
    134         base::Bind(
    135             &DoRegisterOpenedNaClExecutableFile,
    136             nacl_host_message_filter,
    137             Passed(file.Pass()), file_path, reply_msg));
    138   } else {
    139     NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg);
    140     return;
    141   }
    142 }
    143 
    144 }  // namespace
    145 
    146 namespace nacl_file_host {
    147 
    148 void GetReadonlyPnaclFd(
    149     scoped_refptr<nacl::NaClHostMessageFilter> nacl_host_message_filter,
    150     const std::string& filename,
    151     IPC::Message* reply_msg) {
    152   if (!BrowserThread::PostBlockingPoolTask(
    153           FROM_HERE,
    154           base::Bind(&DoOpenPnaclFile,
    155                      nacl_host_message_filter,
    156                      filename,
    157                      reply_msg))) {
    158     NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg);
    159   }
    160 }
    161 
    162 // This function is security sensitive.  Be sure to check with a security
    163 // person before you modify it.
    164 bool PnaclCanOpenFile(const std::string& filename,
    165                       base::FilePath* file_to_open) {
    166   if (filename.length() > kMaxFileLength)
    167     return false;
    168 
    169   if (filename.empty())
    170     return false;
    171 
    172   // Restrict character set of the file name to something really simple
    173   // (a-z, 0-9, and underscores).
    174   for (size_t i = 0; i < filename.length(); ++i) {
    175     char charAt = filename[i];
    176     if (charAt < 'a' || charAt > 'z')
    177       if (charAt < '0' || charAt > '9')
    178         if (charAt != '_')
    179           return false;
    180   }
    181 
    182   // PNaCl must be installed.
    183   base::FilePath pnacl_dir;
    184   if (!nacl::NaClBrowser::GetDelegate()->GetPnaclDirectory(&pnacl_dir) ||
    185       pnacl_dir.empty())
    186     return false;
    187 
    188   // Prepend the prefix to restrict files to a whitelisted set.
    189   base::FilePath full_path = pnacl_dir.AppendASCII(
    190       std::string(kExpectedFilePrefix) + filename);
    191   *file_to_open = full_path;
    192   return true;
    193 }
    194 
    195 void OpenNaClExecutable(
    196     scoped_refptr<nacl::NaClHostMessageFilter> nacl_host_message_filter,
    197     int render_view_id,
    198     const GURL& file_url,
    199     IPC::Message* reply_msg) {
    200   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    201     BrowserThread::PostTask(
    202         BrowserThread::UI, FROM_HERE,
    203         base::Bind(
    204             &OpenNaClExecutable,
    205             nacl_host_message_filter,
    206             render_view_id, file_url, reply_msg));
    207     return;
    208   }
    209 
    210   // Make sure render_view_id is valid and that the URL is a part of the
    211   // render view's site. Without these checks, apps could probe the extension
    212   // directory or run NaCl code from other extensions.
    213   content::RenderViewHost* rvh = content::RenderViewHost::FromID(
    214       nacl_host_message_filter->render_process_id(), render_view_id);
    215   if (!rvh) {
    216     nacl_host_message_filter->BadMessageReceived();  // Kill the renderer.
    217     return;
    218   }
    219   content::SiteInstance* site_instance = rvh->GetSiteInstance();
    220   if (!content::SiteInstance::IsSameWebSite(site_instance->GetBrowserContext(),
    221                                             site_instance->GetSiteURL(),
    222                                             file_url)) {
    223     NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg);
    224     return;
    225   }
    226 
    227   // The URL is part of the current app. Now query the extension system for the
    228   // file path and convert that to a file descriptor. This should be done on a
    229   // blocking pool thread.
    230   if (!BrowserThread::PostBlockingPoolTask(
    231       FROM_HERE,
    232       base::Bind(
    233           &DoOpenNaClExecutableOnThreadPool,
    234           nacl_host_message_filter,
    235           file_url, reply_msg))) {
    236     NotifyRendererOfError(nacl_host_message_filter.get(), reply_msg);
    237   }
    238 }
    239 
    240 }  // namespace nacl_file_host
    241