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

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: Minor cleanup 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 (file.MatchesExtension(FILE_PATH_LITERAL(".exe")) || 55 return (file.MatchesExtension(FILE_PATH_LITERAL(".exe")) ||
51 file.MatchesExtension(FILE_PATH_LITERAL(".cab")) || 56 file.MatchesExtension(FILE_PATH_LITERAL(".cab")) ||
52 file.MatchesExtension(FILE_PATH_LITERAL(".msi")) || 57 file.MatchesExtension(FILE_PATH_LITERAL(".msi")) ||
53 file.MatchesExtension(FILE_PATH_LITERAL(".crx")) || 58 file.MatchesExtension(FILE_PATH_LITERAL(".crx")) ||
54 file.MatchesExtension(FILE_PATH_LITERAL(".apk"))); 59 file.MatchesExtension(FILE_PATH_LITERAL(".apk")) ||
60 // Archives _may_ contain binaries, we'll check in
61 // ExtractFileFeatures.
62 IsArchiveFile(file));
55 } 63 }
56 64
57 ClientDownloadRequest::DownloadType GetDownloadType(const FilePath& file) { 65 ClientDownloadRequest::DownloadType GetDownloadType(const FilePath& file) {
58 DCHECK(IsBinaryFile(file)); 66 DCHECK(IsBinaryFile(file));
59 if (file.MatchesExtension(FILE_PATH_LITERAL(".apk"))) 67 if (file.MatchesExtension(FILE_PATH_LITERAL(".apk")))
60 return ClientDownloadRequest::ANDROID_APK; 68 return ClientDownloadRequest::ANDROID_APK;
61 else if (file.MatchesExtension(FILE_PATH_LITERAL(".crx"))) 69 else if (file.MatchesExtension(FILE_PATH_LITERAL(".crx")))
62 return ClientDownloadRequest::CHROME_EXTENSION; 70 return ClientDownloadRequest::CHROME_EXTENSION;
71 else if (file.MatchesExtension(FILE_PATH_LITERAL(".zip")))
72 return ClientDownloadRequest::ZIPPED_WIN_EXECUTABLE;
mattm 2012/05/11 22:42:06 Seems a little weird to say this since we haven't
Brian Ryner 2012/05/12 00:06:59 Yep, that's right. Added a comment to clarify.
63 return ClientDownloadRequest::WIN_EXECUTABLE; 73 return ClientDownloadRequest::WIN_EXECUTABLE;
64 } 74 }
65 75
66 // List of extensions for which we track some UMA stats. 76 // List of extensions for which we track some UMA stats.
67 enum MaliciousExtensionType { 77 enum MaliciousExtensionType {
68 EXTENSION_EXE, 78 EXTENSION_EXE,
69 EXTENSION_MSI, 79 EXTENSION_MSI,
70 EXTENSION_CAB, 80 EXTENSION_CAB,
71 EXTENSION_SYS, 81 EXTENSION_SYS,
72 EXTENSION_SCR, 82 EXTENSION_SCR,
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 DOWNLOAD_HASH_CHECKS_TOTAL, 140 DOWNLOAD_HASH_CHECKS_TOTAL,
131 DOWNLOAD_HASH_CHECKS_MALWARE, 141 DOWNLOAD_HASH_CHECKS_MALWARE,
132 142
133 // Memory space for histograms is determined by the max. 143 // Memory space for histograms is determined by the max.
134 // ALWAYS ADD NEW VALUES BEFORE THIS ONE. 144 // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
135 DOWNLOAD_CHECKS_MAX 145 DOWNLOAD_CHECKS_MAX
136 }; 146 };
137 } // namespace 147 } // namespace
138 148
139 DownloadProtectionService::DownloadInfo::DownloadInfo() 149 DownloadProtectionService::DownloadInfo::DownloadInfo()
140 : total_bytes(0), user_initiated(false) {} 150 : total_bytes(0), user_initiated(false), zipped_executable(false) {}
141 151
142 DownloadProtectionService::DownloadInfo::~DownloadInfo() {} 152 DownloadProtectionService::DownloadInfo::~DownloadInfo() {}
143 153
144 std::string DownloadProtectionService::DownloadInfo::DebugString() const { 154 std::string DownloadProtectionService::DownloadInfo::DebugString() const {
145 std::string chain; 155 std::string chain;
146 for (size_t i = 0; i < download_url_chain.size(); ++i) { 156 for (size_t i = 0; i < download_url_chain.size(); ++i) {
147 chain += download_url_chain[i].spec(); 157 chain += download_url_chain[i].spec();
148 if (i < download_url_chain.size() - 1) { 158 if (i < download_url_chain.size() - 1) {
149 chain += " -> "; 159 chain += " -> ";
150 } 160 }
(...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after
468 478
469 private: 479 private:
470 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; 480 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
471 friend class base::DeleteHelper<CheckClientDownloadRequest>; 481 friend class base::DeleteHelper<CheckClientDownloadRequest>;
472 482
473 virtual ~CheckClientDownloadRequest() { 483 virtual ~CheckClientDownloadRequest() {
474 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
475 } 485 }
476 486
477 void ExtractFileFeatures() { 487 void ExtractFileFeatures() {
488 // If we're checking an archive file, look to see if there are any
489 // executables inside. If not, we will skip the pingback for this
490 // download.
491 if (info_.target_file.MatchesExtension(FILE_PATH_LITERAL(".zip"))) {
492 ExtractZipFeatures();
493 if (!info_.zipped_executable) {
494 RecordImprovedProtectionStats(REASON_NOT_BINARY_FILE);
noelutz 2012/05/11 17:54:33 might be nice to have a separate reason for that.
Brian Ryner 2012/05/11 22:25:44 Done.
495 PostFinishTask(SAFE);
496 return;
497 }
498 } else {
499 DCHECK(!IsArchiveFile(info_.target_file));
500 ExtractSignatureFeatures();
501 }
502
503 // TODO(noelutz): DownloadInfo should also contain the IP address of
504 // every URL in the redirect chain. We also should check whether the
505 // download URL is hosted on the internal network.
506 BrowserThread::PostTask(
507 BrowserThread::IO,
508 FROM_HERE,
509 base::Bind(&CheckClientDownloadRequest::CheckWhitelists, this));
510 }
511
512 void ExtractSignatureFeatures() {
478 signature_util_->CheckSignature(info_.local_file, &signature_info_); 513 signature_util_->CheckSignature(info_.local_file, &signature_info_);
479 bool is_signed = (signature_info_.certificate_chain_size() > 0); 514 bool is_signed = (signature_info_.certificate_chain_size() > 0);
480 if (is_signed) { 515 if (is_signed) {
481 VLOG(2) << "Downloaded a signed binary: " << info_.local_file.value(); 516 VLOG(2) << "Downloaded a signed binary: " << info_.local_file.value();
482 } else { 517 } else {
483 VLOG(2) << "Downloaded an unsigned binary: " << info_.local_file.value(); 518 VLOG(2) << "Downloaded an unsigned binary: " << info_.local_file.value();
484 } 519 }
485 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed); 520 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed);
521 }
486 522
487 // TODO(noelutz): DownloadInfo should also contain the IP address of every 523 void ExtractZipFeatures() {
noelutz 2012/05/11 17:54:33 Would you mind adding a histogram for both this me
Brian Ryner 2012/05/11 22:25:44 Done.
488 // URL in the redirect chain. We also should check whether the download 524 zip::ZipReader reader;
489 // URL is hosted on the internal network. 525 if (reader.Open(info_.local_file)) {
490 BrowserThread::PostTask( 526 for (; reader.HasMore(); reader.AdvanceToNextEntry()) {
491 BrowserThread::IO, 527 if (!reader.OpenCurrentEntryInZip()) {
492 FROM_HERE, 528 VLOG(1) << "Failed to open current entry in zip file: "
493 base::Bind(&CheckClientDownloadRequest::CheckWhitelists, this)); 529 << info_.local_file.value();
530 continue;
531 }
532 const FilePath& file = reader.current_entry_info()->file_path();
533 // Don't consider an archived archive to be executable.
534 if (IsBinaryFile(file) && !IsArchiveFile(file)) {
535 VLOG(2) << "Downloaded a zipped executable: "
536 << info_.local_file.value();
537 info_.zipped_executable = true;
538 break;
539 } else {
540 VLOG(3) << "Ignoring non-binary file: " << file.value();
541 }
542 }
543 } else {
544 VLOG(1) << "Failed to open zip file: " << info_.local_file.value();
545 }
546 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
547 info_.zipped_executable);
494 } 548 }
495 549
496 void CheckWhitelists() { 550 void CheckWhitelists() {
497 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 551 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
498 DownloadCheckResultReason reason = REASON_MAX; 552 DownloadCheckResultReason reason = REASON_MAX;
499 if (!sb_service_.get()) { 553 if (!sb_service_.get()) {
500 reason = REASON_SB_DISABLED; 554 reason = REASON_SB_DISABLED;
501 } else { 555 } else {
502 for (size_t i = 0; i < info_.download_url_chain.size(); ++i) { 556 for (size_t i = 0; i < info_.download_url_chain.size(); ++i) {
503 const GURL& url = info_.download_url_chain[i]; 557 const GURL& url = info_.download_url_chain[i];
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after
743 // Currently, the UI only works on Windows. On Linux and Mac we still 797 // Currently, the UI only works on Windows. On Linux and Mac we still
744 // want to show the dangerous file type warning if the file is possibly 798 // want to show the dangerous file type warning if the file is possibly
745 // dangerous which means we have to always return false here. 799 // dangerous which means we have to always return false here.
746 #if defined(OS_WIN) 800 #if defined(OS_WIN)
747 DownloadCheckResultReason reason = REASON_MAX; 801 DownloadCheckResultReason reason = REASON_MAX;
748 ClientDownloadRequest::DownloadType type = 802 ClientDownloadRequest::DownloadType type =
749 ClientDownloadRequest::WIN_EXECUTABLE; 803 ClientDownloadRequest::WIN_EXECUTABLE;
750 return (CheckClientDownloadRequest::IsSupportedDownload(info, 804 return (CheckClientDownloadRequest::IsSupportedDownload(info,
751 &reason, 805 &reason,
752 &type) && 806 &type) &&
753 ClientDownloadRequest::WIN_EXECUTABLE == type); 807 ClientDownloadRequest::WIN_EXECUTABLE == type ||
808 ClientDownloadRequest::ZIPPED_WIN_EXECUTABLE == type);
754 #else 809 #else
755 return false; 810 return false;
756 #endif 811 #endif
757 } 812 }
758 813
759 void DownloadProtectionService::CancelPendingRequests() { 814 void DownloadProtectionService::CancelPendingRequests() {
760 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 815 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
761 for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it = 816 for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
762 download_requests_.begin(); 817 download_requests_.begin();
763 it != download_requests_.end();) { 818 it != download_requests_.end();) {
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
857 912
858 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data, 913 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data,
859 sizeof(issuer.fingerprint().data)); 914 sizeof(issuer.fingerprint().data));
860 for (std::set<std::string>::iterator it = paths_to_check.begin(); 915 for (std::set<std::string>::iterator it = paths_to_check.begin();
861 it != paths_to_check.end(); ++it) { 916 it != paths_to_check.end(); ++it) {
862 whitelist_strings->push_back("cert/" + issuer_fp + *it); 917 whitelist_strings->push_back("cert/" + issuer_fp + *it);
863 } 918 }
864 } 919 }
865 920
866 } // namespace safe_browsing 921 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698