• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
NotifyRendererOfError(nacl::NaClHostMessageFilter * nacl_host_message_filter,IPC::Message * reply_msg)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 
PnaclDoOpenFile(const base::FilePath & file_to_open)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 
DoOpenPnaclFile(scoped_refptr<nacl::NaClHostMessageFilter> nacl_host_message_filter,const std::string & filename,IPC::Message * reply_msg)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 
DoRegisterOpenedNaClExecutableFile(scoped_refptr<nacl::NaClHostMessageFilter> nacl_host_message_filter,base::File file,base::FilePath file_path,IPC::Message * reply_msg)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.
DoOpenNaClExecutableOnThreadPool(scoped_refptr<nacl::NaClHostMessageFilter> nacl_host_message_filter,const GURL & file_url,IPC::Message * reply_msg)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 
GetReadonlyPnaclFd(scoped_refptr<nacl::NaClHostMessageFilter> nacl_host_message_filter,const std::string & filename,IPC::Message * reply_msg)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.
PnaclCanOpenFile(const std::string & filename,base::FilePath * file_to_open)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 
OpenNaClExecutable(scoped_refptr<nacl::NaClHostMessageFilter> nacl_host_message_filter,int render_view_id,const GURL & file_url,IPC::Message * reply_msg)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