Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / safe_browsing / download_protection_service.cc
blob58f9e89d2f17b9c9c52be9cb92ae00e560d85119
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_factory.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/safe_browsing/download_feedback_service.h"
27 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
28 #include "chrome/browser/safe_browsing/sandboxed_zip_analyzer.h"
29 #include "chrome/browser/ui/browser.h"
30 #include "chrome/browser/ui/browser_list.h"
31 #include "chrome/common/safe_browsing/binary_feature_extractor.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_results.h"
35 #include "chrome/common/url_constants.h"
36 #include "components/google/core/browser/google_util.h"
37 #include "components/history/core/browser/history_service.h"
38 #include "content/public/browser/browser_thread.h"
39 #include "content/public/browser/download_item.h"
40 #include "content/public/browser/page_navigator.h"
41 #include "crypto/sha2.h"
42 #include "google_apis/google_api_keys.h"
43 #include "net/base/escape.h"
44 #include "net/base/load_flags.h"
45 #include "net/base/url_util.h"
46 #include "net/cert/x509_cert_types.h"
47 #include "net/cert/x509_certificate.h"
48 #include "net/http/http_status_code.h"
49 #include "net/url_request/url_fetcher.h"
50 #include "net/url_request/url_fetcher_delegate.h"
51 #include "net/url_request/url_request_context_getter.h"
52 #include "net/url_request/url_request_status.h"
54 using content::BrowserThread;
56 namespace {
57 static const int64 kDownloadRequestTimeoutMs = 7000;
58 } // namespace
60 namespace safe_browsing {
62 const char DownloadProtectionService::kDownloadRequestUrl[] =
63 "https://sb-ssl.google.com/safebrowsing/clientreport/download";
65 namespace {
66 // List of extensions for which we track some UMA stats. The position of the
67 // extension in kDangerousFileTypes is considered to be the UMA enumeration
68 // value. Naturally, new values should only be added at the end.
69 const base::FilePath::CharType* const kDangerousFileTypes[] = {
70 FILE_PATH_LITERAL(".exe"),
71 FILE_PATH_LITERAL(".msi"),
72 FILE_PATH_LITERAL(".cab"),
73 FILE_PATH_LITERAL(".sys"),
74 FILE_PATH_LITERAL(".scr"),
75 FILE_PATH_LITERAL(".drv"),
76 FILE_PATH_LITERAL(".bat"),
77 FILE_PATH_LITERAL(".zip"),
78 FILE_PATH_LITERAL(".rar"),
79 FILE_PATH_LITERAL(".dll"),
80 FILE_PATH_LITERAL(".pif"),
81 FILE_PATH_LITERAL(".com"),
82 FILE_PATH_LITERAL(".jar"),
83 FILE_PATH_LITERAL(".class"),
84 FILE_PATH_LITERAL(".pdf"),
85 FILE_PATH_LITERAL(".vb"),
86 FILE_PATH_LITERAL(".reg"),
87 FILE_PATH_LITERAL(".grp"),
88 nullptr, // The "Other" bucket. This is in the middle of the array due to
89 // historical reasons.
90 FILE_PATH_LITERAL(".crx"),
91 FILE_PATH_LITERAL(".apk"),
92 FILE_PATH_LITERAL(".dmg"),
93 FILE_PATH_LITERAL(".pkg"),
94 FILE_PATH_LITERAL(".torrent"),
95 FILE_PATH_LITERAL(".website"),
96 FILE_PATH_LITERAL(".url"),
97 FILE_PATH_LITERAL(".vbe"),
98 FILE_PATH_LITERAL(".vbs"),
99 FILE_PATH_LITERAL(".js"),
100 FILE_PATH_LITERAL(".jse"),
101 FILE_PATH_LITERAL(".mht"),
102 FILE_PATH_LITERAL(".mhtml"),
103 FILE_PATH_LITERAL(".msc"),
104 FILE_PATH_LITERAL(".msp"),
105 FILE_PATH_LITERAL(".mst"),
106 FILE_PATH_LITERAL(".bas"),
107 FILE_PATH_LITERAL(".hta"),
108 FILE_PATH_LITERAL(".msh"),
109 FILE_PATH_LITERAL(".msh1"),
110 FILE_PATH_LITERAL(".msh1xml"),
111 FILE_PATH_LITERAL(".msh2"),
112 FILE_PATH_LITERAL(".msh2xml"),
113 FILE_PATH_LITERAL(".mshxml"),
114 FILE_PATH_LITERAL(".ps1"),
115 FILE_PATH_LITERAL(".ps1xml"),
116 FILE_PATH_LITERAL(".ps2"),
117 FILE_PATH_LITERAL(".ps2xml"),
118 FILE_PATH_LITERAL(".psc1"),
119 FILE_PATH_LITERAL(".psc2"),
120 FILE_PATH_LITERAL(".scf"),
121 FILE_PATH_LITERAL(".sct"),
122 FILE_PATH_LITERAL(".wsf"),
125 // UMA enumeration value for unrecognized file types. This is the array index of
126 // the "Other" bucket in kDangerousFileTypes.
127 const int EXTENSION_OTHER = 18;
129 void RecordFileExtensionType(const base::FilePath& file) {
130 DCHECK_EQ(static_cast<base::FilePath::CharType*>(nullptr),
131 kDangerousFileTypes[EXTENSION_OTHER]);
133 int extension_type = EXTENSION_OTHER;
134 for (const auto& extension : kDangerousFileTypes) {
135 if (extension && file.MatchesExtension(extension)) {
136 extension_type = &extension - kDangerousFileTypes;
137 break;
141 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadExtensions",
142 extension_type, arraysize(kDangerousFileTypes));
145 // Enumerate for histogramming purposes.
146 // DO NOT CHANGE THE ORDERING OF THESE VALUES (different histogram data will
147 // be mixed together based on their values).
148 enum SBStatsType {
149 DOWNLOAD_URL_CHECKS_TOTAL,
150 DOWNLOAD_URL_CHECKS_CANCELED,
151 DOWNLOAD_URL_CHECKS_MALWARE,
153 DOWNLOAD_HASH_CHECKS_TOTAL,
154 DOWNLOAD_HASH_CHECKS_MALWARE,
156 // Memory space for histograms is determined by the max.
157 // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
158 DOWNLOAD_CHECKS_MAX
161 // Prepares URLs to be put into a ping message. Currently this just shortens
162 // data: URIs, other URLs are included verbatim.
163 std::string SanitizeUrl(const GURL& url) {
164 std::string spec = url.spec();
165 if (url.SchemeIs(url::kDataScheme)) {
166 size_t comma_pos = spec.find(',');
167 if (comma_pos != std::string::npos && comma_pos != spec.size() - 1) {
168 std::string hash_value = crypto::SHA256HashString(spec);
169 spec.erase(comma_pos + 1);
170 spec += base::HexEncode(hash_value.data(), hash_value.size());
173 return spec;
176 } // namespace
178 // Parent SafeBrowsing::Client class used to lookup the bad binary
179 // URL and digest list. There are two sub-classes (one for each list).
180 class DownloadSBClient
181 : public SafeBrowsingDatabaseManager::Client,
182 public base::RefCountedThreadSafe<DownloadSBClient> {
183 public:
184 DownloadSBClient(
185 const content::DownloadItem& item,
186 const DownloadProtectionService::CheckDownloadCallback& callback,
187 const scoped_refptr<SafeBrowsingUIManager>& ui_manager,
188 SBStatsType total_type,
189 SBStatsType dangerous_type)
190 : sha256_hash_(item.GetHash()),
191 url_chain_(item.GetUrlChain()),
192 referrer_url_(item.GetReferrerUrl()),
193 callback_(callback),
194 ui_manager_(ui_manager),
195 start_time_(base::TimeTicks::Now()),
196 total_type_(total_type),
197 dangerous_type_(dangerous_type) {}
199 virtual void StartCheck() = 0;
200 virtual bool IsDangerous(SBThreatType threat_type) const = 0;
202 protected:
203 friend class base::RefCountedThreadSafe<DownloadSBClient>;
204 ~DownloadSBClient() override {}
206 void CheckDone(SBThreatType threat_type) {
207 DownloadProtectionService::DownloadCheckResult result =
208 IsDangerous(threat_type) ?
209 DownloadProtectionService::DANGEROUS :
210 DownloadProtectionService::SAFE;
211 BrowserThread::PostTask(BrowserThread::UI,
212 FROM_HERE,
213 base::Bind(callback_, result));
214 UpdateDownloadCheckStats(total_type_);
215 if (threat_type != SB_THREAT_TYPE_SAFE) {
216 UpdateDownloadCheckStats(dangerous_type_);
217 BrowserThread::PostTask(
218 BrowserThread::UI,
219 FROM_HERE,
220 base::Bind(&DownloadSBClient::ReportMalware,
221 this, threat_type));
225 void ReportMalware(SBThreatType threat_type) {
226 std::string post_data;
227 if (!sha256_hash_.empty())
228 post_data += base::HexEncode(sha256_hash_.data(),
229 sha256_hash_.size()) + "\n";
230 for (size_t i = 0; i < url_chain_.size(); ++i) {
231 post_data += url_chain_[i].spec() + "\n";
233 ui_manager_->ReportSafeBrowsingHit(
234 url_chain_.back(), // malicious_url
235 url_chain_.front(), // page_url
236 referrer_url_,
237 true, // is_subresource
238 threat_type,
239 post_data);
242 void UpdateDownloadCheckStats(SBStatsType stat_type) {
243 UMA_HISTOGRAM_ENUMERATION("SB2.DownloadChecks",
244 stat_type,
245 DOWNLOAD_CHECKS_MAX);
248 std::string sha256_hash_;
249 std::vector<GURL> url_chain_;
250 GURL referrer_url_;
251 DownloadProtectionService::CheckDownloadCallback callback_;
252 scoped_refptr<SafeBrowsingUIManager> ui_manager_;
253 base::TimeTicks start_time_;
255 private:
256 const SBStatsType total_type_;
257 const SBStatsType dangerous_type_;
259 DISALLOW_COPY_AND_ASSIGN(DownloadSBClient);
262 class DownloadUrlSBClient : public DownloadSBClient {
263 public:
264 DownloadUrlSBClient(
265 const content::DownloadItem& item,
266 const DownloadProtectionService::CheckDownloadCallback& callback,
267 const scoped_refptr<SafeBrowsingUIManager>& ui_manager,
268 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager)
269 : DownloadSBClient(item, callback, ui_manager,
270 DOWNLOAD_URL_CHECKS_TOTAL,
271 DOWNLOAD_URL_CHECKS_MALWARE),
272 database_manager_(database_manager) { }
274 void StartCheck() override {
275 DCHECK_CURRENTLY_ON(BrowserThread::IO);
276 if (!database_manager_.get() ||
277 database_manager_->CheckDownloadUrl(url_chain_, this)) {
278 CheckDone(SB_THREAT_TYPE_SAFE);
279 } else {
280 AddRef(); // SafeBrowsingService takes a pointer not a scoped_refptr.
284 bool IsDangerous(SBThreatType threat_type) const override {
285 return threat_type == SB_THREAT_TYPE_BINARY_MALWARE_URL;
288 void OnCheckDownloadUrlResult(const std::vector<GURL>& url_chain,
289 SBThreatType threat_type) override {
290 CheckDone(threat_type);
291 UMA_HISTOGRAM_TIMES("SB2.DownloadUrlCheckDuration",
292 base::TimeTicks::Now() - start_time_);
293 Release();
296 protected:
297 ~DownloadUrlSBClient() override {}
299 private:
300 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
302 DISALLOW_COPY_AND_ASSIGN(DownloadUrlSBClient);
305 class DownloadProtectionService::CheckClientDownloadRequest
306 : public base::RefCountedThreadSafe<
307 DownloadProtectionService::CheckClientDownloadRequest,
308 BrowserThread::DeleteOnUIThread>,
309 public net::URLFetcherDelegate,
310 public content::DownloadItem::Observer {
311 public:
312 CheckClientDownloadRequest(
313 content::DownloadItem* item,
314 const CheckDownloadCallback& callback,
315 DownloadProtectionService* service,
316 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
317 BinaryFeatureExtractor* binary_feature_extractor)
318 : item_(item),
319 url_chain_(item->GetUrlChain()),
320 referrer_url_(item->GetReferrerUrl()),
321 tab_url_(item->GetTabUrl()),
322 tab_referrer_url_(item->GetTabReferrerUrl()),
323 zipped_executable_(false),
324 callback_(callback),
325 service_(service),
326 binary_feature_extractor_(binary_feature_extractor),
327 database_manager_(database_manager),
328 pingback_enabled_(service_->enabled()),
329 finished_(false),
330 type_(ClientDownloadRequest::WIN_EXECUTABLE),
331 start_time_(base::TimeTicks::Now()),
332 weakptr_factory_(this) {
333 DCHECK_CURRENTLY_ON(BrowserThread::UI);
334 item_->AddObserver(this);
337 void Start() {
338 DVLOG(2) << "Starting SafeBrowsing download check for: "
339 << item_->DebugString(true);
340 DCHECK_CURRENTLY_ON(BrowserThread::UI);
341 // TODO(noelutz): implement some cache to make sure we don't issue the same
342 // request over and over again if a user downloads the same binary multiple
343 // times.
344 DownloadCheckResultReason reason = REASON_MAX;
345 if (!IsSupportedDownload(
346 *item_, item_->GetTargetFilePath(), &reason, &type_)) {
347 switch (reason) {
348 case REASON_EMPTY_URL_CHAIN:
349 case REASON_INVALID_URL:
350 case REASON_UNSUPPORTED_URL_SCHEME:
351 PostFinishTask(UNKNOWN, reason);
352 return;
354 case REASON_NOT_BINARY_FILE:
355 RecordFileExtensionType(item_->GetTargetFilePath());
356 PostFinishTask(UNKNOWN, reason);
357 return;
359 default:
360 // We only expect the reasons explicitly handled above.
361 NOTREACHED();
364 RecordFileExtensionType(item_->GetTargetFilePath());
366 // Compute features from the file contents. Note that we record histograms
367 // based on the result, so this runs regardless of whether the pingbacks
368 // are enabled.
369 if (item_->GetTargetFilePath().MatchesExtension(
370 FILE_PATH_LITERAL(".zip"))) {
371 StartExtractZipFeatures();
372 } else {
373 DCHECK(!download_protection_util::IsArchiveFile(
374 item_->GetTargetFilePath()));
375 StartExtractFileFeatures();
379 // Start a timeout to cancel the request if it takes too long.
380 // This should only be called after we have finished accessing the file.
381 void StartTimeout() {
382 DCHECK_CURRENTLY_ON(BrowserThread::UI);
383 if (!service_) {
384 // Request has already been cancelled.
385 return;
387 timeout_start_time_ = base::TimeTicks::Now();
388 BrowserThread::PostDelayedTask(
389 BrowserThread::UI,
390 FROM_HERE,
391 base::Bind(&CheckClientDownloadRequest::Cancel,
392 weakptr_factory_.GetWeakPtr()),
393 base::TimeDelta::FromMilliseconds(
394 service_->download_request_timeout_ms()));
397 // Canceling a request will cause us to always report the result as UNKNOWN
398 // unless a pending request is about to call FinishRequest.
399 void Cancel() {
400 DCHECK_CURRENTLY_ON(BrowserThread::UI);
401 if (fetcher_.get()) {
402 // The DownloadProtectionService is going to release its reference, so we
403 // might be destroyed before the URLFetcher completes. Cancel the
404 // fetcher so it does not try to invoke OnURLFetchComplete.
405 fetcher_.reset();
407 // Note: If there is no fetcher, then some callback is still holding a
408 // reference to this object. We'll eventually wind up in some method on
409 // the UI thread that will call FinishRequest() again. If FinishRequest()
410 // is called a second time, it will be a no-op.
411 FinishRequest(UNKNOWN, REASON_REQUEST_CANCELED);
412 // Calling FinishRequest might delete this object, we may be deleted by
413 // this point.
416 // content::DownloadItem::Observer implementation.
417 void OnDownloadDestroyed(content::DownloadItem* download) override {
418 Cancel();
419 DCHECK(item_ == NULL);
422 // From the net::URLFetcherDelegate interface.
423 void OnURLFetchComplete(const net::URLFetcher* source) override {
424 DCHECK_CURRENTLY_ON(BrowserThread::UI);
425 DCHECK_EQ(source, fetcher_.get());
426 DVLOG(2) << "Received a response for URL: "
427 << item_->GetUrlChain().back() << ": success="
428 << source->GetStatus().is_success() << " response_code="
429 << source->GetResponseCode();
430 if (source->GetStatus().is_success()) {
431 UMA_HISTOGRAM_SPARSE_SLOWLY(
432 "SBClientDownload.DownloadRequestResponseCode",
433 source->GetResponseCode());
435 UMA_HISTOGRAM_SPARSE_SLOWLY(
436 "SBClientDownload.DownloadRequestNetError",
437 -source->GetStatus().error());
438 DownloadCheckResultReason reason = REASON_SERVER_PING_FAILED;
439 DownloadCheckResult result = UNKNOWN;
440 if (source->GetStatus().is_success() &&
441 net::HTTP_OK == source->GetResponseCode()) {
442 ClientDownloadResponse response;
443 std::string data;
444 bool got_data = source->GetResponseAsString(&data);
445 DCHECK(got_data);
446 if (!response.ParseFromString(data)) {
447 reason = REASON_INVALID_RESPONSE_PROTO;
448 result = UNKNOWN;
449 } else if (response.verdict() == ClientDownloadResponse::SAFE) {
450 reason = REASON_DOWNLOAD_SAFE;
451 result = SAFE;
452 } else if (service_ && !service_->IsSupportedDownload(
453 *item_, item_->GetTargetFilePath())) {
454 // The client of the download protection service assumes that we don't
455 // support this download so we cannot return any other verdict than
456 // UNKNOWN even if the server says it's dangerous to download this file.
457 // Note: if service_ is NULL we already cancelled the request and
458 // returned UNKNOWN.
459 reason = REASON_DOWNLOAD_NOT_SUPPORTED;
460 result = UNKNOWN;
461 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS) {
462 reason = REASON_DOWNLOAD_DANGEROUS;
463 result = DANGEROUS;
464 } else if (response.verdict() == ClientDownloadResponse::UNCOMMON) {
465 reason = REASON_DOWNLOAD_UNCOMMON;
466 result = UNCOMMON;
467 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS_HOST) {
468 reason = REASON_DOWNLOAD_DANGEROUS_HOST;
469 result = DANGEROUS_HOST;
470 } else if (
471 response.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED) {
472 reason = REASON_DOWNLOAD_POTENTIALLY_UNWANTED;
473 result = POTENTIALLY_UNWANTED;
474 } else {
475 LOG(DFATAL) << "Unknown download response verdict: "
476 << response.verdict();
477 reason = REASON_INVALID_RESPONSE_VERDICT;
478 result = UNKNOWN;
480 DownloadFeedbackService::MaybeStorePingsForDownload(
481 result, item_, client_download_request_data_, data);
483 // We don't need the fetcher anymore.
484 fetcher_.reset();
485 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration",
486 base::TimeTicks::Now() - start_time_);
487 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestNetworkDuration",
488 base::TimeTicks::Now() - request_start_time_);
489 FinishRequest(result, reason);
492 static bool IsSupportedDownload(const content::DownloadItem& item,
493 const base::FilePath& target_path,
494 DownloadCheckResultReason* reason,
495 ClientDownloadRequest::DownloadType* type) {
496 if (item.GetUrlChain().empty()) {
497 *reason = REASON_EMPTY_URL_CHAIN;
498 return false;
500 const GURL& final_url = item.GetUrlChain().back();
501 if (!final_url.is_valid() || final_url.is_empty()) {
502 *reason = REASON_INVALID_URL;
503 return false;
505 if (!download_protection_util::IsBinaryFile(target_path)) {
506 *reason = REASON_NOT_BINARY_FILE;
507 return false;
509 if ((!final_url.IsStandard() && !final_url.SchemeIsBlob() &&
510 !final_url.SchemeIs(url::kDataScheme)) ||
511 final_url.SchemeIsFile()) {
512 *reason = REASON_UNSUPPORTED_URL_SCHEME;
513 return false;
515 *type = download_protection_util::GetDownloadType(target_path);
516 return true;
519 private:
520 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
521 friend class base::DeleteHelper<CheckClientDownloadRequest>;
523 ~CheckClientDownloadRequest() override {
524 DCHECK_CURRENTLY_ON(BrowserThread::UI);
525 DCHECK(item_ == NULL);
528 void OnFileFeatureExtractionDone() {
529 // This can run in any thread, since it just posts more messages.
531 // TODO(noelutz): DownloadInfo should also contain the IP address of
532 // every URL in the redirect chain. We also should check whether the
533 // download URL is hosted on the internal network.
534 BrowserThread::PostTask(
535 BrowserThread::IO,
536 FROM_HERE,
537 base::Bind(&CheckClientDownloadRequest::CheckWhitelists, this));
539 // We wait until after the file checks finish to start the timeout, as
540 // windows can cause permissions errors if the timeout fired while we were
541 // checking the file signature and we tried to complete the download.
542 BrowserThread::PostTask(
543 BrowserThread::UI,
544 FROM_HERE,
545 base::Bind(&CheckClientDownloadRequest::StartTimeout, this));
548 void StartExtractFileFeatures() {
549 DCHECK_CURRENTLY_ON(BrowserThread::UI);
550 DCHECK(item_); // Called directly from Start(), item should still exist.
551 // Since we do blocking I/O, offload this to a worker thread.
552 // The task does not need to block shutdown.
553 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
554 FROM_HERE,
555 base::Bind(&CheckClientDownloadRequest::ExtractFileFeatures,
556 this, item_->GetFullPath()),
557 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
560 void ExtractFileFeatures(const base::FilePath& file_path) {
561 base::TimeTicks start_time = base::TimeTicks::Now();
562 binary_feature_extractor_->CheckSignature(file_path, &signature_info_);
563 bool is_signed = (signature_info_.certificate_chain_size() > 0);
564 if (is_signed) {
565 DVLOG(2) << "Downloaded a signed binary: " << file_path.value();
566 } else {
567 DVLOG(2) << "Downloaded an unsigned binary: "
568 << file_path.value();
570 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed);
571 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime",
572 base::TimeTicks::Now() - start_time);
574 start_time = base::TimeTicks::Now();
575 image_headers_.reset(new ClientDownloadRequest_ImageHeaders());
576 if (!binary_feature_extractor_->ExtractImageFeatures(
577 file_path,
578 BinaryFeatureExtractor::kDefaultOptions,
579 image_headers_.get(),
580 nullptr /* signed_data */)) {
581 image_headers_.reset();
583 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractImageHeadersTime",
584 base::TimeTicks::Now() - start_time);
586 OnFileFeatureExtractionDone();
589 void StartExtractZipFeatures() {
590 DCHECK_CURRENTLY_ON(BrowserThread::UI);
591 DCHECK(item_); // Called directly from Start(), item should still exist.
592 zip_analysis_start_time_ = base::TimeTicks::Now();
593 // We give the zip analyzer a weak pointer to this object. Since the
594 // analyzer is refcounted, it might outlive the request.
595 analyzer_ = new SandboxedZipAnalyzer(
596 item_->GetFullPath(),
597 base::Bind(&CheckClientDownloadRequest::OnZipAnalysisFinished,
598 weakptr_factory_.GetWeakPtr()));
599 analyzer_->Start();
602 void OnZipAnalysisFinished(const zip_analyzer::Results& results) {
603 DCHECK_CURRENTLY_ON(BrowserThread::UI);
604 if (!service_)
605 return;
606 if (results.success) {
607 zipped_executable_ = results.has_executable;
608 archived_binary_.CopyFrom(results.archived_binary);
609 DVLOG(1) << "Zip analysis finished for " << item_->GetFullPath().value()
610 << ", has_executable=" << results.has_executable
611 << " has_archive=" << results.has_archive;
612 } else {
613 DVLOG(1) << "Zip analysis failed for " << item_->GetFullPath().value();
615 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
616 zipped_executable_);
617 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable",
618 results.has_archive && !zipped_executable_);
619 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime",
620 base::TimeTicks::Now() - zip_analysis_start_time_);
622 if (!zipped_executable_) {
623 PostFinishTask(UNKNOWN, REASON_ARCHIVE_WITHOUT_BINARIES);
624 return;
626 OnFileFeatureExtractionDone();
629 static void RecordCountOfSignedOrWhitelistedDownload() {
630 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
633 void CheckWhitelists() {
634 DCHECK_CURRENTLY_ON(BrowserThread::IO);
636 if (!database_manager_.get()) {
637 PostFinishTask(UNKNOWN, REASON_SB_DISABLED);
638 return;
641 const GURL& url = url_chain_.back();
642 // TODO(asanka): This may acquire a lock on the SB DB on the IO thread.
643 if (url.is_valid() && database_manager_->MatchDownloadWhitelistUrl(url)) {
644 DVLOG(2) << url << " is on the download whitelist.";
645 RecordCountOfSignedOrWhitelistedDownload();
646 // TODO(grt): Continue processing without uploading so that
647 // ClientDownloadRequest callbacks can be run even for this type of safe
648 // download.
649 PostFinishTask(SAFE, REASON_WHITELISTED_URL);
650 return;
653 if (signature_info_.trusted()) {
654 RecordCountOfSignedOrWhitelistedDownload();
655 for (int i = 0; i < signature_info_.certificate_chain_size(); ++i) {
656 if (CertificateChainIsWhitelisted(
657 signature_info_.certificate_chain(i))) {
658 // TODO(grt): Continue processing without uploading so that
659 // ClientDownloadRequest callbacks can be run even for this type of
660 // safe download.
661 PostFinishTask(SAFE, REASON_TRUSTED_EXECUTABLE);
662 return;
667 if (!pingback_enabled_) {
668 PostFinishTask(UNKNOWN, REASON_PING_DISABLED);
669 return;
672 // Currently, the UI is only enabled on Windows and OSX so we don't even
673 // bother with pinging the server if we're not on one of those platforms.
674 // TODO(noelutz): change this code once the UI is done for Linux.
675 #if defined(OS_WIN) || defined(OS_MACOSX)
676 // The URLFetcher is owned by the UI thread, so post a message to
677 // start the pingback.
678 BrowserThread::PostTask(
679 BrowserThread::UI,
680 FROM_HERE,
681 base::Bind(&CheckClientDownloadRequest::GetTabRedirects, this));
682 #else
683 PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED);
684 #endif
687 void GetTabRedirects() {
688 DCHECK_CURRENTLY_ON(BrowserThread::UI);
689 if (!service_)
690 return;
692 if (!tab_url_.is_valid()) {
693 SendRequest();
694 return;
697 Profile* profile = Profile::FromBrowserContext(item_->GetBrowserContext());
698 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
699 profile, ServiceAccessType::EXPLICIT_ACCESS);
700 if (!history) {
701 SendRequest();
702 return;
705 history->QueryRedirectsTo(
706 tab_url_,
707 base::Bind(&CheckClientDownloadRequest::OnGotTabRedirects,
708 base::Unretained(this),
709 tab_url_),
710 &request_tracker_);
713 void OnGotTabRedirects(const GURL& url,
714 const history::RedirectList* redirect_list) {
715 DCHECK_CURRENTLY_ON(BrowserThread::UI);
716 DCHECK_EQ(url, tab_url_);
717 if (!service_)
718 return;
720 if (!redirect_list->empty()) {
721 tab_redirects_.insert(
722 tab_redirects_.end(), redirect_list->rbegin(), redirect_list->rend());
725 SendRequest();
728 void SendRequest() {
729 DCHECK_CURRENTLY_ON(BrowserThread::UI);
731 // This is our last chance to check whether the request has been canceled
732 // before sending it.
733 if (!service_)
734 return;
736 ClientDownloadRequest request;
737 request.set_url(SanitizeUrl(item_->GetUrlChain().back()));
738 request.mutable_digests()->set_sha256(item_->GetHash());
739 request.set_length(item_->GetReceivedBytes());
740 for (size_t i = 0; i < item_->GetUrlChain().size(); ++i) {
741 ClientDownloadRequest::Resource* resource = request.add_resources();
742 resource->set_url(SanitizeUrl(item_->GetUrlChain()[i]));
743 if (i == item_->GetUrlChain().size() - 1) {
744 // The last URL in the chain is the download URL.
745 resource->set_type(ClientDownloadRequest::DOWNLOAD_URL);
746 resource->set_referrer(SanitizeUrl(item_->GetReferrerUrl()));
747 DVLOG(2) << "dl url " << resource->url();
748 if (!item_->GetRemoteAddress().empty()) {
749 resource->set_remote_ip(item_->GetRemoteAddress());
750 DVLOG(2) << " dl url remote addr: " << resource->remote_ip();
752 DVLOG(2) << "dl referrer " << resource->referrer();
753 } else {
754 DVLOG(2) << "dl redirect " << i << " " << resource->url();
755 resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT);
757 // TODO(noelutz): fill out the remote IP addresses.
759 // TODO(mattm): fill out the remote IP addresses for tab resources.
760 for (size_t i = 0; i < tab_redirects_.size(); ++i) {
761 ClientDownloadRequest::Resource* resource = request.add_resources();
762 DVLOG(2) << "tab redirect " << i << " " << tab_redirects_[i].spec();
763 resource->set_url(SanitizeUrl(tab_redirects_[i]));
764 resource->set_type(ClientDownloadRequest::TAB_REDIRECT);
766 if (tab_url_.is_valid()) {
767 ClientDownloadRequest::Resource* resource = request.add_resources();
768 resource->set_url(SanitizeUrl(tab_url_));
769 DVLOG(2) << "tab url " << resource->url();
770 resource->set_type(ClientDownloadRequest::TAB_URL);
771 if (tab_referrer_url_.is_valid()) {
772 resource->set_referrer(SanitizeUrl(tab_referrer_url_));
773 DVLOG(2) << "tab referrer " << resource->referrer();
777 request.set_user_initiated(item_->HasUserGesture());
778 request.set_file_basename(
779 item_->GetTargetFilePath().BaseName().AsUTF8Unsafe());
780 request.set_download_type(type_);
781 request.mutable_signature()->CopyFrom(signature_info_);
782 if (image_headers_)
783 request.set_allocated_image_headers(image_headers_.release());
784 if (zipped_executable_)
785 request.mutable_archived_binary()->Swap(&archived_binary_);
786 if (!request.SerializeToString(&client_download_request_data_)) {
787 FinishRequest(UNKNOWN, REASON_INVALID_REQUEST_PROTO);
788 return;
790 service_->client_download_request_callbacks_.Notify(item_, &request);
792 DVLOG(2) << "Sending a request for URL: "
793 << item_->GetUrlChain().back();
794 fetcher_ = net::URLFetcher::Create(0 /* ID used for testing */,
795 GetDownloadRequestUrl(),
796 net::URLFetcher::POST, this);
797 fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
798 fetcher_->SetAutomaticallyRetryOn5xx(false); // Don't retry on error.
799 fetcher_->SetRequestContext(service_->request_context_getter_.get());
800 fetcher_->SetUploadData("application/octet-stream",
801 client_download_request_data_);
802 request_start_time_ = base::TimeTicks::Now();
803 UMA_HISTOGRAM_COUNTS("SBClientDownload.DownloadRequestPayloadSize",
804 client_download_request_data_.size());
805 fetcher_->Start();
808 void PostFinishTask(DownloadCheckResult result,
809 DownloadCheckResultReason reason) {
810 BrowserThread::PostTask(
811 BrowserThread::UI,
812 FROM_HERE,
813 base::Bind(&CheckClientDownloadRequest::FinishRequest, this, result,
814 reason));
817 void FinishRequest(DownloadCheckResult result,
818 DownloadCheckResultReason reason) {
819 DCHECK_CURRENTLY_ON(BrowserThread::UI);
820 if (finished_) {
821 return;
823 finished_ = true;
824 // Ensure the timeout task is cancelled while we still have a non-zero
825 // refcount. (crbug.com/240449)
826 weakptr_factory_.InvalidateWeakPtrs();
827 if (!request_start_time_.is_null()) {
828 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestNetworkStats",
829 reason,
830 REASON_MAX);
832 if (!timeout_start_time_.is_null()) {
833 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestTimeoutStats",
834 reason,
835 REASON_MAX);
836 if (reason != REASON_REQUEST_CANCELED) {
837 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestTimeoutDuration",
838 base::TimeTicks::Now() - timeout_start_time_);
841 if (service_) {
842 DVLOG(2) << "SafeBrowsing download verdict for: "
843 << item_->DebugString(true) << " verdict:" << reason
844 << " result:" << result;
845 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
846 reason,
847 REASON_MAX);
848 callback_.Run(result);
849 item_->RemoveObserver(this);
850 item_ = NULL;
851 DownloadProtectionService* service = service_;
852 service_ = NULL;
853 service->RequestFinished(this);
854 // DownloadProtectionService::RequestFinished will decrement our refcount,
855 // so we may be deleted now.
856 } else {
857 callback_.Run(UNKNOWN);
861 bool CertificateChainIsWhitelisted(
862 const ClientDownloadRequest_CertificateChain& chain) {
863 DCHECK_CURRENTLY_ON(BrowserThread::IO);
864 if (chain.element_size() < 2) {
865 // We need to have both a signing certificate and its issuer certificate
866 // present to construct a whitelist entry.
867 return false;
869 scoped_refptr<net::X509Certificate> cert =
870 net::X509Certificate::CreateFromBytes(
871 chain.element(0).certificate().data(),
872 chain.element(0).certificate().size());
873 if (!cert.get()) {
874 return false;
877 for (int i = 1; i < chain.element_size(); ++i) {
878 scoped_refptr<net::X509Certificate> issuer =
879 net::X509Certificate::CreateFromBytes(
880 chain.element(i).certificate().data(),
881 chain.element(i).certificate().size());
882 if (!issuer.get()) {
883 return false;
885 std::vector<std::string> whitelist_strings;
886 DownloadProtectionService::GetCertificateWhitelistStrings(
887 *cert.get(), *issuer.get(), &whitelist_strings);
888 for (size_t j = 0; j < whitelist_strings.size(); ++j) {
889 if (database_manager_->MatchDownloadWhitelistString(
890 whitelist_strings[j])) {
891 DVLOG(2) << "Certificate matched whitelist, cert="
892 << cert->subject().GetDisplayName()
893 << " issuer=" << issuer->subject().GetDisplayName();
894 return true;
897 cert = issuer;
899 return false;
902 // The DownloadItem we are checking. Will be NULL if the request has been
903 // canceled. Must be accessed only on UI thread.
904 content::DownloadItem* item_;
905 // Copies of data from |item_| for access on other threads.
906 std::vector<GURL> url_chain_;
907 GURL referrer_url_;
908 // URL chain of redirects leading to (but not including) |tab_url|.
909 std::vector<GURL> tab_redirects_;
910 // URL and referrer of the window the download was started from.
911 GURL tab_url_;
912 GURL tab_referrer_url_;
914 bool zipped_executable_;
915 ClientDownloadRequest_SignatureInfo signature_info_;
916 scoped_ptr<ClientDownloadRequest_ImageHeaders> image_headers_;
917 google::protobuf::RepeatedPtrField<ClientDownloadRequest_ArchivedBinary>
918 archived_binary_;
919 CheckDownloadCallback callback_;
920 // Will be NULL if the request has been canceled.
921 DownloadProtectionService* service_;
922 scoped_refptr<BinaryFeatureExtractor> binary_feature_extractor_;
923 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
924 const bool pingback_enabled_;
925 scoped_ptr<net::URLFetcher> fetcher_;
926 scoped_refptr<SandboxedZipAnalyzer> analyzer_;
927 base::TimeTicks zip_analysis_start_time_;
928 bool finished_;
929 ClientDownloadRequest::DownloadType type_;
930 std::string client_download_request_data_;
931 base::CancelableTaskTracker request_tracker_; // For HistoryService lookup.
932 base::TimeTicks start_time_; // Used for stats.
933 base::TimeTicks timeout_start_time_;
934 base::TimeTicks request_start_time_;
935 base::WeakPtrFactory<CheckClientDownloadRequest> weakptr_factory_;
937 DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest);
940 DownloadProtectionService::DownloadProtectionService(
941 SafeBrowsingService* sb_service,
942 net::URLRequestContextGetter* request_context_getter)
943 : request_context_getter_(request_context_getter),
944 enabled_(false),
945 binary_feature_extractor_(new BinaryFeatureExtractor()),
946 download_request_timeout_ms_(kDownloadRequestTimeoutMs),
947 feedback_service_(new DownloadFeedbackService(
948 request_context_getter, BrowserThread::GetBlockingPool())) {
950 if (sb_service) {
951 ui_manager_ = sb_service->ui_manager();
952 database_manager_ = sb_service->database_manager();
956 DownloadProtectionService::~DownloadProtectionService() {
957 DCHECK_CURRENTLY_ON(BrowserThread::UI);
958 CancelPendingRequests();
961 void DownloadProtectionService::SetEnabled(bool enabled) {
962 DCHECK_CURRENTLY_ON(BrowserThread::UI);
963 if (enabled == enabled_) {
964 return;
966 enabled_ = enabled;
967 if (!enabled_) {
968 CancelPendingRequests();
972 void DownloadProtectionService::CheckClientDownload(
973 content::DownloadItem* item,
974 const CheckDownloadCallback& callback) {
975 scoped_refptr<CheckClientDownloadRequest> request(
976 new CheckClientDownloadRequest(item, callback, this,
977 database_manager_,
978 binary_feature_extractor_.get()));
979 download_requests_.insert(request);
980 request->Start();
983 void DownloadProtectionService::CheckDownloadUrl(
984 const content::DownloadItem& item,
985 const CheckDownloadCallback& callback) {
986 DCHECK(!item.GetUrlChain().empty());
987 scoped_refptr<DownloadUrlSBClient> client(
988 new DownloadUrlSBClient(item, callback, ui_manager_, database_manager_));
989 // The client will release itself once it is done.
990 BrowserThread::PostTask(
991 BrowserThread::IO,
992 FROM_HERE,
993 base::Bind(&DownloadUrlSBClient::StartCheck, client));
996 bool DownloadProtectionService::IsSupportedDownload(
997 const content::DownloadItem& item,
998 const base::FilePath& target_path) const {
999 // Currently, the UI is only enabled on Windows and OSX. On Linux we still
1000 // want to show the dangerous file type warning if the file is possibly
1001 // dangerous which means we have to always return false here.
1002 #if defined(OS_WIN) || defined(OS_MACOSX)
1003 DownloadCheckResultReason reason = REASON_MAX;
1004 ClientDownloadRequest::DownloadType type =
1005 ClientDownloadRequest::WIN_EXECUTABLE;
1006 return (CheckClientDownloadRequest::IsSupportedDownload(
1007 item, target_path, &reason, &type) &&
1008 (ClientDownloadRequest::CHROME_EXTENSION != type));
1009 #else
1010 return false;
1011 #endif
1014 DownloadProtectionService::ClientDownloadRequestSubscription
1015 DownloadProtectionService::RegisterClientDownloadRequestCallback(
1016 const ClientDownloadRequestCallback& callback) {
1017 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1018 return client_download_request_callbacks_.Add(callback);
1021 void DownloadProtectionService::CancelPendingRequests() {
1022 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1023 for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
1024 download_requests_.begin();
1025 it != download_requests_.end();) {
1026 // We need to advance the iterator before we cancel because canceling
1027 // the request will invalidate it when RequestFinished is called below.
1028 scoped_refptr<CheckClientDownloadRequest> tmp = *it++;
1029 tmp->Cancel();
1031 DCHECK(download_requests_.empty());
1034 void DownloadProtectionService::RequestFinished(
1035 CheckClientDownloadRequest* request) {
1036 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1037 std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
1038 download_requests_.find(request);
1039 DCHECK(it != download_requests_.end());
1040 download_requests_.erase(*it);
1043 void DownloadProtectionService::ShowDetailsForDownload(
1044 const content::DownloadItem& item,
1045 content::PageNavigator* navigator) {
1046 GURL learn_more_url(chrome::kDownloadScanningLearnMoreURL);
1047 learn_more_url = google_util::AppendGoogleLocaleParam(
1048 learn_more_url, g_browser_process->GetApplicationLocale());
1049 learn_more_url = net::AppendQueryParameter(
1050 learn_more_url, "ctx",
1051 base::IntToString(static_cast<int>(item.GetDangerType())));
1052 navigator->OpenURL(
1053 content::OpenURLParams(learn_more_url,
1054 content::Referrer(),
1055 NEW_FOREGROUND_TAB,
1056 ui::PAGE_TRANSITION_LINK,
1057 false));
1060 namespace {
1061 // Escapes a certificate attribute so that it can be used in a whitelist
1062 // entry. Currently, we only escape slashes, since they are used as a
1063 // separator between attributes.
1064 std::string EscapeCertAttribute(const std::string& attribute) {
1065 std::string escaped;
1066 for (size_t i = 0; i < attribute.size(); ++i) {
1067 if (attribute[i] == '%') {
1068 escaped.append("%25");
1069 } else if (attribute[i] == '/') {
1070 escaped.append("%2F");
1071 } else {
1072 escaped.push_back(attribute[i]);
1075 return escaped;
1077 } // namespace
1079 // static
1080 void DownloadProtectionService::GetCertificateWhitelistStrings(
1081 const net::X509Certificate& certificate,
1082 const net::X509Certificate& issuer,
1083 std::vector<std::string>* whitelist_strings) {
1084 // The whitelist paths are in the format:
1085 // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit]
1087 // Any of CN, O, or OU may be omitted from the whitelist entry, in which
1088 // case they match anything. However, the attributes that do appear will
1089 // always be in the order shown above. At least one attribute will always
1090 // be present.
1092 const net::CertPrincipal& subject = certificate.subject();
1093 std::vector<std::string> ou_tokens;
1094 for (size_t i = 0; i < subject.organization_unit_names.size(); ++i) {
1095 ou_tokens.push_back(
1096 "/OU=" + EscapeCertAttribute(subject.organization_unit_names[i]));
1099 std::vector<std::string> o_tokens;
1100 for (size_t i = 0; i < subject.organization_names.size(); ++i) {
1101 o_tokens.push_back(
1102 "/O=" + EscapeCertAttribute(subject.organization_names[i]));
1105 std::string cn_token;
1106 if (!subject.common_name.empty()) {
1107 cn_token = "/CN=" + EscapeCertAttribute(subject.common_name);
1110 std::set<std::string> paths_to_check;
1111 if (!cn_token.empty()) {
1112 paths_to_check.insert(cn_token);
1114 for (size_t i = 0; i < o_tokens.size(); ++i) {
1115 paths_to_check.insert(cn_token + o_tokens[i]);
1116 paths_to_check.insert(o_tokens[i]);
1117 for (size_t j = 0; j < ou_tokens.size(); ++j) {
1118 paths_to_check.insert(cn_token + o_tokens[i] + ou_tokens[j]);
1119 paths_to_check.insert(o_tokens[i] + ou_tokens[j]);
1122 for (size_t i = 0; i < ou_tokens.size(); ++i) {
1123 paths_to_check.insert(cn_token + ou_tokens[i]);
1124 paths_to_check.insert(ou_tokens[i]);
1127 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data,
1128 sizeof(issuer.fingerprint().data));
1129 for (std::set<std::string>::iterator it = paths_to_check.begin();
1130 it != paths_to_check.end(); ++it) {
1131 whitelist_strings->push_back("cert/" + issuer_fp + *it);
1135 // static
1136 GURL DownloadProtectionService::GetDownloadRequestUrl() {
1137 GURL url(kDownloadRequestUrl);
1138 std::string api_key = google_apis::GetAPIKey();
1139 if (!api_key.empty())
1140 url = url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
1142 return url;
1145 } // namespace safe_browsing