root/content/browser/renderer_host/pepper/pepper_flash_file_message_filter.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CanRead
  2. CanCreateReadWrite
  3. GetDataDirName
  4. OverrideTaskRunnerForMessage
  5. OnResourceMessageReceived
  6. OnOpenFile
  7. OnRenameFile
  8. OnDeleteFileOrDir
  9. OnCreateDir
  10. OnQueryFile
  11. OnGetDirContents
  12. OnCreateTemporaryFile
  13. ValidateAndConvertPepperFilePath

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/pepper/pepper_flash_file_message_filter.h"

#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/threading/sequenced_worker_pool.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/renderer_host/pepper/pepper_security_helper.h"
#include "content/public/browser/browser_ppapi_host.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_constants.h"
#include "ipc/ipc_platform_file.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/file_path.h"
#include "ppapi/shared_impl/file_type_conversion.h"

namespace content {

namespace {

bool CanRead(int process_id, const base::FilePath& path) {
  return ChildProcessSecurityPolicyImpl::GetInstance()->
      CanReadFile(process_id, path);
}

bool CanCreateReadWrite(int process_id, const base::FilePath& path) {
  return ChildProcessSecurityPolicyImpl::GetInstance()->
      CanCreateReadWriteFile(process_id, path);
}

}  // namespace

PepperFlashFileMessageFilter::PepperFlashFileMessageFilter(
    PP_Instance instance,
    BrowserPpapiHost* host)
    : plugin_process_handle_(host->GetPluginProcessHandle()) {
  int unused;
  host->GetRenderFrameIDsForInstance(instance, &render_process_id_, &unused);
  base::FilePath profile_data_directory = host->GetProfileDataDirectory();
  std::string plugin_name = host->GetPluginName();

  if (profile_data_directory.empty() || plugin_name.empty()) {
    // These are used to construct the path. If they are not set it means we
    // will construct a bad path and could provide access to the wrong files.
    // In this case, |plugin_data_directory_| will remain unset and
    // |ValidateAndConvertPepperFilePath| will fail.
    NOTREACHED();
  } else {
    plugin_data_directory_ = GetDataDirName(profile_data_directory).Append(
        base::FilePath::FromUTF8Unsafe(plugin_name));
  }
}

PepperFlashFileMessageFilter::~PepperFlashFileMessageFilter() {
}

// static
base::FilePath PepperFlashFileMessageFilter::GetDataDirName(
    const base::FilePath& profile_path) {
  return profile_path.Append(kPepperDataDirname);
}

scoped_refptr<base::TaskRunner>
PepperFlashFileMessageFilter::OverrideTaskRunnerForMessage(
    const IPC::Message& msg) {
  // The blocking pool provides a pool of threads to run file
  // operations, instead of a single thread which might require
  // queuing time.  Since these messages are synchronous as sent from
  // the plugin, the sending thread cannot send a new message until
  // this one returns, so there is no need to sequence tasks here.  If
  // the plugin has multiple threads, it cannot make assumptions about
  // ordering of IPC message sends, so it cannot make assumptions
  // about ordering of operations caused by those IPC messages.
  return scoped_refptr<base::TaskRunner>(BrowserThread::GetBlockingPool());
}

int32_t PepperFlashFileMessageFilter::OnResourceMessageReceived(
   const IPC::Message& msg,
   ppapi::host::HostMessageContext* context) {
  IPC_BEGIN_MESSAGE_MAP(PepperFlashFileMessageFilter, msg)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_OpenFile,
                                      OnOpenFile)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_RenameFile,
                                      OnRenameFile)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_DeleteFileOrDir,
                                      OnDeleteFileOrDir)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_CreateDir,
                                      OnCreateDir)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_QueryFile,
                                      OnQueryFile)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashFile_GetDirContents,
                                      OnGetDirContents)
    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
        PpapiHostMsg_FlashFile_CreateTemporaryFile,
        OnCreateTemporaryFile)
  IPC_END_MESSAGE_MAP()
  return PP_ERROR_FAILED;
}

int32_t PepperFlashFileMessageFilter::OnOpenFile(
    ppapi::host::HostMessageContext* context,
    const ppapi::PepperFilePath& path,
    int pp_open_flags) {
  base::FilePath full_path = ValidateAndConvertPepperFilePath(
      path,
      base::Bind(&CanOpenWithPepperFlags, pp_open_flags));
  if (full_path.empty()) {
    return ppapi::FileErrorToPepperError(
        base::File::FILE_ERROR_ACCESS_DENIED);
  }

  int platform_file_flags = 0;
  if (!ppapi::PepperFileOpenFlagsToPlatformFileFlags(
          pp_open_flags, &platform_file_flags)) {
    return base::File::FILE_ERROR_FAILED;
  }

  base::File file(full_path, platform_file_flags);
  if (!file.IsValid()) {
    return ppapi::FileErrorToPepperError(file.error_details());
  }

  // Make sure we didn't try to open a directory: directory fd shouldn't be
  // passed to untrusted processes because they open security holes.
  base::File::Info info;
  if (!file.GetInfo(&info) || info.is_directory) {
    // When in doubt, throw it out.
    return ppapi::FileErrorToPepperError(
        base::File::FILE_ERROR_ACCESS_DENIED);
  }

  IPC::PlatformFileForTransit transit_file =
      IPC::TakeFileHandleForProcess(file.Pass(), plugin_process_handle_);
  ppapi::host::ReplyMessageContext reply_context =
      context->MakeReplyMessageContext();
  reply_context.params.AppendHandle(ppapi::proxy::SerializedHandle(
      ppapi::proxy::SerializedHandle::FILE, transit_file));
  SendReply(reply_context, IPC::Message());
  return PP_OK_COMPLETIONPENDING;
}

int32_t PepperFlashFileMessageFilter::OnRenameFile(
    ppapi::host::HostMessageContext* context,
    const ppapi::PepperFilePath& from_path,
    const ppapi::PepperFilePath& to_path) {
  base::FilePath from_full_path = ValidateAndConvertPepperFilePath(
      from_path, base::Bind(&CanCreateReadWrite));
  base::FilePath to_full_path = ValidateAndConvertPepperFilePath(
      to_path, base::Bind(&CanCreateReadWrite));
  if (from_full_path.empty() || to_full_path.empty()) {
    return ppapi::FileErrorToPepperError(
        base::File::FILE_ERROR_ACCESS_DENIED);
  }

  bool result = base::Move(from_full_path, to_full_path);
  return ppapi::FileErrorToPepperError(result ?
      base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
}

int32_t PepperFlashFileMessageFilter::OnDeleteFileOrDir(
    ppapi::host::HostMessageContext* context,
    const ppapi::PepperFilePath& path,
    bool recursive) {
  base::FilePath full_path = ValidateAndConvertPepperFilePath(
      path, base::Bind(&CanCreateReadWrite));
  if (full_path.empty()) {
    return ppapi::FileErrorToPepperError(
        base::File::FILE_ERROR_ACCESS_DENIED);
  }

  bool result = base::DeleteFile(full_path, recursive);
  return ppapi::FileErrorToPepperError(result ?
      base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
}
int32_t PepperFlashFileMessageFilter::OnCreateDir(
    ppapi::host::HostMessageContext* context,
    const ppapi::PepperFilePath& path) {
  base::FilePath full_path = ValidateAndConvertPepperFilePath(
      path, base::Bind(&CanCreateReadWrite));
  if (full_path.empty()) {
    return ppapi::FileErrorToPepperError(
        base::File::FILE_ERROR_ACCESS_DENIED);
  }

  bool result = base::CreateDirectory(full_path);
  return ppapi::FileErrorToPepperError(result ?
      base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
}

int32_t PepperFlashFileMessageFilter::OnQueryFile(
    ppapi::host::HostMessageContext* context,
    const ppapi::PepperFilePath& path) {
  base::FilePath full_path = ValidateAndConvertPepperFilePath(
      path, base::Bind(&CanRead));
  if (full_path.empty()) {
    return ppapi::FileErrorToPepperError(
        base::File::FILE_ERROR_ACCESS_DENIED);
  }

  base::File::Info info;
  bool result = base::GetFileInfo(full_path, &info);
  context->reply_msg = PpapiPluginMsg_FlashFile_QueryFileReply(info);
  return ppapi::FileErrorToPepperError(result ?
      base::File::FILE_OK : base::File::FILE_ERROR_ACCESS_DENIED);
}

int32_t PepperFlashFileMessageFilter::OnGetDirContents(
    ppapi::host::HostMessageContext* context,
    const ppapi::PepperFilePath& path) {
  base::FilePath full_path = ValidateAndConvertPepperFilePath(
      path, base::Bind(&CanRead));
  if (full_path.empty()) {
    return ppapi::FileErrorToPepperError(
        base::File::FILE_ERROR_ACCESS_DENIED);
  }

  ppapi::DirContents contents;
  base::FileEnumerator enumerator(full_path, false,
      base::FileEnumerator::FILES |
      base::FileEnumerator::DIRECTORIES |
      base::FileEnumerator::INCLUDE_DOT_DOT);

  while (!enumerator.Next().empty()) {
    base::FileEnumerator::FileInfo info = enumerator.GetInfo();
    ppapi::DirEntry entry = {
      info.GetName(),
      info.IsDirectory()
    };
    contents.push_back(entry);
  }

  context->reply_msg = PpapiPluginMsg_FlashFile_GetDirContentsReply(contents);
  return PP_OK;
}

int32_t PepperFlashFileMessageFilter::OnCreateTemporaryFile(
    ppapi::host::HostMessageContext* context) {
  ppapi::PepperFilePath dir_path(
      ppapi::PepperFilePath::DOMAIN_MODULE_LOCAL, base::FilePath());
  base::FilePath validated_dir_path = ValidateAndConvertPepperFilePath(
      dir_path, base::Bind(&CanCreateReadWrite));
  if (validated_dir_path.empty() ||
      (!base::DirectoryExists(validated_dir_path) &&
       !base::CreateDirectory(validated_dir_path))) {
    return ppapi::FileErrorToPepperError(
        base::File::FILE_ERROR_ACCESS_DENIED);
  }

  base::FilePath file_path;
  if (!base::CreateTemporaryFileInDir(validated_dir_path, &file_path)) {
    return ppapi::FileErrorToPepperError(
        base::File::FILE_ERROR_FAILED);
  }

  base::File file(file_path,
                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ |
                      base::File::FLAG_WRITE | base::File::FLAG_TEMPORARY |
                      base::File::FLAG_DELETE_ON_CLOSE);

  if (!file.IsValid())
    return ppapi::FileErrorToPepperError(file.error_details());

  IPC::PlatformFileForTransit transit_file =
      IPC::TakeFileHandleForProcess(file.Pass(), plugin_process_handle_);
  ppapi::host::ReplyMessageContext reply_context =
      context->MakeReplyMessageContext();
  reply_context.params.AppendHandle(ppapi::proxy::SerializedHandle(
      ppapi::proxy::SerializedHandle::FILE, transit_file));
  SendReply(reply_context, IPC::Message());
  return PP_OK_COMPLETIONPENDING;
}

base::FilePath PepperFlashFileMessageFilter::ValidateAndConvertPepperFilePath(
    const ppapi::PepperFilePath& pepper_path,
    const CheckPermissionsCallback& check_permissions_callback) const {
  base::FilePath file_path;  // Empty path returned on error.
  switch (pepper_path.domain()) {
    case ppapi::PepperFilePath::DOMAIN_ABSOLUTE:
      if (pepper_path.path().IsAbsolute() &&
          check_permissions_callback.Run(render_process_id_,
                                         pepper_path.path()))
        file_path = pepper_path.path();
      break;
    case ppapi::PepperFilePath::DOMAIN_MODULE_LOCAL:
      // This filter provides the module name portion of the path to prevent
      // plugins from accessing each other's data.
      if (!plugin_data_directory_.empty() &&
          !pepper_path.path().IsAbsolute() &&
          !pepper_path.path().ReferencesParent())
        file_path = plugin_data_directory_.Append(pepper_path.path());
      break;
    default:
      NOTREACHED();
      break;
  }
  return file_path;
}

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */