Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / safe_browsing / download_protection_service.cc
blobb00646a2c1fbf45d133b1c4bad56e802b5fed15e
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/download_protection_service.h"
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "base/format_macros.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/metrics/histogram.h"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/sequenced_task_runner_helpers.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/task/cancelable_task_tracker.h"
21 #include "base/threading/sequenced_worker_pool.h"
22 #include "base/time/time.h"
23 #include "chrome/browser/browser_process.h"
24 #include "chrome/browser/history/history_service.h"
25 #include "chrome/browser/history/history_service_factory.h"
26 #include "chrome/browser/safe_browsing/binary_feature_extractor.h"
27 #include "chrome/browser/safe_browsing/download_feedback_service.h"
28 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
29 #include "chrome/browser/safe_browsing/sandboxed_zip_analyzer.h"
30 #include "chrome/browser/ui/browser.h"
31 #include "chrome/browser/ui/browser_list.h"
32 #include "chrome/common/safe_browsing/csd.pb.h"
33 #include "chrome/common/safe_browsing/download_protection_util.h"
34 #include "chrome/common/safe_browsing/zip_analyzer.h"
35 #include "chrome/common/url_constants.h"
36 #include "components/google/core/browser/google_util.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/browser/download_item.h"
39 #include "content/public/browser/page_navigator.h"
40 #include "google_apis/google_api_keys.h"
41 #include "net/base/escape.h"
42 #include "net/base/load_flags.h"
43 #include "net/cert/x509_cert_types.h"
44 #include "net/cert/x509_certificate.h"
45 #include "net/http/http_status_code.h"
46 #include "net/url_request/url_fetcher.h"
47 #include "net/url_request/url_fetcher_delegate.h"
48 #include "net/url_request/url_request_context_getter.h"
49 #include "net/url_request/url_request_status.h"
51 using content::BrowserThread;
53 namespace {
54 static const int64 kDownloadRequestTimeoutMs = 7000;
55 } // namespace
57 namespace safe_browsing {
59 const char DownloadProtectionService::kDownloadRequestUrl[] =
60 "https://sb-ssl.google.com/safebrowsing/clientreport/download";
62 namespace {
63 // List of extensions for which we track some UMA stats.
64 enum MaliciousExtensionType {
65 EXTENSION_EXE,
66 EXTENSION_MSI,
67 EXTENSION_CAB,
68 EXTENSION_SYS,
69 EXTENSION_SCR,
70 EXTENSION_DRV,
71 EXTENSION_BAT,
72 EXTENSION_ZIP,
73 EXTENSION_RAR,
74 EXTENSION_DLL,
75 EXTENSION_PIF,
76 EXTENSION_COM,
77 EXTENSION_JAR,
78 EXTENSION_CLASS,
79 EXTENSION_PDF,
80 EXTENSION_VB,
81 EXTENSION_REG,
82 EXTENSION_GRP,
83 EXTENSION_OTHER, // Groups all other extensions into one bucket.
84 EXTENSION_CRX,
85 EXTENSION_APK,
86 EXTENSION_DMG,
87 EXTENSION_PKG,
88 EXTENSION_TORRENT,
89 EXTENSION_MAX,
92 MaliciousExtensionType GetExtensionType(const base::FilePath& f) {
93 if (f.MatchesExtension(FILE_PATH_LITERAL(".exe"))) return EXTENSION_EXE;
94 if (f.MatchesExtension(FILE_PATH_LITERAL(".msi"))) return EXTENSION_MSI;
95 if (f.MatchesExtension(FILE_PATH_LITERAL(".cab"))) return EXTENSION_CAB;
96 if (f.MatchesExtension(FILE_PATH_LITERAL(".sys"))) return EXTENSION_SYS;
97 if (f.MatchesExtension(FILE_PATH_LITERAL(".scr"))) return EXTENSION_SCR;
98 if (f.MatchesExtension(FILE_PATH_LITERAL(".drv"))) return EXTENSION_DRV;
99 if (f.MatchesExtension(FILE_PATH_LITERAL(".bat"))) return EXTENSION_BAT;
100 if (f.MatchesExtension(FILE_PATH_LITERAL(".zip"))) return EXTENSION_ZIP;
101 if (f.MatchesExtension(FILE_PATH_LITERAL(".rar"))) return EXTENSION_RAR;
102 if (f.MatchesExtension(FILE_PATH_LITERAL(".dll"))) return EXTENSION_DLL;
103 if (f.MatchesExtension(FILE_PATH_LITERAL(".pif"))) return EXTENSION_PIF;
104 if (f.MatchesExtension(FILE_PATH_LITERAL(".com"))) return EXTENSION_COM;
105 if (f.MatchesExtension(FILE_PATH_LITERAL(".jar"))) return EXTENSION_JAR;
106 if (f.MatchesExtension(FILE_PATH_LITERAL(".class"))) return EXTENSION_CLASS;
107 if (f.MatchesExtension(FILE_PATH_LITERAL(".pdf"))) return EXTENSION_PDF;
108 if (f.MatchesExtension(FILE_PATH_LITERAL(".vb"))) return EXTENSION_VB;
109 if (f.MatchesExtension(FILE_PATH_LITERAL(".reg"))) return EXTENSION_REG;
110 if (f.MatchesExtension(FILE_PATH_LITERAL(".grp"))) return EXTENSION_GRP;
111 if (f.MatchesExtension(FILE_PATH_LITERAL(".crx"))) return EXTENSION_CRX;
112 if (f.MatchesExtension(FILE_PATH_LITERAL(".apk"))) return EXTENSION_APK;
113 if (f.MatchesExtension(FILE_PATH_LITERAL(".dmg"))) return EXTENSION_DMG;
114 if (f.MatchesExtension(FILE_PATH_LITERAL(".pkg"))) return EXTENSION_PKG;
115 if (f.MatchesExtension(FILE_PATH_LITERAL(".torrent")))
116 return EXTENSION_TORRENT;
117 return EXTENSION_OTHER;
120 void RecordFileExtensionType(const base::FilePath& file) {
121 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadExtensions",
122 GetExtensionType(file),
123 EXTENSION_MAX);
126 // Enumerate for histogramming purposes.
127 // DO NOT CHANGE THE ORDERING OF THESE VALUES (different histogram data will
128 // be mixed together based on their values).
129 enum SBStatsType {
130 DOWNLOAD_URL_CHECKS_TOTAL,
131 DOWNLOAD_URL_CHECKS_CANCELED,
132 DOWNLOAD_URL_CHECKS_MALWARE,
134 DOWNLOAD_HASH_CHECKS_TOTAL,
135 DOWNLOAD_HASH_CHECKS_MALWARE,
137 // Memory space for histograms is determined by the max.
138 // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
139 DOWNLOAD_CHECKS_MAX
141 } // namespace
143 // Parent SafeBrowsing::Client class used to lookup the bad binary
144 // URL and digest list. There are two sub-classes (one for each list).
145 class DownloadSBClient
146 : public SafeBrowsingDatabaseManager::Client,
147 public base::RefCountedThreadSafe<DownloadSBClient> {
148 public:
149 DownloadSBClient(
150 const content::DownloadItem& item,
151 const DownloadProtectionService::CheckDownloadCallback& callback,
152 const scoped_refptr<SafeBrowsingUIManager>& ui_manager,
153 SBStatsType total_type,
154 SBStatsType dangerous_type)
155 : sha256_hash_(item.GetHash()),
156 url_chain_(item.GetUrlChain()),
157 referrer_url_(item.GetReferrerUrl()),
158 callback_(callback),
159 ui_manager_(ui_manager),
160 start_time_(base::TimeTicks::Now()),
161 total_type_(total_type),
162 dangerous_type_(dangerous_type) {}
164 virtual void StartCheck() = 0;
165 virtual bool IsDangerous(SBThreatType threat_type) const = 0;
167 protected:
168 friend class base::RefCountedThreadSafe<DownloadSBClient>;
169 virtual ~DownloadSBClient() {}
171 void CheckDone(SBThreatType threat_type) {
172 DownloadProtectionService::DownloadCheckResult result =
173 IsDangerous(threat_type) ?
174 DownloadProtectionService::DANGEROUS :
175 DownloadProtectionService::SAFE;
176 BrowserThread::PostTask(BrowserThread::UI,
177 FROM_HERE,
178 base::Bind(callback_, result));
179 UpdateDownloadCheckStats(total_type_);
180 if (threat_type != SB_THREAT_TYPE_SAFE) {
181 UpdateDownloadCheckStats(dangerous_type_);
182 BrowserThread::PostTask(
183 BrowserThread::UI,
184 FROM_HERE,
185 base::Bind(&DownloadSBClient::ReportMalware,
186 this, threat_type));
190 void ReportMalware(SBThreatType threat_type) {
191 std::string post_data;
192 if (!sha256_hash_.empty())
193 post_data += base::HexEncode(sha256_hash_.data(),
194 sha256_hash_.size()) + "\n";
195 for (size_t i = 0; i < url_chain_.size(); ++i) {
196 post_data += url_chain_[i].spec() + "\n";
198 ui_manager_->ReportSafeBrowsingHit(
199 url_chain_.back(), // malicious_url
200 url_chain_.front(), // page_url
201 referrer_url_,
202 true, // is_subresource
203 threat_type,
204 post_data);
207 void UpdateDownloadCheckStats(SBStatsType stat_type) {
208 UMA_HISTOGRAM_ENUMERATION("SB2.DownloadChecks",
209 stat_type,
210 DOWNLOAD_CHECKS_MAX);
213 std::string sha256_hash_;
214 std::vector<GURL> url_chain_;
215 GURL referrer_url_;
216 DownloadProtectionService::CheckDownloadCallback callback_;
217 scoped_refptr<SafeBrowsingUIManager> ui_manager_;
218 base::TimeTicks start_time_;
220 private:
221 const SBStatsType total_type_;
222 const SBStatsType dangerous_type_;
224 DISALLOW_COPY_AND_ASSIGN(DownloadSBClient);
227 class DownloadUrlSBClient : public DownloadSBClient {
228 public:
229 DownloadUrlSBClient(
230 const content::DownloadItem& item,
231 const DownloadProtectionService::CheckDownloadCallback& callback,
232 const scoped_refptr<SafeBrowsingUIManager>& ui_manager,
233 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager)
234 : DownloadSBClient(item, callback, ui_manager,
235 DOWNLOAD_URL_CHECKS_TOTAL,
236 DOWNLOAD_URL_CHECKS_MALWARE),
237 database_manager_(database_manager) { }
239 virtual void StartCheck() override {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
241 if (!database_manager_.get() ||
242 database_manager_->CheckDownloadUrl(url_chain_, this)) {
243 CheckDone(SB_THREAT_TYPE_SAFE);
244 } else {
245 AddRef(); // SafeBrowsingService takes a pointer not a scoped_refptr.
249 virtual bool IsDangerous(SBThreatType threat_type) const override {
250 return threat_type == SB_THREAT_TYPE_BINARY_MALWARE_URL;
253 virtual void OnCheckDownloadUrlResult(const std::vector<GURL>& url_chain,
254 SBThreatType threat_type) override {
255 CheckDone(threat_type);
256 UMA_HISTOGRAM_TIMES("SB2.DownloadUrlCheckDuration",
257 base::TimeTicks::Now() - start_time_);
258 Release();
261 protected:
262 virtual ~DownloadUrlSBClient() {}
264 private:
265 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
267 DISALLOW_COPY_AND_ASSIGN(DownloadUrlSBClient);
270 class DownloadProtectionService::CheckClientDownloadRequest
271 : public base::RefCountedThreadSafe<
272 DownloadProtectionService::CheckClientDownloadRequest,
273 BrowserThread::DeleteOnUIThread>,
274 public net::URLFetcherDelegate,
275 public content::DownloadItem::Observer {
276 public:
277 CheckClientDownloadRequest(
278 content::DownloadItem* item,
279 const CheckDownloadCallback& callback,
280 DownloadProtectionService* service,
281 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
282 BinaryFeatureExtractor* binary_feature_extractor)
283 : item_(item),
284 url_chain_(item->GetUrlChain()),
285 referrer_url_(item->GetReferrerUrl()),
286 tab_url_(item->GetTabUrl()),
287 tab_referrer_url_(item->GetTabReferrerUrl()),
288 zipped_executable_(false),
289 callback_(callback),
290 service_(service),
291 binary_feature_extractor_(binary_feature_extractor),
292 database_manager_(database_manager),
293 pingback_enabled_(service_->enabled()),
294 finished_(false),
295 type_(ClientDownloadRequest::WIN_EXECUTABLE),
296 weakptr_factory_(this),
297 start_time_(base::TimeTicks::Now()) {
298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
299 item_->AddObserver(this);
302 void Start() {
303 VLOG(2) << "Starting SafeBrowsing download check for: "
304 << item_->DebugString(true);
305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
306 // TODO(noelutz): implement some cache to make sure we don't issue the same
307 // request over and over again if a user downloads the same binary multiple
308 // times.
309 DownloadCheckResultReason reason = REASON_MAX;
310 if (!IsSupportedDownload(
311 *item_, item_->GetTargetFilePath(), &reason, &type_)) {
312 switch (reason) {
313 case REASON_EMPTY_URL_CHAIN:
314 case REASON_INVALID_URL:
315 PostFinishTask(UNKNOWN, reason);
316 return;
318 case REASON_NOT_BINARY_FILE:
319 RecordFileExtensionType(item_->GetTargetFilePath());
320 PostFinishTask(UNKNOWN, reason);
321 return;
323 default:
324 // We only expect the reasons explicitly handled above.
325 NOTREACHED();
328 RecordFileExtensionType(item_->GetTargetFilePath());
330 // Compute features from the file contents. Note that we record histograms
331 // based on the result, so this runs regardless of whether the pingbacks
332 // are enabled.
333 if (item_->GetTargetFilePath().MatchesExtension(
334 FILE_PATH_LITERAL(".zip"))) {
335 StartExtractZipFeatures();
336 } else {
337 DCHECK(!download_protection_util::IsArchiveFile(
338 item_->GetTargetFilePath()));
339 StartExtractFileFeatures();
343 // Start a timeout to cancel the request if it takes too long.
344 // This should only be called after we have finished accessing the file.
345 void StartTimeout() {
346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
347 if (!service_) {
348 // Request has already been cancelled.
349 return;
351 timeout_start_time_ = base::TimeTicks::Now();
352 BrowserThread::PostDelayedTask(
353 BrowserThread::UI,
354 FROM_HERE,
355 base::Bind(&CheckClientDownloadRequest::Cancel,
356 weakptr_factory_.GetWeakPtr()),
357 base::TimeDelta::FromMilliseconds(
358 service_->download_request_timeout_ms()));
361 // Canceling a request will cause us to always report the result as UNKNOWN
362 // unless a pending request is about to call FinishRequest.
363 void Cancel() {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
365 if (fetcher_.get()) {
366 // The DownloadProtectionService is going to release its reference, so we
367 // might be destroyed before the URLFetcher completes. Cancel the
368 // fetcher so it does not try to invoke OnURLFetchComplete.
369 fetcher_.reset();
371 // Note: If there is no fetcher, then some callback is still holding a
372 // reference to this object. We'll eventually wind up in some method on
373 // the UI thread that will call FinishRequest() again. If FinishRequest()
374 // is called a second time, it will be a no-op.
375 FinishRequest(UNKNOWN, REASON_REQUEST_CANCELED);
376 // Calling FinishRequest might delete this object, we may be deleted by
377 // this point.
380 // content::DownloadItem::Observer implementation.
381 virtual void OnDownloadDestroyed(content::DownloadItem* download) override {
382 Cancel();
383 DCHECK(item_ == NULL);
386 // From the net::URLFetcherDelegate interface.
387 virtual void OnURLFetchComplete(const net::URLFetcher* source) override {
388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
389 DCHECK_EQ(source, fetcher_.get());
390 VLOG(2) << "Received a response for URL: "
391 << item_->GetUrlChain().back() << ": success="
392 << source->GetStatus().is_success() << " response_code="
393 << source->GetResponseCode();
394 if (source->GetStatus().is_success()) {
395 UMA_HISTOGRAM_SPARSE_SLOWLY(
396 "SBClientDownload.DownloadRequestResponseCode",
397 source->GetResponseCode());
399 UMA_HISTOGRAM_SPARSE_SLOWLY(
400 "SBClientDownload.DownloadRequestNetError",
401 -source->GetStatus().error());
402 DownloadCheckResultReason reason = REASON_SERVER_PING_FAILED;
403 DownloadCheckResult result = UNKNOWN;
404 if (source->GetStatus().is_success() &&
405 net::HTTP_OK == source->GetResponseCode()) {
406 ClientDownloadResponse response;
407 std::string data;
408 bool got_data = source->GetResponseAsString(&data);
409 DCHECK(got_data);
410 if (!response.ParseFromString(data)) {
411 reason = REASON_INVALID_RESPONSE_PROTO;
412 result = UNKNOWN;
413 } else if (response.verdict() == ClientDownloadResponse::SAFE) {
414 reason = REASON_DOWNLOAD_SAFE;
415 result = SAFE;
416 } else if (service_ && !service_->IsSupportedDownload(
417 *item_, item_->GetTargetFilePath())) {
418 // The client of the download protection service assumes that we don't
419 // support this download so we cannot return any other verdict than
420 // UNKNOWN even if the server says it's dangerous to download this file.
421 // Note: if service_ is NULL we already cancelled the request and
422 // returned UNKNOWN.
423 reason = REASON_DOWNLOAD_NOT_SUPPORTED;
424 result = UNKNOWN;
425 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS) {
426 reason = REASON_DOWNLOAD_DANGEROUS;
427 result = DANGEROUS;
428 } else if (response.verdict() == ClientDownloadResponse::UNCOMMON) {
429 reason = REASON_DOWNLOAD_UNCOMMON;
430 result = UNCOMMON;
431 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS_HOST) {
432 reason = REASON_DOWNLOAD_DANGEROUS_HOST;
433 result = DANGEROUS_HOST;
434 } else if (
435 response.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED) {
436 reason = REASON_DOWNLOAD_POTENTIALLY_UNWANTED;
437 result = POTENTIALLY_UNWANTED;
438 } else {
439 LOG(DFATAL) << "Unknown download response verdict: "
440 << response.verdict();
441 reason = REASON_INVALID_RESPONSE_VERDICT;
442 result = UNKNOWN;
444 DownloadFeedbackService::MaybeStorePingsForDownload(
445 result, item_, client_download_request_data_, data);
447 // We don't need the fetcher anymore.
448 fetcher_.reset();
449 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration",
450 base::TimeTicks::Now() - start_time_);
451 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestNetworkDuration",
452 base::TimeTicks::Now() - request_start_time_);
453 FinishRequest(result, reason);
456 static bool IsSupportedDownload(const content::DownloadItem& item,
457 const base::FilePath& target_path,
458 DownloadCheckResultReason* reason,
459 ClientDownloadRequest::DownloadType* type) {
460 if (item.GetUrlChain().empty()) {
461 *reason = REASON_EMPTY_URL_CHAIN;
462 return false;
464 const GURL& final_url = item.GetUrlChain().back();
465 if (!final_url.is_valid() || final_url.is_empty() ||
466 !final_url.IsStandard() || final_url.SchemeIsFile()) {
467 *reason = REASON_INVALID_URL;
468 return false;
470 if (!download_protection_util::IsBinaryFile(target_path)) {
471 *reason = REASON_NOT_BINARY_FILE;
472 return false;
474 *type = download_protection_util::GetDownloadType(target_path);
475 return true;
478 private:
479 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
480 friend class base::DeleteHelper<CheckClientDownloadRequest>;
482 virtual ~CheckClientDownloadRequest() {
483 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
484 DCHECK(item_ == NULL);
487 void OnFileFeatureExtractionDone() {
488 // This can run in any thread, since it just posts more messages.
490 // TODO(noelutz): DownloadInfo should also contain the IP address of
491 // every URL in the redirect chain. We also should check whether the
492 // download URL is hosted on the internal network.
493 BrowserThread::PostTask(
494 BrowserThread::IO,
495 FROM_HERE,
496 base::Bind(&CheckClientDownloadRequest::CheckWhitelists, this));
498 // We wait until after the file checks finish to start the timeout, as
499 // windows can cause permissions errors if the timeout fired while we were
500 // checking the file signature and we tried to complete the download.
501 BrowserThread::PostTask(
502 BrowserThread::UI,
503 FROM_HERE,
504 base::Bind(&CheckClientDownloadRequest::StartTimeout, this));
507 void StartExtractFileFeatures() {
508 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
509 DCHECK(item_); // Called directly from Start(), item should still exist.
510 // Since we do blocking I/O, offload this to a worker thread.
511 // The task does not need to block shutdown.
512 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
513 FROM_HERE,
514 base::Bind(&CheckClientDownloadRequest::ExtractFileFeatures,
515 this, item_->GetFullPath()),
516 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
519 void ExtractFileFeatures(const base::FilePath& file_path) {
520 base::TimeTicks start_time = base::TimeTicks::Now();
521 binary_feature_extractor_->CheckSignature(file_path, &signature_info_);
522 bool is_signed = (signature_info_.certificate_chain_size() > 0);
523 if (is_signed) {
524 VLOG(2) << "Downloaded a signed binary: " << file_path.value();
525 } else {
526 VLOG(2) << "Downloaded an unsigned binary: "
527 << file_path.value();
529 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed);
530 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime",
531 base::TimeTicks::Now() - start_time);
533 start_time = base::TimeTicks::Now();
534 binary_feature_extractor_->ExtractImageHeaders(file_path, &image_headers_);
535 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractImageHeadersTime",
536 base::TimeTicks::Now() - start_time);
538 OnFileFeatureExtractionDone();
541 void StartExtractZipFeatures() {
542 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
543 DCHECK(item_); // Called directly from Start(), item should still exist.
544 zip_analysis_start_time_ = base::TimeTicks::Now();
545 // We give the zip analyzer a weak pointer to this object. Since the
546 // analyzer is refcounted, it might outlive the request.
547 analyzer_ = new SandboxedZipAnalyzer(
548 item_->GetFullPath(),
549 base::Bind(&CheckClientDownloadRequest::OnZipAnalysisFinished,
550 weakptr_factory_.GetWeakPtr()));
551 analyzer_->Start();
554 void OnZipAnalysisFinished(const zip_analyzer::Results& results) {
555 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
556 if (!service_)
557 return;
558 if (results.success) {
559 zipped_executable_ = results.has_executable;
560 VLOG(1) << "Zip analysis finished for " << item_->GetFullPath().value()
561 << ", has_executable=" << results.has_executable
562 << " has_archive=" << results.has_archive;
563 } else {
564 VLOG(1) << "Zip analysis failed for " << item_->GetFullPath().value();
566 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
567 zipped_executable_);
568 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable",
569 results.has_archive && !zipped_executable_);
570 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime",
571 base::TimeTicks::Now() - zip_analysis_start_time_);
573 if (!zipped_executable_) {
574 PostFinishTask(UNKNOWN, REASON_ARCHIVE_WITHOUT_BINARIES);
575 return;
577 OnFileFeatureExtractionDone();
580 static void RecordCountOfSignedOrWhitelistedDownload() {
581 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
584 void CheckWhitelists() {
585 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
587 if (!database_manager_.get()) {
588 PostFinishTask(UNKNOWN, REASON_SB_DISABLED);
589 return;
592 const GURL& url = url_chain_.back();
593 if (url.is_valid() && database_manager_->MatchDownloadWhitelistUrl(url)) {
594 VLOG(2) << url << " is on the download whitelist.";
595 RecordCountOfSignedOrWhitelistedDownload();
596 PostFinishTask(SAFE, REASON_WHITELISTED_URL);
597 return;
600 if (signature_info_.trusted()) {
601 RecordCountOfSignedOrWhitelistedDownload();
602 for (int i = 0; i < signature_info_.certificate_chain_size(); ++i) {
603 if (CertificateChainIsWhitelisted(
604 signature_info_.certificate_chain(i))) {
605 PostFinishTask(SAFE, REASON_TRUSTED_EXECUTABLE);
606 return;
611 if (!pingback_enabled_) {
612 PostFinishTask(UNKNOWN, REASON_PING_DISABLED);
613 return;
616 // Currently, the UI only works on Windows so we don't even bother with
617 // pinging the server if we're not on Windows.
618 // TODO(noelutz): change this code once the UI is done for Linux and Mac.
619 #if defined(OS_MACOSX)
620 // TODO(mattm): remove this (see crbug.com/414834).
621 if (base::FieldTrialList::FindFullName("SafeBrowsingOSXClientDownloadPings")
622 != "Enabled") {
623 PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED);
624 return;
626 #endif
627 #if defined(OS_WIN) || defined(OS_MACOSX)
628 // The URLFetcher is owned by the UI thread, so post a message to
629 // start the pingback.
630 BrowserThread::PostTask(
631 BrowserThread::UI,
632 FROM_HERE,
633 base::Bind(&CheckClientDownloadRequest::GetTabRedirects, this));
634 #else
635 PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED);
636 #endif
639 void GetTabRedirects() {
640 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
641 if (!tab_url_.is_valid()) {
642 SendRequest();
643 return;
646 Profile* profile = Profile::FromBrowserContext(item_->GetBrowserContext());
647 HistoryService* history =
648 HistoryServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS);
649 if (!history) {
650 SendRequest();
651 return;
654 history->QueryRedirectsTo(
655 tab_url_,
656 base::Bind(&CheckClientDownloadRequest::OnGotTabRedirects,
657 base::Unretained(this),
658 tab_url_),
659 &request_tracker_);
662 void OnGotTabRedirects(const GURL& url,
663 const history::RedirectList* redirect_list) {
664 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
665 DCHECK_EQ(url, tab_url_);
667 if (!redirect_list->empty()) {
668 tab_redirects_.insert(
669 tab_redirects_.end(), redirect_list->rbegin(), redirect_list->rend());
672 SendRequest();
675 void SendRequest() {
676 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
678 // This is our last chance to check whether the request has been canceled
679 // before sending it.
680 if (!service_)
681 return;
683 ClientDownloadRequest request;
684 request.set_url(item_->GetUrlChain().back().spec());
685 request.mutable_digests()->set_sha256(item_->GetHash());
686 request.set_length(item_->GetReceivedBytes());
687 for (size_t i = 0; i < item_->GetUrlChain().size(); ++i) {
688 ClientDownloadRequest::Resource* resource = request.add_resources();
689 resource->set_url(item_->GetUrlChain()[i].spec());
690 if (i == item_->GetUrlChain().size() - 1) {
691 // The last URL in the chain is the download URL.
692 resource->set_type(ClientDownloadRequest::DOWNLOAD_URL);
693 resource->set_referrer(item_->GetReferrerUrl().spec());
694 DVLOG(2) << "dl url " << resource->url();
695 if (!item_->GetRemoteAddress().empty()) {
696 resource->set_remote_ip(item_->GetRemoteAddress());
697 DVLOG(2) << " dl url remote addr: " << resource->remote_ip();
699 DVLOG(2) << "dl referrer " << resource->referrer();
700 } else {
701 DVLOG(2) << "dl redirect " << i << " " << resource->url();
702 resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT);
704 // TODO(noelutz): fill out the remote IP addresses.
706 // TODO(mattm): fill out the remote IP addresses for tab resources.
707 for (size_t i = 0; i < tab_redirects_.size(); ++i) {
708 ClientDownloadRequest::Resource* resource = request.add_resources();
709 DVLOG(2) << "tab redirect " << i << " " << tab_redirects_[i].spec();
710 resource->set_url(tab_redirects_[i].spec());
711 resource->set_type(ClientDownloadRequest::TAB_REDIRECT);
713 if (tab_url_.is_valid()) {
714 ClientDownloadRequest::Resource* resource = request.add_resources();
715 resource->set_url(tab_url_.spec());
716 DVLOG(2) << "tab url " << resource->url();
717 resource->set_type(ClientDownloadRequest::TAB_URL);
718 if (tab_referrer_url_.is_valid()) {
719 resource->set_referrer(tab_referrer_url_.spec());
720 DVLOG(2) << "tab referrer " << resource->referrer();
724 request.set_user_initiated(item_->HasUserGesture());
725 request.set_file_basename(
726 item_->GetTargetFilePath().BaseName().AsUTF8Unsafe());
727 request.set_download_type(type_);
728 request.mutable_signature()->CopyFrom(signature_info_);
729 request.mutable_image_headers()->CopyFrom(image_headers_);
730 if (!request.SerializeToString(&client_download_request_data_)) {
731 FinishRequest(UNKNOWN, REASON_INVALID_REQUEST_PROTO);
732 return;
735 VLOG(2) << "Sending a request for URL: "
736 << item_->GetUrlChain().back();
737 fetcher_.reset(net::URLFetcher::Create(0 /* ID used for testing */,
738 GetDownloadRequestUrl(),
739 net::URLFetcher::POST,
740 this));
741 fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
742 fetcher_->SetAutomaticallyRetryOn5xx(false); // Don't retry on error.
743 fetcher_->SetRequestContext(service_->request_context_getter_.get());
744 fetcher_->SetUploadData("application/octet-stream",
745 client_download_request_data_);
746 request_start_time_ = base::TimeTicks::Now();
747 UMA_HISTOGRAM_COUNTS("SBClientDownload.DownloadRequestPayloadSize",
748 client_download_request_data_.size());
749 fetcher_->Start();
752 void PostFinishTask(DownloadCheckResult result,
753 DownloadCheckResultReason reason) {
754 BrowserThread::PostTask(
755 BrowserThread::UI,
756 FROM_HERE,
757 base::Bind(&CheckClientDownloadRequest::FinishRequest, this, result,
758 reason));
761 void FinishRequest(DownloadCheckResult result,
762 DownloadCheckResultReason reason) {
763 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
764 if (finished_) {
765 return;
767 finished_ = true;
768 // Ensure the timeout task is cancelled while we still have a non-zero
769 // refcount. (crbug.com/240449)
770 weakptr_factory_.InvalidateWeakPtrs();
771 if (!request_start_time_.is_null()) {
772 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestNetworkStats",
773 reason,
774 REASON_MAX);
776 if (!timeout_start_time_.is_null()) {
777 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestTimeoutStats",
778 reason,
779 REASON_MAX);
780 if (reason != REASON_REQUEST_CANCELED) {
781 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestTimeoutDuration",
782 base::TimeTicks::Now() - timeout_start_time_);
785 if (service_) {
786 VLOG(2) << "SafeBrowsing download verdict for: "
787 << item_->DebugString(true) << " verdict:" << reason
788 << " result:" << result;
789 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
790 reason,
791 REASON_MAX);
792 #if defined(OS_MACOSX)
793 // OSX is currently sending pings only for evaluation purposes, ignore
794 // the result for now.
795 // TODO(mattm): remove this and update the ifdef in
796 // DownloadItemImpl::IsDangerous (see crbug.com/413968).
797 result = UNKNOWN;
798 #endif
799 callback_.Run(result);
800 item_->RemoveObserver(this);
801 item_ = NULL;
802 DownloadProtectionService* service = service_;
803 service_ = NULL;
804 service->RequestFinished(this);
805 // DownloadProtectionService::RequestFinished will decrement our refcount,
806 // so we may be deleted now.
807 } else {
808 callback_.Run(UNKNOWN);
812 bool CertificateChainIsWhitelisted(
813 const ClientDownloadRequest_CertificateChain& chain) {
814 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
815 if (chain.element_size() < 2) {
816 // We need to have both a signing certificate and its issuer certificate
817 // present to construct a whitelist entry.
818 return false;
820 scoped_refptr<net::X509Certificate> cert =
821 net::X509Certificate::CreateFromBytes(
822 chain.element(0).certificate().data(),
823 chain.element(0).certificate().size());
824 if (!cert.get()) {
825 return false;
828 for (int i = 1; i < chain.element_size(); ++i) {
829 scoped_refptr<net::X509Certificate> issuer =
830 net::X509Certificate::CreateFromBytes(
831 chain.element(i).certificate().data(),
832 chain.element(i).certificate().size());
833 if (!issuer.get()) {
834 return false;
836 std::vector<std::string> whitelist_strings;
837 DownloadProtectionService::GetCertificateWhitelistStrings(
838 *cert.get(), *issuer.get(), &whitelist_strings);
839 for (size_t j = 0; j < whitelist_strings.size(); ++j) {
840 if (database_manager_->MatchDownloadWhitelistString(
841 whitelist_strings[j])) {
842 VLOG(2) << "Certificate matched whitelist, cert="
843 << cert->subject().GetDisplayName()
844 << " issuer=" << issuer->subject().GetDisplayName();
845 return true;
848 cert = issuer;
850 return false;
853 // The DownloadItem we are checking. Will be NULL if the request has been
854 // canceled. Must be accessed only on UI thread.
855 content::DownloadItem* item_;
856 // Copies of data from |item_| for access on other threads.
857 std::vector<GURL> url_chain_;
858 GURL referrer_url_;
859 // URL chain of redirects leading to (but not including) |tab_url|.
860 std::vector<GURL> tab_redirects_;
861 // URL and referrer of the window the download was started from.
862 GURL tab_url_;
863 GURL tab_referrer_url_;
865 bool zipped_executable_;
866 ClientDownloadRequest_SignatureInfo signature_info_;
867 ClientDownloadRequest_ImageHeaders image_headers_;
868 CheckDownloadCallback callback_;
869 // Will be NULL if the request has been canceled.
870 DownloadProtectionService* service_;
871 scoped_refptr<BinaryFeatureExtractor> binary_feature_extractor_;
872 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
873 const bool pingback_enabled_;
874 scoped_ptr<net::URLFetcher> fetcher_;
875 scoped_refptr<SandboxedZipAnalyzer> analyzer_;
876 base::TimeTicks zip_analysis_start_time_;
877 bool finished_;
878 ClientDownloadRequest::DownloadType type_;
879 std::string client_download_request_data_;
880 base::CancelableTaskTracker request_tracker_; // For HistoryService lookup.
881 base::WeakPtrFactory<CheckClientDownloadRequest> weakptr_factory_;
882 base::TimeTicks start_time_; // Used for stats.
883 base::TimeTicks timeout_start_time_;
884 base::TimeTicks request_start_time_;
886 DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest);
889 DownloadProtectionService::DownloadProtectionService(
890 SafeBrowsingService* sb_service,
891 net::URLRequestContextGetter* request_context_getter)
892 : request_context_getter_(request_context_getter),
893 enabled_(false),
894 binary_feature_extractor_(new BinaryFeatureExtractor()),
895 download_request_timeout_ms_(kDownloadRequestTimeoutMs),
896 feedback_service_(new DownloadFeedbackService(
897 request_context_getter, BrowserThread::GetBlockingPool())) {
899 if (sb_service) {
900 ui_manager_ = sb_service->ui_manager();
901 database_manager_ = sb_service->database_manager();
905 DownloadProtectionService::~DownloadProtectionService() {
906 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
907 CancelPendingRequests();
910 void DownloadProtectionService::SetEnabled(bool enabled) {
911 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
912 if (enabled == enabled_) {
913 return;
915 enabled_ = enabled;
916 if (!enabled_) {
917 CancelPendingRequests();
921 void DownloadProtectionService::CheckClientDownload(
922 content::DownloadItem* item,
923 const CheckDownloadCallback& callback) {
924 scoped_refptr<CheckClientDownloadRequest> request(
925 new CheckClientDownloadRequest(item, callback, this,
926 database_manager_,
927 binary_feature_extractor_.get()));
928 download_requests_.insert(request);
929 request->Start();
932 void DownloadProtectionService::CheckDownloadUrl(
933 const content::DownloadItem& item,
934 const CheckDownloadCallback& callback) {
935 DCHECK(!item.GetUrlChain().empty());
936 scoped_refptr<DownloadUrlSBClient> client(
937 new DownloadUrlSBClient(item, callback, ui_manager_, database_manager_));
938 // The client will release itself once it is done.
939 BrowserThread::PostTask(
940 BrowserThread::IO,
941 FROM_HERE,
942 base::Bind(&DownloadUrlSBClient::StartCheck, client));
945 bool DownloadProtectionService::IsSupportedDownload(
946 const content::DownloadItem& item,
947 const base::FilePath& target_path) const {
948 // Currently, the UI is only enabled on Windows. On Mac we send the ping but
949 // ignore the result (see ifdef in FinishRequest). On Linux we still
950 // want to show the dangerous file type warning if the file is possibly
951 // dangerous which means we have to always return false here.
952 #if defined(OS_WIN)
953 DownloadCheckResultReason reason = REASON_MAX;
954 ClientDownloadRequest::DownloadType type =
955 ClientDownloadRequest::WIN_EXECUTABLE;
956 return (CheckClientDownloadRequest::IsSupportedDownload(
957 item, target_path, &reason, &type) &&
958 (ClientDownloadRequest::CHROME_EXTENSION != type));
959 #else
960 return false;
961 #endif
964 void DownloadProtectionService::CancelPendingRequests() {
965 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
966 for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
967 download_requests_.begin();
968 it != download_requests_.end();) {
969 // We need to advance the iterator before we cancel because canceling
970 // the request will invalidate it when RequestFinished is called below.
971 scoped_refptr<CheckClientDownloadRequest> tmp = *it++;
972 tmp->Cancel();
974 DCHECK(download_requests_.empty());
977 void DownloadProtectionService::RequestFinished(
978 CheckClientDownloadRequest* request) {
979 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
980 std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
981 download_requests_.find(request);
982 DCHECK(it != download_requests_.end());
983 download_requests_.erase(*it);
986 void DownloadProtectionService::ShowDetailsForDownload(
987 const content::DownloadItem& item,
988 content::PageNavigator* navigator) {
989 GURL learn_more_url(chrome::kDownloadScanningLearnMoreURL);
990 learn_more_url = google_util::AppendGoogleLocaleParam(
991 learn_more_url, g_browser_process->GetApplicationLocale());
992 navigator->OpenURL(
993 content::OpenURLParams(learn_more_url,
994 content::Referrer(),
995 NEW_FOREGROUND_TAB,
996 ui::PAGE_TRANSITION_LINK,
997 false));
1000 namespace {
1001 // Escapes a certificate attribute so that it can be used in a whitelist
1002 // entry. Currently, we only escape slashes, since they are used as a
1003 // separator between attributes.
1004 std::string EscapeCertAttribute(const std::string& attribute) {
1005 std::string escaped;
1006 for (size_t i = 0; i < attribute.size(); ++i) {
1007 if (attribute[i] == '%') {
1008 escaped.append("%25");
1009 } else if (attribute[i] == '/') {
1010 escaped.append("%2F");
1011 } else {
1012 escaped.push_back(attribute[i]);
1015 return escaped;
1017 } // namespace
1019 // static
1020 void DownloadProtectionService::GetCertificateWhitelistStrings(
1021 const net::X509Certificate& certificate,
1022 const net::X509Certificate& issuer,
1023 std::vector<std::string>* whitelist_strings) {
1024 // The whitelist paths are in the format:
1025 // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit]
1027 // Any of CN, O, or OU may be omitted from the whitelist entry, in which
1028 // case they match anything. However, the attributes that do appear will
1029 // always be in the order shown above. At least one attribute will always
1030 // be present.
1032 const net::CertPrincipal& subject = certificate.subject();
1033 std::vector<std::string> ou_tokens;
1034 for (size_t i = 0; i < subject.organization_unit_names.size(); ++i) {
1035 ou_tokens.push_back(
1036 "/OU=" + EscapeCertAttribute(subject.organization_unit_names[i]));
1039 std::vector<std::string> o_tokens;
1040 for (size_t i = 0; i < subject.organization_names.size(); ++i) {
1041 o_tokens.push_back(
1042 "/O=" + EscapeCertAttribute(subject.organization_names[i]));
1045 std::string cn_token;
1046 if (!subject.common_name.empty()) {
1047 cn_token = "/CN=" + EscapeCertAttribute(subject.common_name);
1050 std::set<std::string> paths_to_check;
1051 if (!cn_token.empty()) {
1052 paths_to_check.insert(cn_token);
1054 for (size_t i = 0; i < o_tokens.size(); ++i) {
1055 paths_to_check.insert(cn_token + o_tokens[i]);
1056 paths_to_check.insert(o_tokens[i]);
1057 for (size_t j = 0; j < ou_tokens.size(); ++j) {
1058 paths_to_check.insert(cn_token + o_tokens[i] + ou_tokens[j]);
1059 paths_to_check.insert(o_tokens[i] + ou_tokens[j]);
1062 for (size_t i = 0; i < ou_tokens.size(); ++i) {
1063 paths_to_check.insert(cn_token + ou_tokens[i]);
1064 paths_to_check.insert(ou_tokens[i]);
1067 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data,
1068 sizeof(issuer.fingerprint().data));
1069 for (std::set<std::string>::iterator it = paths_to_check.begin();
1070 it != paths_to_check.end(); ++it) {
1071 whitelist_strings->push_back("cert/" + issuer_fp + *it);
1075 // static
1076 GURL DownloadProtectionService::GetDownloadRequestUrl() {
1077 GURL url(kDownloadRequestUrl);
1078 std::string api_key = google_apis::GetAPIKey();
1079 if (!api_key.empty())
1080 url = url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
1082 return url;
1085 } // namespace safe_browsing