12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/safe_browsing/sandboxed_zip_analyzer.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/command_line.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/location.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/logging.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/threading/sequenced_worker_pool.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/chrome_utility_messages.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/safe_browsing/zip_analyzer.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/browser_thread.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/child_process_data.h"
167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "content/public/browser/render_process_host.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/common/content_switches.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ipc/ipc_message_macros.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ipc/ipc_platform_file.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::BrowserThread;
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace safe_browsing {
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SandboxedZipAnalyzer::SandboxedZipAnalyzer(
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& zip_file,
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ResultCallback& result_callback)
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : zip_file_name_(zip_file),
29eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      callback_(result_callback),
30eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      callback_called_(false) {
31eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SandboxedZipAnalyzer::Start() {
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Starting the analyzer will block on opening the zip file, so run this
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // on a worker thread.  The task does not need to block shutdown.
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          FROM_HERE,
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          base::Bind(&SandboxedZipAnalyzer::AnalyzeInSandbox, this),
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)) {
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    NOTREACHED();
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SandboxedZipAnalyzer::~SandboxedZipAnalyzer() {
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If we're using UtilityProcessHost, we may not be destroyed on
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the UI or IO thread.
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SandboxedZipAnalyzer::AnalyzeInSandbox() {
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  zip_file_.Initialize(zip_file_name_,
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                       base::File::FLAG_OPEN | base::File::FLAG_READ);
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!zip_file_.IsValid()) {
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    VLOG(1) << "Could not open zip file: " << zip_file_name_.value();
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!BrowserThread::PostTask(
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            BrowserThread::IO, FROM_HERE,
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            base::Bind(&SandboxedZipAnalyzer::OnAnalyzeZipFileFinished, this,
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       zip_analyzer::Results()))) {
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTREACHED();
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
647dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  BrowserThread::PostTask(
657dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      BrowserThread::IO, FROM_HERE,
667dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      base::Bind(&SandboxedZipAnalyzer::StartProcessOnIOThread, this));
677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // The file will be closed on the IO thread once it has been handed
687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // off to the child process.
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SandboxedZipAnalyzer::OnMessageReceived(const IPC::Message& message) {
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool handled = true;
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IPC_BEGIN_MESSAGE_MAP(SandboxedZipAnalyzer, message)
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted,
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        OnUtilityProcessStarted)
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IPC_MESSAGE_HANDLER(
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished,
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        OnAnalyzeZipFileFinished)
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IPC_MESSAGE_UNHANDLED(handled = false)
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IPC_END_MESSAGE_MAP()
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return handled;
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SandboxedZipAnalyzer::OnAnalyzeZipFileFinished(
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const zip_analyzer::Results& results) {
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
87eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (callback_called_)
88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return;
89eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                          base::Bind(callback_, results));
91eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  callback_called_ = true;
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SandboxedZipAnalyzer::StartProcessOnIOThread() {
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  utility_process_host_ = content::UtilityProcessHost::Create(
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this,
987d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get())
997d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      ->AsWeakPtr();
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Wait for the startup notification before sending the main IPC to the
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // utility process, so that we can dup the file handle.
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SandboxedZipAnalyzer::OnUtilityProcessStarted() {
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1077dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  base::ProcessHandle utility_process =
1087dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      content::RenderProcessHost::run_renderer_in_process() ?
1097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          base::GetCurrentProcessHandle() :
1107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          utility_process_host_->GetData().handle;
1117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
1127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (utility_process == base::kNullProcessHandle) {
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DLOG(ERROR) << "Child process handle is null";
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  utility_process_host_->Send(
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection(
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          IPC::TakeFileHandleForProcess(zip_file_.Pass(), utility_process)));
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace safe_browsing
121