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