Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(478)

Side by Side Diff: chrome/browser/safe_browsing/download_protection_service.cc

Issue 10382113: Add SafeBrowsing support for checking downloaded zip files that contain executables. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix FilePath printf format on Windows Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/safe_browsing/download_protection_service.h" 5 #include "chrome/browser/safe_browsing/download_protection_service.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/compiler_specific.h" 8 #include "base/compiler_specific.h"
9 #include "base/format_macros.h" 9 #include "base/format_macros.h"
10 #include "base/memory/scoped_ptr.h" 10 #include "base/memory/scoped_ptr.h"
11 #include "base/memory/weak_ptr.h" 11 #include "base/memory/weak_ptr.h"
12 #include "base/message_loop_helpers.h" 12 #include "base/message_loop_helpers.h"
13 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram.h"
14 #include "base/stl_util.h" 14 #include "base/stl_util.h"
15 #include "base/string_number_conversions.h" 15 #include "base/string_number_conversions.h"
16 #include "base/string_util.h" 16 #include "base/string_util.h"
17 #include "base/stringprintf.h" 17 #include "base/stringprintf.h"
18 #include "base/threading/sequenced_worker_pool.h" 18 #include "base/threading/sequenced_worker_pool.h"
19 #include "base/time.h" 19 #include "base/time.h"
20 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 20 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
21 #include "chrome/browser/safe_browsing/signature_util.h" 21 #include "chrome/browser/safe_browsing/signature_util.h"
22 #include "chrome/browser/ui/browser_list.h" 22 #include "chrome/browser/ui/browser_list.h"
23 #include "chrome/common/safe_browsing/csd.pb.h" 23 #include "chrome/common/safe_browsing/csd.pb.h"
24 #include "chrome/common/url_constants.h" 24 #include "chrome/common/url_constants.h"
25 #include "chrome/common/zip_reader.h"
25 #include "content/public/browser/browser_thread.h" 26 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/download_item.h" 27 #include "content/public/browser/download_item.h"
27 #include "content/public/browser/page_navigator.h" 28 #include "content/public/browser/page_navigator.h"
28 #include "content/public/common/url_fetcher.h" 29 #include "content/public/common/url_fetcher.h"
29 #include "content/public/common/url_fetcher_delegate.h" 30 #include "content/public/common/url_fetcher_delegate.h"
30 #include "net/base/load_flags.h" 31 #include "net/base/load_flags.h"
31 #include "net/base/x509_cert_types.h" 32 #include "net/base/x509_cert_types.h"
32 #include "net/base/x509_certificate.h" 33 #include "net/base/x509_certificate.h"
33 #include "net/http/http_status_code.h" 34 #include "net/http/http_status_code.h"
34 #include "net/url_request/url_request_context_getter.h" 35 #include "net/url_request/url_request_context_getter.h"
35 #include "net/url_request/url_request_status.h" 36 #include "net/url_request/url_request_status.h"
36 37
37 using content::BrowserThread; 38 using content::BrowserThread;
38 39
39 namespace { 40 namespace {
40 static const int64 kDownloadRequestTimeoutMs = 3000; 41 static const int64 kDownloadRequestTimeoutMs = 3000;
41 } // namespace 42 } // namespace
42 43
43 namespace safe_browsing { 44 namespace safe_browsing {
44 45
45 const char DownloadProtectionService::kDownloadRequestUrl[] = 46 const char DownloadProtectionService::kDownloadRequestUrl[] =
46 "https://sb-ssl.google.com/safebrowsing/clientreport/download"; 47 "https://sb-ssl.google.com/safebrowsing/clientreport/download";
47 48
48 namespace { 49 namespace {
50 bool IsArchiveFile(const FilePath& file) {
51 return file.MatchesExtension(FILE_PATH_LITERAL(".zip"));
52 }
53
49 bool IsBinaryFile(const FilePath& file) { 54 bool IsBinaryFile(const FilePath& file) {
50 return ( 55 return (
51 // Executable extensions for MS Windows. 56 // Executable extensions for MS Windows.
52 file.MatchesExtension(FILE_PATH_LITERAL(".bas")) || 57 file.MatchesExtension(FILE_PATH_LITERAL(".bas")) ||
53 file.MatchesExtension(FILE_PATH_LITERAL(".bat")) || 58 file.MatchesExtension(FILE_PATH_LITERAL(".bat")) ||
54 file.MatchesExtension(FILE_PATH_LITERAL(".cab")) || 59 file.MatchesExtension(FILE_PATH_LITERAL(".cab")) ||
55 file.MatchesExtension(FILE_PATH_LITERAL(".cmd")) || 60 file.MatchesExtension(FILE_PATH_LITERAL(".cmd")) ||
56 file.MatchesExtension(FILE_PATH_LITERAL(".com")) || 61 file.MatchesExtension(FILE_PATH_LITERAL(".com")) ||
57 file.MatchesExtension(FILE_PATH_LITERAL(".exe")) || 62 file.MatchesExtension(FILE_PATH_LITERAL(".exe")) ||
58 file.MatchesExtension(FILE_PATH_LITERAL(".hta")) || 63 file.MatchesExtension(FILE_PATH_LITERAL(".hta")) ||
59 file.MatchesExtension(FILE_PATH_LITERAL(".msi")) || 64 file.MatchesExtension(FILE_PATH_LITERAL(".msi")) ||
60 file.MatchesExtension(FILE_PATH_LITERAL(".pif")) || 65 file.MatchesExtension(FILE_PATH_LITERAL(".pif")) ||
61 file.MatchesExtension(FILE_PATH_LITERAL(".reg")) || 66 file.MatchesExtension(FILE_PATH_LITERAL(".reg")) ||
62 file.MatchesExtension(FILE_PATH_LITERAL(".scr")) || 67 file.MatchesExtension(FILE_PATH_LITERAL(".scr")) ||
63 file.MatchesExtension(FILE_PATH_LITERAL(".vb")) || 68 file.MatchesExtension(FILE_PATH_LITERAL(".vb")) ||
64 file.MatchesExtension(FILE_PATH_LITERAL(".vbs")) || 69 file.MatchesExtension(FILE_PATH_LITERAL(".vbs")) ||
65 // Chrome extensions and android APKs are also reported. 70 // Chrome extensions and android APKs are also reported.
66 file.MatchesExtension(FILE_PATH_LITERAL(".crx")) || 71 file.MatchesExtension(FILE_PATH_LITERAL(".crx")) ||
67 file.MatchesExtension(FILE_PATH_LITERAL(".apk"))); 72 file.MatchesExtension(FILE_PATH_LITERAL(".apk")) ||
73 // Archives _may_ contain binaries, we'll check in ExtractFileFeatures.
74 IsArchiveFile(file));
68 } 75 }
69 76
70 ClientDownloadRequest::DownloadType GetDownloadType(const FilePath& file) { 77 ClientDownloadRequest::DownloadType GetDownloadType(const FilePath& file) {
71 DCHECK(IsBinaryFile(file)); 78 DCHECK(IsBinaryFile(file));
72 if (file.MatchesExtension(FILE_PATH_LITERAL(".apk"))) 79 if (file.MatchesExtension(FILE_PATH_LITERAL(".apk")))
73 return ClientDownloadRequest::ANDROID_APK; 80 return ClientDownloadRequest::ANDROID_APK;
74 else if (file.MatchesExtension(FILE_PATH_LITERAL(".crx"))) 81 else if (file.MatchesExtension(FILE_PATH_LITERAL(".crx")))
75 return ClientDownloadRequest::CHROME_EXTENSION; 82 return ClientDownloadRequest::CHROME_EXTENSION;
83 // For zip files, we use the ZIPPED_EXECUTABLE type since we will only send
84 // the pingback if we find an executable inside the zip archive.
85 else if (file.MatchesExtension(FILE_PATH_LITERAL(".zip")))
86 return ClientDownloadRequest::ZIPPED_EXECUTABLE;
76 return ClientDownloadRequest::WIN_EXECUTABLE; 87 return ClientDownloadRequest::WIN_EXECUTABLE;
77 } 88 }
78 89
79 // List of extensions for which we track some UMA stats. 90 // List of extensions for which we track some UMA stats.
80 enum MaliciousExtensionType { 91 enum MaliciousExtensionType {
81 EXTENSION_EXE, 92 EXTENSION_EXE,
82 EXTENSION_MSI, 93 EXTENSION_MSI,
83 EXTENSION_CAB, 94 EXTENSION_CAB,
84 EXTENSION_SYS, 95 EXTENSION_SYS,
85 EXTENSION_SCR, 96 EXTENSION_SCR,
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
143 DOWNLOAD_HASH_CHECKS_TOTAL, 154 DOWNLOAD_HASH_CHECKS_TOTAL,
144 DOWNLOAD_HASH_CHECKS_MALWARE, 155 DOWNLOAD_HASH_CHECKS_MALWARE,
145 156
146 // Memory space for histograms is determined by the max. 157 // Memory space for histograms is determined by the max.
147 // ALWAYS ADD NEW VALUES BEFORE THIS ONE. 158 // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
148 DOWNLOAD_CHECKS_MAX 159 DOWNLOAD_CHECKS_MAX
149 }; 160 };
150 } // namespace 161 } // namespace
151 162
152 DownloadProtectionService::DownloadInfo::DownloadInfo() 163 DownloadProtectionService::DownloadInfo::DownloadInfo()
153 : total_bytes(0), user_initiated(false) {} 164 : total_bytes(0), user_initiated(false), zipped_executable(false) {}
154 165
155 DownloadProtectionService::DownloadInfo::~DownloadInfo() {} 166 DownloadProtectionService::DownloadInfo::~DownloadInfo() {}
156 167
157 std::string DownloadProtectionService::DownloadInfo::DebugString() const { 168 std::string DownloadProtectionService::DownloadInfo::DebugString() const {
158 std::string chain; 169 std::string chain;
159 for (size_t i = 0; i < download_url_chain.size(); ++i) { 170 for (size_t i = 0; i < download_url_chain.size(); ++i) {
160 chain += download_url_chain[i].spec(); 171 chain += download_url_chain[i].spec();
161 if (i < download_url_chain.size() - 1) { 172 if (i < download_url_chain.size() - 1) {
162 chain += " -> "; 173 chain += " -> ";
163 } 174 }
164 } 175 }
165 return base::StringPrintf( 176 return base::StringPrintf(
166 "DownloadInfo {addr:0x%p, download_url_chain:[%s], local_file:%s, " 177 "DownloadInfo {addr:0x%p, download_url_chain:[%s], local_file:%"
167 "target_file:%s, referrer_url:%s, sha256_hash:%s, total_bytes:%" PRId64 178 PRFilePath ", target_file:%" PRFilePath ", referrer_url:%s, "
168 ", user_initiated: %s}", 179 "sha256_hash:%s, total_bytes:%" PRId64 ", user_initiated: %s, "
180 "zipped_executable: %s}",
169 reinterpret_cast<const void*>(this), 181 reinterpret_cast<const void*>(this),
170 chain.c_str(), 182 chain.c_str(),
171 local_file.value().c_str(), 183 local_file.value().c_str(),
172 target_file.value().c_str(), 184 target_file.value().c_str(),
173 referrer_url.spec().c_str(), 185 referrer_url.spec().c_str(),
174 base::HexEncode(sha256_hash.data(), sha256_hash.size()).c_str(), 186 base::HexEncode(sha256_hash.data(), sha256_hash.size()).c_str(),
175 total_bytes, 187 total_bytes,
176 user_initiated ? "true" : "false"); 188 user_initiated ? "true" : "false",
189 zipped_executable ? "true" : "false");
177 } 190 }
178 191
179 // static 192 // static
180 DownloadProtectionService::DownloadInfo 193 DownloadProtectionService::DownloadInfo
181 DownloadProtectionService::DownloadInfo::FromDownloadItem( 194 DownloadProtectionService::DownloadInfo::FromDownloadItem(
182 const content::DownloadItem& item) { 195 const content::DownloadItem& item) {
183 DownloadInfo download_info; 196 DownloadInfo download_info;
184 download_info.target_file = item.GetTargetFilePath(); 197 download_info.target_file = item.GetTargetFilePath();
185 download_info.sha256_hash = item.GetHash(); 198 download_info.sha256_hash = item.GetHash();
186 download_info.local_file = item.GetFullPath(); 199 download_info.local_file = item.GetFullPath();
(...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after
481 494
482 private: 495 private:
483 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; 496 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
484 friend class base::DeleteHelper<CheckClientDownloadRequest>; 497 friend class base::DeleteHelper<CheckClientDownloadRequest>;
485 498
486 virtual ~CheckClientDownloadRequest() { 499 virtual ~CheckClientDownloadRequest() {
487 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 500 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
488 } 501 }
489 502
490 void ExtractFileFeatures() { 503 void ExtractFileFeatures() {
504 // If we're checking an archive file, look to see if there are any
505 // executables inside. If not, we will skip the pingback for this
506 // download.
507 if (info_.target_file.MatchesExtension(FILE_PATH_LITERAL(".zip"))) {
508 ExtractZipFeatures();
509 if (!info_.zipped_executable) {
510 RecordImprovedProtectionStats(REASON_ARCHIVE_WITHOUT_BINARIES);
511 PostFinishTask(SAFE);
512 return;
513 }
514 } else {
515 DCHECK(!IsArchiveFile(info_.target_file));
516 ExtractSignatureFeatures();
517 }
518
519 // TODO(noelutz): DownloadInfo should also contain the IP address of
520 // every URL in the redirect chain. We also should check whether the
521 // download URL is hosted on the internal network.
522 BrowserThread::PostTask(
523 BrowserThread::IO,
524 FROM_HERE,
525 base::Bind(&CheckClientDownloadRequest::CheckWhitelists, this));
526 }
527
528 void ExtractSignatureFeatures() {
529 base::TimeTicks start_time = base::TimeTicks::Now();
491 signature_util_->CheckSignature(info_.local_file, &signature_info_); 530 signature_util_->CheckSignature(info_.local_file, &signature_info_);
492 bool is_signed = (signature_info_.certificate_chain_size() > 0); 531 bool is_signed = (signature_info_.certificate_chain_size() > 0);
493 if (is_signed) { 532 if (is_signed) {
494 VLOG(2) << "Downloaded a signed binary: " << info_.local_file.value(); 533 VLOG(2) << "Downloaded a signed binary: " << info_.local_file.value();
495 } else { 534 } else {
496 VLOG(2) << "Downloaded an unsigned binary: " << info_.local_file.value(); 535 VLOG(2) << "Downloaded an unsigned binary: " << info_.local_file.value();
497 } 536 }
498 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed); 537 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed);
538 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime",
539 base::TimeTicks::Now() - start_time);
540 }
499 541
500 // TODO(noelutz): DownloadInfo should also contain the IP address of every 542 void ExtractZipFeatures() {
501 // URL in the redirect chain. We also should check whether the download 543 base::TimeTicks start_time = base::TimeTicks::Now();
502 // URL is hosted on the internal network. 544 zip::ZipReader reader;
503 BrowserThread::PostTask( 545 bool zip_file_has_archive = false;
504 BrowserThread::IO, 546 if (reader.Open(info_.local_file)) {
505 FROM_HERE, 547 for (; reader.HasMore(); reader.AdvanceToNextEntry()) {
506 base::Bind(&CheckClientDownloadRequest::CheckWhitelists, this)); 548 if (!reader.OpenCurrentEntryInZip()) {
549 VLOG(1) << "Failed to open current entry in zip file: "
550 << info_.local_file.value();
551 continue;
552 }
553 const FilePath& file = reader.current_entry_info()->file_path();
554 if (IsBinaryFile(file)) {
555 // Don't consider an archived archive to be executable, but record
556 // a histogram.
557 if (IsArchiveFile(file)) {
558 zip_file_has_archive = true;
559 } else {
560 VLOG(2) << "Downloaded a zipped executable: "
561 << info_.local_file.value();
562 info_.zipped_executable = true;
563 break;
564 }
565 } else {
566 VLOG(3) << "Ignoring non-binary file: " << file.value();
567 }
568 }
569 } else {
570 VLOG(1) << "Failed to open zip file: " << info_.local_file.value();
571 }
572 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
573 info_.zipped_executable);
574 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable",
575 zip_file_has_archive && !info_.zipped_executable);
576 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime",
577 base::TimeTicks::Now() - start_time);
507 } 578 }
508 579
509 void CheckWhitelists() { 580 void CheckWhitelists() {
510 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 581 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
511 DownloadCheckResultReason reason = REASON_MAX; 582 DownloadCheckResultReason reason = REASON_MAX;
512 if (!sb_service_.get()) { 583 if (!sb_service_.get()) {
513 reason = REASON_SB_DISABLED; 584 reason = REASON_SB_DISABLED;
514 } else { 585 } else {
515 for (size_t i = 0; i < info_.download_url_chain.size(); ++i) { 586 for (size_t i = 0; i < info_.download_url_chain.size(); ++i) {
516 const GURL& url = info_.download_url_chain[i]; 587 const GURL& url = info_.download_url_chain[i];
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after
756 // Currently, the UI only works on Windows. On Linux and Mac we still 827 // Currently, the UI only works on Windows. On Linux and Mac we still
757 // want to show the dangerous file type warning if the file is possibly 828 // want to show the dangerous file type warning if the file is possibly
758 // dangerous which means we have to always return false here. 829 // dangerous which means we have to always return false here.
759 #if defined(OS_WIN) 830 #if defined(OS_WIN)
760 DownloadCheckResultReason reason = REASON_MAX; 831 DownloadCheckResultReason reason = REASON_MAX;
761 ClientDownloadRequest::DownloadType type = 832 ClientDownloadRequest::DownloadType type =
762 ClientDownloadRequest::WIN_EXECUTABLE; 833 ClientDownloadRequest::WIN_EXECUTABLE;
763 return (CheckClientDownloadRequest::IsSupportedDownload(info, 834 return (CheckClientDownloadRequest::IsSupportedDownload(info,
764 &reason, 835 &reason,
765 &type) && 836 &type) &&
766 ClientDownloadRequest::WIN_EXECUTABLE == type); 837 (ClientDownloadRequest::WIN_EXECUTABLE == type ||
838 ClientDownloadRequest::ZIPPED_EXECUTABLE == type));
767 #else 839 #else
768 return false; 840 return false;
769 #endif 841 #endif
770 } 842 }
771 843
772 void DownloadProtectionService::CancelPendingRequests() { 844 void DownloadProtectionService::CancelPendingRequests() {
773 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 845 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
774 for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it = 846 for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
775 download_requests_.begin(); 847 download_requests_.begin();
776 it != download_requests_.end();) { 848 it != download_requests_.end();) {
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
870 942
871 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data, 943 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data,
872 sizeof(issuer.fingerprint().data)); 944 sizeof(issuer.fingerprint().data));
873 for (std::set<std::string>::iterator it = paths_to_check.begin(); 945 for (std::set<std::string>::iterator it = paths_to_check.begin();
874 it != paths_to_check.end(); ++it) { 946 it != paths_to_check.end(); ++it) {
875 whitelist_strings->push_back("cert/" + issuer_fp + *it); 947 whitelist_strings->push_back("cert/" + issuer_fp + *it);
876 } 948 }
877 } 949 }
878 950
879 } // namespace safe_browsing 951 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698