summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Sesek <rsesek@chromium.org>2015-08-21 18:12:15 -0400
committerRobert Sesek <rsesek@chromium.org>2015-08-21 22:13:17 +0000
commit23cbfc1d685fa7389e88588584e02786820d4d26 (patch)
tree9845d5f0a2fc877e1ce9149cf92f1fc04c4bb9b6
parent902c40650be8afef499d7de6e1fc60532f06cc2b (diff)
downloadchromium_src-23cbfc1d685fa7389e88588584e02786820d4d26.zip
chromium_src-23cbfc1d685fa7389e88588584e02786820d4d26.tar.gz
chromium_src-23cbfc1d685fa7389e88588584e02786820d4d26.tar.bz2
Add the SandboxedDMGParser and wire it up to the DownloadProtectionService.
BUG=496898,464083 R=isherman@chromium.org, kenrb@chromium.org, mattm@chromium.org, thestig@chromium.org Review URL: https://codereview.chromium.org/1299223006 . Cr-Commit-Position: refs/heads/master@{#344876}
-rw-r--r--chrome/browser/safe_browsing/download_protection_service.cc74
-rw-r--r--chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.cc109
-rw-r--r--chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.h70
-rw-r--r--chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac_unittest.cc143
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi4
-rw-r--r--chrome/common/chrome_utility_messages.h31
-rwxr-xr-xchrome/test/data/safe_browsing/dmg/generate_test_data.sh18
-rw-r--r--chrome/utility/chrome_content_utility_client.cc21
-rw-r--r--chrome/utility/chrome_content_utility_client.h4
-rw-r--r--chrome/utility/safe_browsing/mac/hfs.cc2
-rw-r--r--tools/metrics/histograms/histograms.xml25
12 files changed, 492 insertions, 11 deletions
diff --git a/chrome/browser/safe_browsing/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection_service.cc
index 1215b2f..14cb18a 100644
--- a/chrome/browser/safe_browsing/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection_service.cc
@@ -54,6 +54,10 @@
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_status.h"
+#if defined(OS_MACOSX)
+#include "chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.h"
+#endif
+
using content::BrowserThread;
namespace {
@@ -266,7 +270,7 @@ class DownloadProtectionService::CheckClientDownloadRequest
referrer_url_(item->GetReferrerUrl()),
tab_url_(item->GetTabUrl()),
tab_referrer_url_(item->GetTabReferrerUrl()),
- zipped_executable_(false),
+ archived_executable_(false),
callback_(callback),
service_(service),
binary_feature_extractor_(binary_feature_extractor),
@@ -315,6 +319,11 @@ class DownloadProtectionService::CheckClientDownloadRequest
if (item_->GetTargetFilePath().MatchesExtension(
FILE_PATH_LITERAL(".zip"))) {
StartExtractZipFeatures();
+#if defined(OS_MACOSX)
+ } else if (item_->GetTargetFilePath().MatchesExtension(
+ FILE_PATH_LITERAL(".dmg"))) {
+ StartExtractDmgFeatures();
+#endif
} else {
DCHECK(!download_protection_util::IsArchiveFile(
item_->GetTargetFilePath()));
@@ -551,7 +560,7 @@ class DownloadProtectionService::CheckClientDownloadRequest
if (!service_)
return;
if (results.success) {
- zipped_executable_ = results.has_executable;
+ archived_executable_ = results.has_executable;
archived_binary_.CopyFrom(results.archived_binary);
DVLOG(1) << "Zip analysis finished for " << item_->GetFullPath().value()
<< ", has_executable=" << results.has_executable
@@ -560,24 +569,67 @@ class DownloadProtectionService::CheckClientDownloadRequest
DVLOG(1) << "Zip analysis failed for " << item_->GetFullPath().value();
}
UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
- zipped_executable_);
+ archived_executable_);
UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable",
- results.has_archive && !zipped_executable_);
+ results.has_archive && !archived_executable_);
UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime",
base::TimeTicks::Now() - zip_analysis_start_time_);
for (const auto& file_extension : results.archived_archive_filetypes)
RecordArchivedArchiveFileExtensionType(file_extension);
- if (!zipped_executable_ && !results.has_archive) {
+ if (!archived_executable_ && !results.has_archive) {
PostFinishTask(UNKNOWN, REASON_ARCHIVE_WITHOUT_BINARIES);
return;
}
- if (!zipped_executable_ && results.has_archive)
+ if (!archived_executable_ && results.has_archive)
type_ = ClientDownloadRequest::ZIPPED_ARCHIVE;
OnFileFeatureExtractionDone();
}
+#if defined(OS_MACOSX)
+ void StartExtractDmgFeatures() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(item_);
+ dmg_analyzer_ = new SandboxedDMGAnalyzer(
+ item_->GetFullPath(),
+ base::Bind(&CheckClientDownloadRequest::OnDmgAnalysisFinished,
+ weakptr_factory_.GetWeakPtr()));
+ dmg_analyzer_->Start();
+ dmg_analysis_start_time_ = base::TimeTicks::Now();
+ }
+
+ void OnDmgAnalysisFinished(const zip_analyzer::Results& results) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK_EQ(ClientDownloadRequest::MAC_EXECUTABLE, type_);
+ if (!service_)
+ return;
+
+ if (results.success) {
+ archived_executable_ = results.has_executable;
+ archived_binary_.CopyFrom(results.archived_binary);
+ DVLOG(1) << "DMG analysis has finished for "
+ << item_->GetFullPath().value() << ", has_executable="
+ << results.has_executable;
+ } else {
+ DVLOG(1) << "DMG analysis failed for "<< item_->GetFullPath().value();
+ }
+
+ UMA_HISTOGRAM_BOOLEAN("SBClientDownload.DmgFileSuccess", results.success);
+ UMA_HISTOGRAM_BOOLEAN("SBClientDownload.DmgFileHasExecutable",
+ archived_executable_);
+ UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractDmgFeaturesTime",
+ base::TimeTicks::Now() - dmg_analysis_start_time_);
+
+ if (!archived_executable_) {
+ PostFinishTask(UNKNOWN, REASON_ARCHIVE_WITHOUT_BINARIES);
+ return;
+ }
+
+ OnFileFeatureExtractionDone();
+ }
+#endif // defined(OS_MACOSX)
+
static void RecordCountOfSignedOrWhitelistedDownload() {
UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
}
@@ -748,7 +800,7 @@ class DownloadProtectionService::CheckClientDownloadRequest
request.mutable_signature()->CopyFrom(signature_info_);
if (image_headers_)
request.set_allocated_image_headers(image_headers_.release());
- if (zipped_executable_)
+ if (archived_executable_)
request.mutable_archived_binary()->Swap(&archived_binary_);
if (!request.SerializeToString(&client_download_request_data_)) {
FinishRequest(UNKNOWN, REASON_INVALID_REQUEST_PROTO);
@@ -758,6 +810,8 @@ class DownloadProtectionService::CheckClientDownloadRequest
DVLOG(2) << "Sending a request for URL: "
<< item_->GetUrlChain().back();
+ DVLOG(2) << "Detected " << request.archived_binary().size() << " archived "
+ << "binaries";
fetcher_ = net::URLFetcher::Create(0 /* ID used for testing */,
GetDownloadRequestUrl(),
net::URLFetcher::POST, this);
@@ -878,7 +932,7 @@ class DownloadProtectionService::CheckClientDownloadRequest
GURL tab_url_;
GURL tab_referrer_url_;
- bool zipped_executable_;
+ bool archived_executable_;
ClientDownloadRequest_SignatureInfo signature_info_;
scoped_ptr<ClientDownloadRequest_ImageHeaders> image_headers_;
google::protobuf::RepeatedPtrField<ClientDownloadRequest_ArchivedBinary>
@@ -892,6 +946,10 @@ class DownloadProtectionService::CheckClientDownloadRequest
scoped_ptr<net::URLFetcher> fetcher_;
scoped_refptr<SandboxedZipAnalyzer> analyzer_;
base::TimeTicks zip_analysis_start_time_;
+#if defined(OS_MACOSX)
+ scoped_refptr<SandboxedDMGAnalyzer> dmg_analyzer_;
+ base::TimeTicks dmg_analysis_start_time_;
+#endif
bool finished_;
ClientDownloadRequest::DownloadType type_;
std::string client_download_request_data_;
diff --git a/chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.cc b/chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.cc
new file mode 100644
index 0000000..81da207
--- /dev/null
+++ b/chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.cc
@@ -0,0 +1,109 @@
+// Copyright 2015 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 "chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.h"
+
+#include "base/bind.h"
+#include "chrome/common/chrome_utility_messages.h"
+#include "chrome/common/safe_browsing/zip_analyzer_results.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/render_process_host.h"
+#include "ipc/ipc_message_macros.h"
+#include "ipc/ipc_platform_file.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using content::BrowserThread;
+
+namespace safe_browsing {
+
+SandboxedDMGAnalyzer::SandboxedDMGAnalyzer(const base::FilePath& dmg_file,
+ const ResultsCallback& callback)
+ : file_path_(dmg_file), callback_(callback), callback_called_(false) {
+}
+
+SandboxedDMGAnalyzer::~SandboxedDMGAnalyzer() {}
+
+void SandboxedDMGAnalyzer::Start() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE, base::Bind(&SandboxedDMGAnalyzer::OpenDMGFile, this),
+ base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)) {
+ NOTREACHED();
+ }
+}
+
+void SandboxedDMGAnalyzer::OpenDMGFile() {
+ DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
+
+ file_.Initialize(file_path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ if (!file_.IsValid()) {
+ DLOG(ERROR) << "Could not open DMG file at path " << file_path_.value();
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&SandboxedDMGAnalyzer::OnAnalysisFinished, this,
+ zip_analyzer::Results()));
+ return;
+ }
+
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&SandboxedDMGAnalyzer::StartAnalysis,
+ this));
+}
+
+void SandboxedDMGAnalyzer::StartAnalysis() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ utility_process_host_ =
+ content::UtilityProcessHost::Create(this,
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))
+ ->AsWeakPtr();
+
+ utility_process_host_->SetName(l10n_util::GetStringUTF16(
+ IDS_UTILITY_PROCESS_SAFE_BROWSING_ZIP_FILE_ANALYZER_NAME));
+ utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
+}
+
+bool SandboxedDMGAnalyzer::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(SandboxedDMGAnalyzer, message)
+ IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted,
+ OnUtilityProcessStarted)
+ IPC_MESSAGE_HANDLER(
+ ChromeUtilityHostMsg_AnalyzeDmgFileForDownloadProtection_Finished,
+ OnAnalysisFinished)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void SandboxedDMGAnalyzer::OnUtilityProcessStarted() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ base::ProcessHandle utility_process =
+ content::RenderProcessHost::run_renderer_in_process() ?
+ base::GetCurrentProcessHandle() :
+ utility_process_host_->GetData().handle;
+ if (utility_process == base::kNullProcessHandle) {
+ DLOG(ERROR) << "Child process handle is null";
+ }
+
+ utility_process_host_->Send(
+ new ChromeUtilityMsg_AnalyzeDmgFileForDownloadProtection(
+ IPC::TakeFileHandleForProcess(file_.Pass(), utility_process)));
+}
+
+void SandboxedDMGAnalyzer::OnAnalysisFinished(
+ const zip_analyzer::Results& results) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (callback_called_)
+ return;
+
+ callback_called_ = true;
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(callback_, results));
+}
+
+} // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.h b/chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.h
new file mode 100644
index 0000000..c9510e5
--- /dev/null
+++ b/chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.h
@@ -0,0 +1,70 @@
+// Copyright 2015 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.
+
+#ifndef CHROME_BROWSER_SAFE_BROWSING_SANDBOXED_DMG_ANALYZER_MAC_H_
+#define CHROME_BROWSER_SAFE_BROWSING_SANDBOXED_DMG_ANALYZER_MAC_H_
+
+#include "base/callback.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/utility_process_host.h"
+#include "content/public/browser/utility_process_host_client.h"
+
+namespace safe_browsing {
+
+namespace zip_analyzer {
+struct Results;
+}
+
+// This class is used to analyze DMG files in a sandboxed utility process for
+// file download protection. This class must be created on the UI thread, and
+// which is where the result callback will be invoked.
+class SandboxedDMGAnalyzer : public content::UtilityProcessHostClient {
+ public:
+ // Callback that is invoked when the analysis results are ready.
+ using ResultsCallback = base::Callback<void(const zip_analyzer::Results&)>;
+
+ SandboxedDMGAnalyzer(const base::FilePath& dmg_file,
+ const ResultsCallback& callback);
+
+ // Begins the analysis. This must be called on the UI thread.
+ void Start();
+
+ private:
+ ~SandboxedDMGAnalyzer() override;
+
+ // Called on the blocking pool, this opens the DMG file, in preparation for
+ // sending the FD to the utility process.
+ void OpenDMGFile();
+
+ // Called on the IO thread, this starts the utility process.
+ void StartAnalysis();
+
+ // content::UtilityProcessHostClient:
+ bool OnMessageReceived(const IPC::Message& message) override;
+
+ // Message handler for reply ping when the utility process has started.
+ void OnUtilityProcessStarted();
+
+ // Message handler to receive the results of the analysis. Invokes the
+ // |callback_|.
+ void OnAnalysisFinished(const zip_analyzer::Results& results);
+
+ const base::FilePath file_path_; // The path of the DMG file.
+ base::File file_; // The opened file handle for |file_path_|.
+
+ // Weak reference to the utility process, which owns this.
+ base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
+
+ const ResultsCallback callback_; // Result callback.
+ bool callback_called_; // Whether |callback_| has already been invoked.
+
+ DISALLOW_COPY_AND_ASSIGN(SandboxedDMGAnalyzer);
+};
+
+} // namespace safe_browsing
+
+#endif // CHROME_BROWSER_SAFE_BROWSING_SANDBOXED_DMG_ANALYZER_MAC_H_
diff --git a/chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac_unittest.cc b/chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac_unittest.cc
new file mode 100644
index 0000000..e0f3c4b
--- /dev/null
+++ b/chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright 2015 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 "chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.h"
+
+#include <mach-o/loader.h>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/safe_browsing/zip_analyzer_results.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+namespace {
+
+class SandboxedDMGAnalyzerTest : public testing::Test {
+ public:
+ SandboxedDMGAnalyzerTest()
+ : browser_thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
+ }
+
+ void AnalyzeFile(const base::FilePath& path,
+ zip_analyzer::Results* results) {
+ base::RunLoop run_loop;
+ ResultsGetter results_getter(run_loop.QuitClosure(), results);
+ scoped_refptr<SandboxedDMGAnalyzer> analyzer(
+ new SandboxedDMGAnalyzer(path, results_getter.GetCallback()));
+ analyzer->Start();
+ run_loop.Run();
+ }
+
+ base::FilePath GetFilePath(const char* file_name) {
+ base::FilePath test_data;
+ EXPECT_TRUE(PathService::Get(chrome::DIR_GEN_TEST_DATA, &test_data));
+ return test_data.AppendASCII("chrome")
+ .AppendASCII("safe_browsing_dmg")
+ .AppendASCII(file_name);
+ }
+
+ private:
+ // A helper class to store the results from the ResultsCallback and run
+ // another closure.
+ class ResultsGetter {
+ public:
+ ResultsGetter(const base::Closure& next_closure,
+ zip_analyzer::Results* results)
+ : next_closure_(next_closure), results_(results) {}
+
+ SandboxedDMGAnalyzer::ResultsCallback GetCallback() {
+ return base::Bind(&ResultsGetter::ResultsCallback,
+ base::Unretained(this));
+ }
+
+ private:
+ void ResultsCallback(const zip_analyzer::Results& results) {
+ *results_ = results;
+ next_closure_.Run();
+ }
+
+ base::Closure next_closure_;
+ zip_analyzer::Results* results_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResultsGetter);
+ };
+
+ content::TestBrowserThreadBundle browser_thread_bundle_;
+ content::InProcessUtilityThreadHelper utility_thread_helper_;
+};
+
+TEST_F(SandboxedDMGAnalyzerTest, AnalyzeDMG) {
+ base::FilePath path;
+ ASSERT_NO_FATAL_FAILURE(path = GetFilePath("mach_o_in_dmg.dmg"));
+
+ zip_analyzer::Results results;
+ AnalyzeFile(path, &results);
+
+ EXPECT_TRUE(results.success);
+ EXPECT_TRUE(results.has_executable);
+ EXPECT_EQ(2, results.archived_binary.size());
+
+ bool got_executable = false, got_dylib = false;
+ for (const auto& binary : results.archived_binary) {
+ const std::string& file_name = binary.file_basename();
+ const google::protobuf::RepeatedPtrField<
+ ClientDownloadRequest_MachOHeaders>& headers =
+ binary.image_headers().mach_o_headers();
+
+ EXPECT_EQ(ClientDownloadRequest_DownloadType_MAC_EXECUTABLE,
+ binary.download_type());
+
+ if (file_name.find("executablefat") != std::string::npos) {
+ got_executable = true;
+ ASSERT_EQ(2, headers.size());
+
+ const ClientDownloadRequest_MachOHeaders& arch32 = headers.Get(0);
+ EXPECT_EQ(15, arch32.load_commands().size());
+ EXPECT_EQ(MH_MAGIC,
+ *reinterpret_cast<const uint32_t*>(arch32.mach_header().c_str()));
+
+ const ClientDownloadRequest_MachOHeaders& arch64 = headers.Get(1);
+ EXPECT_EQ(15, arch64.load_commands().size());
+ EXPECT_EQ(MH_MAGIC_64,
+ *reinterpret_cast<const uint32_t*>(arch64.mach_header().c_str()));
+
+ const std::string& sha256_bytes = binary.digests().sha256();
+ std::string actual_sha256 = base::HexEncode(sha256_bytes.c_str(),
+ sha256_bytes.size());
+ EXPECT_EQ(
+ "E462FF752FF9D84E34D843E5D46E2012ADCBD48540A8473FB794B286A389B945",
+ actual_sha256);
+ } else if (file_name.find("lib64.dylib") != std::string::npos) {
+ got_dylib = true;
+ ASSERT_EQ(1, headers.size());
+
+ const ClientDownloadRequest_MachOHeaders& arch = headers.Get(0);
+ EXPECT_EQ(13, arch.load_commands().size());
+ EXPECT_EQ(MH_MAGIC_64,
+ *reinterpret_cast<const uint32_t*>(arch.mach_header().c_str()));
+
+ const std::string& sha256_bytes = binary.digests().sha256();
+ std::string actual_sha256 = base::HexEncode(sha256_bytes.c_str(),
+ sha256_bytes.size());
+ EXPECT_EQ(
+ "2012CE4987B0FA4A5D285DF7E810560E841CFAB3054BC19E1AAB345F862A6C4E",
+ actual_sha256);
+ } else {
+ ADD_FAILURE() << "Unepxected result file " << binary.file_basename();
+ }
+ }
+
+ EXPECT_TRUE(got_executable);
+ EXPECT_TRUE(got_dylib);
+}
+
+} // namespace
+} // namespace safe_browsing
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 9fdac30..a5dff03 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2530,6 +2530,8 @@
'browser/safe_browsing/safe_browsing_store.h',
'browser/safe_browsing/safe_browsing_store_file.cc',
'browser/safe_browsing/safe_browsing_store_file.h',
+ 'browser/safe_browsing/sandboxed_dmg_analyzer_mac.cc',
+ 'browser/safe_browsing/sandboxed_dmg_analyzer_mac.h',
'browser/safe_browsing/sandboxed_zip_analyzer.cc',
'browser/safe_browsing/sandboxed_zip_analyzer.h',
'browser/safe_browsing/two_phase_uploader.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index b764382..e122706 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -1011,6 +1011,7 @@
'browser/safe_browsing/safe_browsing_store_file_unittest.cc',
'browser/safe_browsing/safe_browsing_store_unittest.cc',
'browser/safe_browsing/safe_browsing_util_unittest.cc',
+ 'browser/safe_browsing/sandboxed_dmg_analyzer_mac_unittest.cc',
'browser/safe_browsing/sandboxed_zip_analyzer_unittest.cc',
'browser/safe_browsing/test_database_manager.cc',
'browser/safe_browsing/test_database_manager.h',
@@ -2729,6 +2730,8 @@
'inputs': [
'<(generate_test_data)',
'test/data/safe_browsing/dmg/make_hfs.sh',
+ 'test/data/safe_browsing/mach_o/executablefat',
+ 'test/data/safe_browsing/mach_o/lib64.dylib',
],
'outputs': [
'<(PRODUCT_DIR)/test_data/chrome/safe_browsing_dmg/dmg_UDBZ_GPTSPUD.dmg',
@@ -2757,6 +2760,7 @@
'<(PRODUCT_DIR)/test_data/chrome/safe_browsing_dmg/dmg_UFBI_SPUD.dmg',
'<(PRODUCT_DIR)/test_data/chrome/safe_browsing_dmg/hfs_plus.img',
'<(PRODUCT_DIR)/test_data/chrome/safe_browsing_dmg/hfsx_case_sensitive.img',
+ '<(PRODUCT_DIR)/test_data/chrome/safe_browsing_dmg/mach_o_in_dmg.dmg',
],
'action': [ '<(generate_test_data)', '<(PRODUCT_DIR)/test_data/chrome/safe_browsing_dmg' ],
},
diff --git a/chrome/common/chrome_utility_messages.h b/chrome/common/chrome_utility_messages.h
index e7ff9a4..d9f7155 100644
--- a/chrome/common/chrome_utility_messages.h
+++ b/chrome/common/chrome_utility_messages.h
@@ -87,8 +87,21 @@ IPC_PROTOBUF_MESSAGE_TRAITS_BEGIN(
IPC_PROTOBUF_MESSAGE_TRAITS_END()
IPC_PROTOBUF_MESSAGE_TRAITS_BEGIN(
+ safe_browsing::ClientDownloadRequest_MachOHeaders_LoadCommand)
+ IPC_PROTOBUF_MESSAGE_TRAITS_OPTIONAL_FUNDAMENTAL_MEMBER(command_id)
+ IPC_PROTOBUF_MESSAGE_TRAITS_REPEATED_COMPLEX_MEMBER(command)
+IPC_PROTOBUF_MESSAGE_TRAITS_END()
+
+IPC_PROTOBUF_MESSAGE_TRAITS_BEGIN(
+ safe_browsing::ClientDownloadRequest_MachOHeaders)
+ IPC_PROTOBUF_MESSAGE_TRAITS_REPEATED_COMPLEX_MEMBER(mach_header)
+ IPC_PROTOBUF_MESSAGE_TRAITS_REPEATED_COMPLEX_MEMBER(load_commands)
+IPC_PROTOBUF_MESSAGE_TRAITS_END()
+
+IPC_PROTOBUF_MESSAGE_TRAITS_BEGIN(
safe_browsing::ClientDownloadRequest_ImageHeaders)
IPC_PROTOBUF_MESSAGE_TRAITS_OPTIONAL_COMPLEX_MEMBER(pe_headers)
+ IPC_PROTOBUF_MESSAGE_TRAITS_REPEATED_COMPLEX_MEMBER(mach_o_headers)
IPC_PROTOBUF_MESSAGE_TRAITS_END()
IPC_PROTOBUF_MESSAGE_TRAITS_BEGIN(
@@ -176,7 +189,14 @@ IPC_MESSAGE_CONTROL0(ChromeUtilityMsg_StartupPing)
IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection,
IPC::PlatformFileForTransit /* zip_file */,
IPC::PlatformFileForTransit /* temp_file */)
-#endif
+
+#if defined(OS_MACOSX)
+// Tells the utility process to analyze a DMG file for malicious download
+// protection.
+IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_AnalyzeDmgFileForDownloadProtection,
+ IPC::PlatformFileForTransit /* dmg_file */)
+#endif // defined(OS_MACOSX)
+#endif // defined(FULL_SAFE_BROWSING)
#if defined(OS_WIN)
// Invokes ui::base::win::OpenFileViaShell from the utility process.
@@ -243,7 +263,14 @@ IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_ProcessStarted)
IPC_MESSAGE_CONTROL1(
ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished,
safe_browsing::zip_analyzer::Results)
-#endif
+
+#if defined(OS_MACOSX)
+// Reply when a DMG file has been analyzed for malicious download protection.
+IPC_MESSAGE_CONTROL1(
+ ChromeUtilityHostMsg_AnalyzeDmgFileForDownloadProtection_Finished,
+ safe_browsing::zip_analyzer::Results)
+#endif // defined(OS_MACOSX)
+#endif // defined(FULL_SAFE_BROWSING)
#if defined(OS_WIN)
IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_GetOpenFileName_Failed)
diff --git a/chrome/test/data/safe_browsing/dmg/generate_test_data.sh b/chrome/test/data/safe_browsing/dmg/generate_test_data.sh
index efeb3e9..8bf21d5 100755
--- a/chrome/test/data/safe_browsing/dmg/generate_test_data.sh
+++ b/chrome/test/data/safe_browsing/dmg/generate_test_data.sh
@@ -50,6 +50,24 @@ generate_test_data() {
done
rm -rf "${DMG_SOURCE}"
+
+ # DMG With Mach-O ############################################################
+
+ mkdir "${DMG_SOURCE}"
+
+ FAKE_APP="${DMG_SOURCE}/Foo.app/Contents/MacOS/"
+ mkdir -p "${FAKE_APP}"
+ cp "${THIS_DIR}/../mach_o/executablefat" "${FAKE_APP}"
+ touch "${FAKE_APP}/../Info.plist"
+
+ mkdir "${DMG_SOURCE}/.hidden"
+ cp "${THIS_DIR}/../mach_o/lib64.dylib" "${DMG_SOURCE}/.hidden/"
+
+ hdiutil create -srcfolder "${DMG_SOURCE}" \
+ -format UDZO -layout SPUD -volname "Mach-O in DMG" -ov \
+ "${OUT_DIR}/mach_o_in_dmg"
+
+ rm -rf "${DMG_SOURCE}"
}
# Silence any log output.
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 440ff96..3d09c61 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -59,6 +59,10 @@
#include "chrome/utility/local_discovery/service_discovery_message_handler.h"
#endif
+#if defined(OS_MACOSX) && defined(FULL_SAFE_BROWSING)
+#include "chrome/utility/safe_browsing/mac/dmg_analyzer.h"
+#endif
+
namespace {
bool Send(IPC::Message* message) {
@@ -192,6 +196,10 @@ bool ChromeContentUtilityClient::OnMessageReceived(
#if defined(FULL_SAFE_BROWSING)
IPC_MESSAGE_HANDLER(ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection,
OnAnalyzeZipFileForDownloadProtection)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(ChromeUtilityMsg_AnalyzeDmgFileForDownloadProtection,
+ OnAnalyzeDmgFileForDownloadProtection)
+#endif
#endif
#if defined(ENABLE_EXTENSIONS)
IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseMediaMetadata,
@@ -396,6 +404,19 @@ void ChromeContentUtilityClient::OnAnalyzeZipFileForDownloadProtection(
results));
ReleaseProcessIfNeeded();
}
+
+#if defined(OS_MACOSX)
+void ChromeContentUtilityClient::OnAnalyzeDmgFileForDownloadProtection(
+ const IPC::PlatformFileForTransit& dmg_file) {
+ safe_browsing::zip_analyzer::Results results;
+ safe_browsing::dmg::AnalyzeDMGFile(
+ IPC::PlatformFileForTransitToFile(dmg_file), &results);
+ Send(new ChromeUtilityHostMsg_AnalyzeDmgFileForDownloadProtection_Finished(
+ results));
+ ReleaseProcessIfNeeded();
+}
+#endif
+
#endif
#if defined(ENABLE_EXTENSIONS)
diff --git a/chrome/utility/chrome_content_utility_client.h b/chrome/utility/chrome_content_utility_client.h
index 1205c7b..039791b 100644
--- a/chrome/utility/chrome_content_utility_client.h
+++ b/chrome/utility/chrome_content_utility_client.h
@@ -69,6 +69,10 @@ class ChromeContentUtilityClient : public content::ContentUtilityClient {
void OnAnalyzeZipFileForDownloadProtection(
const IPC::PlatformFileForTransit& zip_file,
const IPC::PlatformFileForTransit& temp_file);
+#if defined(OS_MACOSX)
+ void OnAnalyzeDmgFileForDownloadProtection(
+ const IPC::PlatformFileForTransit& dmg_file);
+#endif
#endif
#if defined(ENABLE_EXTENSIONS)
void OnParseMediaMetadata(const std::string& mime_type,
diff --git a/chrome/utility/safe_browsing/mac/hfs.cc b/chrome/utility/safe_browsing/mac/hfs.cc
index da9f5d7..185d11b 100644
--- a/chrome/utility/safe_browsing/mac/hfs.cc
+++ b/chrome/utility/safe_browsing/mac/hfs.cc
@@ -411,7 +411,7 @@ bool HFSForkReadStream::Read(uint8_t* buffer,
off_t HFSForkReadStream::Seek(off_t offset, int whence) {
DCHECK_EQ(SEEK_SET, whence);
DCHECK_GE(offset, 0);
- DCHECK_LT(static_cast<uint64_t>(offset), fork_.logicalSize);
+ DCHECK(offset == 0 || static_cast<uint64_t>(offset) < fork_.logicalSize);
size_t target_block = offset / hfs_->block_size();
size_t block_count = 0;
for (size_t i = 0; i < arraysize(fork_.extents); ++i) {
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 5caffb5..cff294a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -37704,6 +37704,23 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
</summary>
</histogram>
+<histogram name="SBClientDownload.DmgFileHasExecutable" enum="Boolean">
+ <owner>rsesek@chromium.org</owner>
+ <summary>
+ For each DMG file analyzed by the SafeBrowsing download service, records if
+ the DMG contained an executable file.
+ </summary>
+</histogram>
+
+<histogram name="SBClientDownload.DmgFileSuccess" enum="BooleanSuccess">
+ <owner>rsesek@chromium.org</owner>
+ <summary>
+ For each DMG file analyzed by the SafeBrowsing download service, records
+ true if the analysis was successful, or false if there was an error
+ analyzing the file.
+ </summary>
+</histogram>
+
<histogram name="SBClientDownload.DownloadExtensions"
enum="SBClientDownloadExtensions">
<owner>mattm@chromium.org</owner>
@@ -37786,6 +37803,14 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
</summary>
</histogram>
+<histogram name="SBClientDownload.ExtractDmgFeaturesTime" units="milliseconds">
+ <owner>rsesek@chromium.org</owner>
+ <summary>
+ Records the time it takes for the SafeBrowsing download service to extract
+ info from a downloaded DMG file.
+ </summary>
+</histogram>
+
<histogram name="SBClientDownload.ExtractImageHeadersTime" units="milliseconds">
<owner>grt@chromium.org</owner>
<summary>