diff options
author | Robert Sesek <rsesek@chromium.org> | 2015-08-21 18:12:15 -0400 |
---|---|---|
committer | Robert Sesek <rsesek@chromium.org> | 2015-08-21 22:13:17 +0000 |
commit | 23cbfc1d685fa7389e88588584e02786820d4d26 (patch) | |
tree | 9845d5f0a2fc877e1ce9149cf92f1fc04c4bb9b6 | |
parent | 902c40650be8afef499d7de6e1fc60532f06cc2b (diff) | |
download | chromium_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.cc | 74 | ||||
-rw-r--r-- | chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.cc | 109 | ||||
-rw-r--r-- | chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.h | 70 | ||||
-rw-r--r-- | chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac_unittest.cc | 143 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests_unit.gypi | 4 | ||||
-rw-r--r-- | chrome/common/chrome_utility_messages.h | 31 | ||||
-rwxr-xr-x | chrome/test/data/safe_browsing/dmg/generate_test_data.sh | 18 | ||||
-rw-r--r-- | chrome/utility/chrome_content_utility_client.cc | 21 | ||||
-rw-r--r-- | chrome/utility/chrome_content_utility_client.h | 4 | ||||
-rw-r--r-- | chrome/utility/safe_browsing/mac/hfs.cc | 2 | ||||
-rw-r--r-- | tools/metrics/histograms/histograms.xml | 25 |
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> |