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 "content/browser/renderer_host/pepper/pepper_flash_file_message_filter.h"
6
7#include "base/bind.h"
8#include "base/files/file.h"
9#include "base/files/file_enumerator.h"
10#include "base/files/file_util.h"
11#include "base/threading/sequenced_worker_pool.h"
12#include "content/browser/child_process_security_policy_impl.h"
13#include "content/browser/renderer_host/pepper/pepper_security_helper.h"
14#include "content/public/browser/browser_ppapi_host.h"
15#include "content/public/browser/browser_thread.h"
16#include "content/public/common/content_constants.h"
17#include "ipc/ipc_platform_file.h"
18#include "ppapi/c/pp_errors.h"
19#include "ppapi/host/dispatch_host_message.h"
20#include "ppapi/host/host_message_context.h"
21#include "ppapi/host/ppapi_host.h"
22#include "ppapi/proxy/ppapi_messages.h"
23#include "ppapi/shared_impl/file_path.h"
24#include "ppapi/shared_impl/file_type_conversion.h"
25
26namespace content {
27
28namespace {
29
30bool CanRead(int process_id, const base::FilePath& path) {
31  return ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(process_id,
32                                                                    path);
33}
34
35bool CanCreateReadWrite(int process_id, const base::FilePath& path) {
36  return ChildProcessSecurityPolicyImpl::GetInstance()->CanCreateReadWriteFile(
37      process_id, path);
38}
39
40}  // namespace
41
42PepperFlashFileMessageFilter::PepperFlashFileMessageFilter(
43    PP_Instance instance,
44    BrowserPpapiHost* host)
45    : plugin_process_handle_(host->GetPluginProcessHandle()) {
46  int unused;
47  host->GetRenderFrameIDsForInstance(instance, &render_process_id_, &unused);
48  base::FilePath profile_data_directory = host->GetProfileDataDirectory();
49  std::string plugin_name = host->GetPluginName();
50
51  if (profile_data_directory.empty() || plugin_name.empty()) {
52    // These are used to construct the path. If they are not set it means we
53    // will construct a bad path and could provide access to the wrong files.
54    // In this case, |plugin_data_directory_| will remain unset and
55    // |ValidateAndConvertPepperFilePath| will fail.
56    NOTREACHED();
57  } else {
58    plugin_data_directory_ = GetDataDirName(profile_data_directory).Append(
59        base::FilePath::FromUTF8Unsafe(plugin_name));
60  }
61}
62
63PepperFlashFileMessageFilter::~PepperFlashFileMessageFilter() {}
64
65// static
66base::FilePath PepperFlashFileMessageFilter::GetDataDirName(
67    const base::FilePath& profile_path) {
68  return profile_path.Append(kPepperDataDirname);
69}
70
71scoped_refptr<base::TaskRunner>
72PepperFlashFileMessageFilter::OverrideTaskRunnerForMessage(
73    const IPC::Message& msg) {
74  // The blocking pool provides a pool of threads to run file
75  // operations, instead of a single thread which might require
76  // queuing time.  Since these messages are synchronous as sent from
77  // the plugin, the sending thread cannot send a new message until
78  // this one returns, so there is no need to sequence tasks here.  If
79  // the plugin has multiple threads, it cannot make assumptions about
80  // ordering of IPC message sends, so it cannot make assumptions
81  // about ordering of operations caused by those IPC messages.
82  return scoped_refptr<base::TaskRunner>(BrowserThread::GetBlockingPool());
83}
84
85int32_t PepperFlashFileMessageFilter::OnResourceMessageReceived(
86    const IPC::Message& msg,
87    ppapi::host::HostMessageContext* context) {
88  PPAPI_BEGIN_MESSAGE_MAP(PepperFlashFileMessageFilter, msg)
89    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_OpenFile,
90                                      OnOpenFile)
91    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_RenameFile,
92                                      OnRenameFile)
93    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_DeleteFileOrDir,
94                                      OnDeleteFileOrDir)
95    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_CreateDir,
96                                      OnCreateDir)
97    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_QueryFile,
98                                      OnQueryFile)
99    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_GetDirContents,
100                                      OnGetDirContents)
101    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
102        PpapiHostMsg_FlashFile_CreateTemporaryFile, OnCreateTemporaryFile)
103  PPAPI_END_MESSAGE_MAP()
104  return PP_ERROR_FAILED;
105}
106
107int32_t PepperFlashFileMessageFilter::OnOpenFile(
108    ppapi::host::HostMessageContext* context,
109    const ppapi::PepperFilePath& path,
110    int pp_open_flags) {
111  base::FilePath full_path = ValidateAndConvertPepperFilePath(
112      path, base::Bind(&CanOpenWithPepperFlags, pp_open_flags));
113  if (full_path.empty()) {
114    return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
115  }
116
117  int platform_file_flags = 0;
118  if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(pp_open_flags,
119                                                     &platform_file_flags)) {
120    return base::File::FILE_ERROR_FAILED;
121  }
122
123  base::File file(full_path, platform_file_flags);
124  if (!file.IsValid()) {
125    return ppapi::FileErrorToPepperError(file.error_details());
126  }
127
128  // Make sure we didn't try to open a directory: directory fd shouldn't be
129  // passed to untrusted processes because they open security holes.
130  base::File::Info info;
131  if (!file.GetInfo(&info) || info.is_directory) {
132    // When in doubt, throw it out.
133    return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
134  }
135
136  IPC::PlatformFileForTransit transit_file =
137      IPC::TakeFileHandleForProcess(file.Pass(), plugin_process_handle_);
138  ppapi::host::ReplyMessageContext reply_context =
139      context->MakeReplyMessageContext();
140  reply_context.params.AppendHandle(ppapi::proxy::SerializedHandle(
141      ppapi::proxy::SerializedHandle::FILE, transit_file));
142  SendReply(reply_context, IPC::Message());
143  return PP_OK_COMPLETIONPENDING;
144}
145
146int32_t PepperFlashFileMessageFilter::OnRenameFile(
147    ppapi::host::HostMessageContext* context,
148    const ppapi::PepperFilePath& from_path,
149    const ppapi::PepperFilePath& to_path) {
150  base::FilePath from_full_path = ValidateAndConvertPepperFilePath(
151      from_path, base::Bind(&CanCreateReadWrite));
152  base::FilePath to_full_path = ValidateAndConvertPepperFilePath(
153      to_path, base::Bind(&CanCreateReadWrite));
154  if (from_full_path.empty() || to_full_path.empty()) {
155    return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
156  }
157
158  bool result = base::Move(from_full_path, to_full_path);
159  return ppapi::FileErrorToPepperError(
160      result ? base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
161}
162
163int32_t PepperFlashFileMessageFilter::OnDeleteFileOrDir(
164    ppapi::host::HostMessageContext* context,
165    const ppapi::PepperFilePath& path,
166    bool recursive) {
167  base::FilePath full_path =
168      ValidateAndConvertPepperFilePath(path, base::Bind(&CanCreateReadWrite));
169  if (full_path.empty()) {
170    return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
171  }
172
173  bool result = base::DeleteFile(full_path, recursive);
174  return ppapi::FileErrorToPepperError(
175      result ? base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
176}
177int32_t PepperFlashFileMessageFilter::OnCreateDir(
178    ppapi::host::HostMessageContext* context,
179    const ppapi::PepperFilePath& path) {
180  base::FilePath full_path =
181      ValidateAndConvertPepperFilePath(path, base::Bind(&CanCreateReadWrite));
182  if (full_path.empty()) {
183    return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
184  }
185
186  bool result = base::CreateDirectory(full_path);
187  return ppapi::FileErrorToPepperError(
188      result ? base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
189}
190
191int32_t PepperFlashFileMessageFilter::OnQueryFile(
192    ppapi::host::HostMessageContext* context,
193    const ppapi::PepperFilePath& path) {
194  base::FilePath full_path =
195      ValidateAndConvertPepperFilePath(path, base::Bind(&CanRead));
196  if (full_path.empty()) {
197    return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
198  }
199
200  base::File::Info info;
201  bool result = base::GetFileInfo(full_path, &info);
202  context->reply_msg = PpapiPluginMsg_FlashFile_QueryFileReply(info);
203  return ppapi::FileErrorToPepperError(
204      result ? base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
205}
206
207int32_t PepperFlashFileMessageFilter::OnGetDirContents(
208    ppapi::host::HostMessageContext* context,
209    const ppapi::PepperFilePath& path) {
210  base::FilePath full_path =
211      ValidateAndConvertPepperFilePath(path, base::Bind(&CanRead));
212  if (full_path.empty()) {
213    return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
214  }
215
216  ppapi::DirContents contents;
217  base::FileEnumerator enumerator(full_path,
218                                  false,
219                                  base::FileEnumerator::FILES |
220                                      base::FileEnumerator::DIRECTORIES |
221                                      base::FileEnumerator::INCLUDE_DOT_DOT);
222
223  while (!enumerator.Next().empty()) {
224    base::FileEnumerator::FileInfo info = enumerator.GetInfo();
225    ppapi::DirEntry entry = {info.GetName(), info.IsDirectory()};
226    contents.push_back(entry);
227  }
228
229  context->reply_msg = PpapiPluginMsg_FlashFile_GetDirContentsReply(contents);
230  return PP_OK;
231}
232
233int32_t PepperFlashFileMessageFilter::OnCreateTemporaryFile(
234    ppapi::host::HostMessageContext* context) {
235  ppapi::PepperFilePath dir_path(ppapi::PepperFilePath::DOMAIN_MODULE_LOCAL,
236                                 base::FilePath());
237  base::FilePath validated_dir_path = ValidateAndConvertPepperFilePath(
238      dir_path, base::Bind(&CanCreateReadWrite));
239  if (validated_dir_path.empty() ||
240      (!base::DirectoryExists(validated_dir_path) &&
241       !base::CreateDirectory(validated_dir_path))) {
242    return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_ACCESS_DENIED);
243  }
244
245  base::FilePath file_path;
246  if (!base::CreateTemporaryFileInDir(validated_dir_path, &file_path)) {
247    return ppapi::FileErrorToPepperError(base::File::FILE_ERROR_FAILED);
248  }
249
250  base::File file(file_path,
251                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ |
252                      base::File::FLAG_WRITE | base::File::FLAG_TEMPORARY |
253                      base::File::FLAG_DELETE_ON_CLOSE);
254
255  if (!file.IsValid())
256    return ppapi::FileErrorToPepperError(file.error_details());
257
258  IPC::PlatformFileForTransit transit_file =
259      IPC::TakeFileHandleForProcess(file.Pass(), plugin_process_handle_);
260  ppapi::host::ReplyMessageContext reply_context =
261      context->MakeReplyMessageContext();
262  reply_context.params.AppendHandle(ppapi::proxy::SerializedHandle(
263      ppapi::proxy::SerializedHandle::FILE, transit_file));
264  SendReply(reply_context, IPC::Message());
265  return PP_OK_COMPLETIONPENDING;
266}
267
268base::FilePath PepperFlashFileMessageFilter::ValidateAndConvertPepperFilePath(
269    const ppapi::PepperFilePath& pepper_path,
270    const CheckPermissionsCallback& check_permissions_callback) const {
271  base::FilePath file_path;  // Empty path returned on error.
272  switch (pepper_path.domain()) {
273    case ppapi::PepperFilePath::DOMAIN_ABSOLUTE:
274      if (pepper_path.path().IsAbsolute() &&
275          check_permissions_callback.Run(render_process_id_,
276                                         pepper_path.path()))
277        file_path = pepper_path.path();
278      break;
279    case ppapi::PepperFilePath::DOMAIN_MODULE_LOCAL:
280      // This filter provides the module name portion of the path to prevent
281      // plugins from accessing each other's data.
282      if (!plugin_data_directory_.empty() && !pepper_path.path().IsAbsolute() &&
283          !pepper_path.path().ReferencesParent())
284        file_path = plugin_data_directory_.Append(pepper_path.path());
285      break;
286    default:
287      NOTREACHED();
288      break;
289  }
290  return file_path;
291}
292
293}  // namespace content
294