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.
5 #include "chrome/browser/safe_browsing/sandboxed_zip_analyzer.h"
8 #include "base/command_line.h"
9 #include "base/files/file_util.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/threading/sequenced_worker_pool.h"
13 #include "chrome/common/chrome_utility_messages.h"
14 #include "chrome/common/safe_browsing/zip_analyzer_results.h"
15 #include "chrome/grit/generated_resources.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/child_process_data.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/common/content_switches.h"
20 #include "ipc/ipc_message_macros.h"
21 #include "ipc/ipc_platform_file.h"
22 #include "ui/base/l10n/l10n_util.h"
24 using content::BrowserThread
;
26 namespace safe_browsing
{
28 SandboxedZipAnalyzer::SandboxedZipAnalyzer(
29 const base::FilePath
& zip_file
,
30 const ResultCallback
& result_callback
)
31 : zip_file_name_(zip_file
),
32 callback_(result_callback
),
33 callback_called_(false) {
36 void SandboxedZipAnalyzer::Start() {
37 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
38 // Starting the analyzer will block on opening the zip file, so run this
39 // on a worker thread. The task does not need to block shutdown.
40 if (!BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
42 base::Bind(&SandboxedZipAnalyzer::AnalyzeInSandbox
, this),
43 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
)) {
48 SandboxedZipAnalyzer::~SandboxedZipAnalyzer() {
49 // If we're using UtilityProcessHost, we may not be destroyed on
50 // the UI or IO thread.
54 void SandboxedZipAnalyzer::CloseTemporaryFile() {
55 if (!temp_file_
.IsValid())
57 // Close the temporary file in the blocking pool since doing so will delete
59 if (!BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
60 FROM_HERE
, base::Bind(&base::File::Close
,
61 base::Owned(new base::File(temp_file_
.Pass()))),
62 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
)) {
67 void SandboxedZipAnalyzer::AnalyzeInSandbox() {
68 // This zip file will be closed on the IO thread once it has been handed
69 // off to the child process.
70 zip_file_
.Initialize(zip_file_name_
,
71 base::File::FLAG_OPEN
| base::File::FLAG_READ
);
72 if (!zip_file_
.IsValid()) {
73 DVLOG(1) << "Could not open zip file: " << zip_file_name_
.value();
74 if (!BrowserThread::PostTask(
75 BrowserThread::IO
, FROM_HERE
,
76 base::Bind(&SandboxedZipAnalyzer::OnAnalyzeZipFileFinished
, this,
77 zip_analyzer::Results()))) {
83 // This temp file will be closed in the blocking pool when results from the
85 base::FilePath temp_path
;
86 if (base::CreateTemporaryFile(&temp_path
)) {
87 temp_file_
.Initialize(temp_path
, (base::File::FLAG_CREATE_ALWAYS
|
88 base::File::FLAG_READ
|
89 base::File::FLAG_WRITE
|
90 base::File::FLAG_TEMPORARY
|
91 base::File::FLAG_DELETE_ON_CLOSE
));
93 DVLOG_IF(1, !temp_file_
.IsValid())
94 << "Could not open temporary output file: " << temp_path
.value();
96 BrowserThread::PostTask(
97 BrowserThread::IO
, FROM_HERE
,
98 base::Bind(&SandboxedZipAnalyzer::StartProcessOnIOThread
, this));
101 bool SandboxedZipAnalyzer::OnMessageReceived(const IPC::Message
& message
) {
103 IPC_BEGIN_MESSAGE_MAP(SandboxedZipAnalyzer
, message
)
104 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted
,
105 OnUtilityProcessStarted
)
107 ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished
,
108 OnAnalyzeZipFileFinished
)
109 IPC_MESSAGE_UNHANDLED(handled
= false)
110 IPC_END_MESSAGE_MAP()
114 void SandboxedZipAnalyzer::StartProcessOnIOThread() {
115 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
116 utility_process_host_
= content::UtilityProcessHost::Create(
118 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
).get())
120 utility_process_host_
->SetName(l10n_util::GetStringUTF16(
121 IDS_UTILITY_PROCESS_SAFE_BROWSING_ZIP_FILE_ANALYZER_NAME
));
122 utility_process_host_
->Send(new ChromeUtilityMsg_StartupPing
);
123 // Wait for the startup notification before sending the main IPC to the
124 // utility process, so that we can dup the file handle.
127 void SandboxedZipAnalyzer::OnUtilityProcessStarted() {
128 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
129 base::ProcessHandle utility_process
=
130 content::RenderProcessHost::run_renderer_in_process() ?
131 base::GetCurrentProcessHandle() :
132 utility_process_host_
->GetData().handle
;
134 if (utility_process
== base::kNullProcessHandle
) {
135 DLOG(ERROR
) << "Child process handle is null";
137 utility_process_host_
->Send(
138 new ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection(
139 IPC::TakeFileHandleForProcess(zip_file_
.Pass(), utility_process
),
140 IPC::GetFileHandleForProcess(temp_file_
.GetPlatformFile(),
142 false /* !close_source_handle */)));
145 void SandboxedZipAnalyzer::OnAnalyzeZipFileFinished(
146 const zip_analyzer::Results
& results
) {
147 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
148 if (callback_called_
)
150 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
151 base::Bind(callback_
, results
));
152 callback_called_
= true;
153 CloseTemporaryFile();
156 } // namespace safe_browsing