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 "chrome/browser/safe_browsing/sandboxed_zip_analyzer.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/threading/sequenced_worker_pool.h"
12 #include "chrome/common/chrome_utility_messages.h"
13 #include "chrome/common/safe_browsing/zip_analyzer.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/child_process_data.h"
16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/common/content_switches.h"
18 #include "ipc/ipc_message_macros.h"
19 #include "ipc/ipc_platform_file.h"
20
21 using content::BrowserThread;
22
23 namespace safe_browsing {
24
SandboxedZipAnalyzer(const base::FilePath & zip_file,const ResultCallback & result_callback)25 SandboxedZipAnalyzer::SandboxedZipAnalyzer(
26 const base::FilePath& zip_file,
27 const ResultCallback& result_callback)
28 : zip_file_name_(zip_file),
29 callback_(result_callback),
30 callback_called_(false) {
31 }
32
Start()33 void SandboxedZipAnalyzer::Start() {
34 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
35 // Starting the analyzer will block on opening the zip file, so run this
36 // on a worker thread. The task does not need to block shutdown.
37 if (!BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
38 FROM_HERE,
39 base::Bind(&SandboxedZipAnalyzer::AnalyzeInSandbox, this),
40 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)) {
41 NOTREACHED();
42 }
43 }
44
~SandboxedZipAnalyzer()45 SandboxedZipAnalyzer::~SandboxedZipAnalyzer() {
46 // If we're using UtilityProcessHost, we may not be destroyed on
47 // the UI or IO thread.
48 }
49
AnalyzeInSandbox()50 void SandboxedZipAnalyzer::AnalyzeInSandbox() {
51 zip_file_.Initialize(zip_file_name_,
52 base::File::FLAG_OPEN | base::File::FLAG_READ);
53 if (!zip_file_.IsValid()) {
54 VLOG(1) << "Could not open zip file: " << zip_file_name_.value();
55 if (!BrowserThread::PostTask(
56 BrowserThread::IO, FROM_HERE,
57 base::Bind(&SandboxedZipAnalyzer::OnAnalyzeZipFileFinished, this,
58 zip_analyzer::Results()))) {
59 NOTREACHED();
60 }
61 return;
62 }
63
64 BrowserThread::PostTask(
65 BrowserThread::IO, FROM_HERE,
66 base::Bind(&SandboxedZipAnalyzer::StartProcessOnIOThread, this));
67 // The file will be closed on the IO thread once it has been handed
68 // off to the child process.
69 }
70
OnMessageReceived(const IPC::Message & message)71 bool SandboxedZipAnalyzer::OnMessageReceived(const IPC::Message& message) {
72 bool handled = true;
73 IPC_BEGIN_MESSAGE_MAP(SandboxedZipAnalyzer, message)
74 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted,
75 OnUtilityProcessStarted)
76 IPC_MESSAGE_HANDLER(
77 ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished,
78 OnAnalyzeZipFileFinished)
79 IPC_MESSAGE_UNHANDLED(handled = false)
80 IPC_END_MESSAGE_MAP()
81 return handled;
82 }
83
OnAnalyzeZipFileFinished(const zip_analyzer::Results & results)84 void SandboxedZipAnalyzer::OnAnalyzeZipFileFinished(
85 const zip_analyzer::Results& results) {
86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
87 if (callback_called_)
88 return;
89 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
90 base::Bind(callback_, results));
91 callback_called_ = true;
92 }
93
StartProcessOnIOThread()94 void SandboxedZipAnalyzer::StartProcessOnIOThread() {
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
96 utility_process_host_ = content::UtilityProcessHost::Create(
97 this,
98 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get())
99 ->AsWeakPtr();
100 utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
101 // Wait for the startup notification before sending the main IPC to the
102 // utility process, so that we can dup the file handle.
103 }
104
OnUtilityProcessStarted()105 void SandboxedZipAnalyzer::OnUtilityProcessStarted() {
106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
107 base::ProcessHandle utility_process =
108 content::RenderProcessHost::run_renderer_in_process() ?
109 base::GetCurrentProcessHandle() :
110 utility_process_host_->GetData().handle;
111
112 if (utility_process == base::kNullProcessHandle) {
113 DLOG(ERROR) << "Child process handle is null";
114 }
115 utility_process_host_->Send(
116 new ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection(
117 IPC::TakeFileHandleForProcess(zip_file_.Pass(), utility_process)));
118 }
119
120 } // namespace safe_browsing
121