Home | History | Annotate | Download | only in pepper
      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