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
26 namespace content {
27
28 namespace {
29
CanRead(int process_id,const base::FilePath & path)30 bool CanRead(int process_id, const base::FilePath& path) {
31 return ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(process_id,
32 path);
33 }
34
CanCreateReadWrite(int process_id,const base::FilePath & path)35 bool CanCreateReadWrite(int process_id, const base::FilePath& path) {
36 return ChildProcessSecurityPolicyImpl::GetInstance()->CanCreateReadWriteFile(
37 process_id, path);
38 }
39
40 } // namespace
41
PepperFlashFileMessageFilter(PP_Instance instance,BrowserPpapiHost * host)42 PepperFlashFileMessageFilter::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
~PepperFlashFileMessageFilter()63 PepperFlashFileMessageFilter::~PepperFlashFileMessageFilter() {}
64
65 // static
GetDataDirName(const base::FilePath & profile_path)66 base::FilePath PepperFlashFileMessageFilter::GetDataDirName(
67 const base::FilePath& profile_path) {
68 return profile_path.Append(kPepperDataDirname);
69 }
70
71 scoped_refptr<base::TaskRunner>
OverrideTaskRunnerForMessage(const IPC::Message & msg)72 PepperFlashFileMessageFilter::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
OnResourceMessageReceived(const IPC::Message & msg,ppapi::host::HostMessageContext * context)85 int32_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
OnOpenFile(ppapi::host::HostMessageContext * context,const ppapi::PepperFilePath & path,int pp_open_flags)107 int32_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
OnRenameFile(ppapi::host::HostMessageContext * context,const ppapi::PepperFilePath & from_path,const ppapi::PepperFilePath & to_path)146 int32_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
OnDeleteFileOrDir(ppapi::host::HostMessageContext * context,const ppapi::PepperFilePath & path,bool recursive)163 int32_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 }
OnCreateDir(ppapi::host::HostMessageContext * context,const ppapi::PepperFilePath & path)177 int32_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
OnQueryFile(ppapi::host::HostMessageContext * context,const ppapi::PepperFilePath & path)191 int32_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
OnGetDirContents(ppapi::host::HostMessageContext * context,const ppapi::PepperFilePath & path)207 int32_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
OnCreateTemporaryFile(ppapi::host::HostMessageContext * context)233 int32_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
ValidateAndConvertPepperFilePath(const ppapi::PepperFilePath & pepper_path,const CheckPermissionsCallback & check_permissions_callback) const268 base::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