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"
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/prefs/pref_service.h"
16 #include "base/sequenced_task_runner_helpers.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/task/cancelable_task_tracker.h"
22 #include "base/threading/sequenced_worker_pool.h"
23 #include "base/time/time.h"
24 #include "chrome/browser/browser_process.h"
25 #include "chrome/browser/history/history_service_factory.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/profiles/profile_manager.h"
28 #include "chrome/browser/safe_browsing/download_feedback_service.h"
29 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
30 #include "chrome/browser/safe_browsing/sandboxed_zip_analyzer.h"
31 #include "chrome/browser/ui/browser.h"
32 #include "chrome/browser/ui/browser_list.h"
33 #include "chrome/common/pref_names.h"
34 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
35 #include "chrome/common/safe_browsing/csd.pb.h"
36 #include "chrome/common/safe_browsing/download_protection_util.h"
37 #include "chrome/common/safe_browsing/zip_analyzer_results.h"
38 #include "chrome/common/url_constants.h"
39 #include "components/google/core/browser/google_util.h"
40 #include "components/history/core/browser/history_service.h"
41 #include "content/public/browser/browser_thread.h"
42 #include "content/public/browser/download_item.h"
43 #include "content/public/browser/page_navigator.h"
44 #include "crypto/sha2.h"
45 #include "google_apis/google_api_keys.h"
46 #include "net/base/escape.h"
47 #include "net/base/load_flags.h"
48 #include "net/base/url_util.h"
49 #include "net/cert/x509_cert_types.h"
50 #include "net/cert/x509_certificate.h"
51 #include "net/http/http_status_code.h"
52 #include "net/url_request/url_fetcher.h"
53 #include "net/url_request/url_fetcher_delegate.h"
54 #include "net/url_request/url_request_context_getter.h"
55 #include "net/url_request/url_request_status.h"
57 #if defined(OS_MACOSX)
58 #include "chrome/browser/safe_browsing/sandboxed_dmg_analyzer_mac.h"
61 using content::BrowserThread
;
64 static const int64 kDownloadRequestTimeoutMs
= 7000;
67 namespace safe_browsing
{
69 const char DownloadProtectionService::kDownloadRequestUrl
[] =
70 "https://sb-ssl.google.com/safebrowsing/clientreport/download";
73 void RecordFileExtensionType(const base::FilePath
& file
) {
74 UMA_HISTOGRAM_ENUMERATION(
75 "SBClientDownload.DownloadExtensions",
76 download_protection_util::GetSBClientDownloadExtensionValueForUMA(file
),
77 download_protection_util::kSBClientDownloadExtensionsMax
);
80 void RecordArchivedArchiveFileExtensionType(
81 const base::FilePath::StringType
& extension
) {
82 UMA_HISTOGRAM_ENUMERATION(
83 "SBClientDownload.ArchivedArchiveExtensions",
84 download_protection_util::GetSBClientDownloadExtensionValueForUMA(
85 base::FilePath(extension
)),
86 download_protection_util::kSBClientDownloadExtensionsMax
);
89 // Enumerate for histogramming purposes.
90 // DO NOT CHANGE THE ORDERING OF THESE VALUES (different histogram data will
91 // be mixed together based on their values).
93 DOWNLOAD_URL_CHECKS_TOTAL
,
94 DOWNLOAD_URL_CHECKS_CANCELED
,
95 DOWNLOAD_URL_CHECKS_MALWARE
,
97 DOWNLOAD_HASH_CHECKS_TOTAL
,
98 DOWNLOAD_HASH_CHECKS_MALWARE
,
100 // Memory space for histograms is determined by the max.
101 // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
105 // Prepares URLs to be put into a ping message. Currently this just shortens
106 // data: URIs, other URLs are included verbatim.
107 std::string
SanitizeUrl(const GURL
& url
) {
108 std::string spec
= url
.spec();
109 if (url
.SchemeIs(url::kDataScheme
)) {
110 size_t comma_pos
= spec
.find(',');
111 if (comma_pos
!= std::string::npos
&& comma_pos
!= spec
.size() - 1) {
112 std::string hash_value
= crypto::SHA256HashString(spec
);
113 spec
.erase(comma_pos
+ 1);
114 spec
+= base::HexEncode(hash_value
.data(), hash_value
.size());
122 // Parent SafeBrowsing::Client class used to lookup the bad binary
123 // URL and digest list. There are two sub-classes (one for each list).
124 class DownloadSBClient
125 : public SafeBrowsingDatabaseManager::Client
,
126 public base::RefCountedThreadSafe
<DownloadSBClient
> {
129 const content::DownloadItem
& item
,
130 const DownloadProtectionService::CheckDownloadCallback
& callback
,
131 const scoped_refptr
<SafeBrowsingUIManager
>& ui_manager
,
132 SBStatsType total_type
,
133 SBStatsType dangerous_type
)
134 : sha256_hash_(item
.GetHash()),
135 url_chain_(item
.GetUrlChain()),
136 referrer_url_(item
.GetReferrerUrl()),
138 ui_manager_(ui_manager
),
139 start_time_(base::TimeTicks::Now()),
140 total_type_(total_type
),
141 dangerous_type_(dangerous_type
) {
142 Profile
* profile
= Profile::FromBrowserContext(item
.GetBrowserContext());
143 is_extended_reporting_
= profile
&&
144 profile
->GetPrefs()->GetBoolean(
145 prefs::kSafeBrowsingExtendedReportingEnabled
);
148 virtual void StartCheck() = 0;
149 virtual bool IsDangerous(SBThreatType threat_type
) const = 0;
152 friend class base::RefCountedThreadSafe
<DownloadSBClient
>;
153 ~DownloadSBClient() override
{}
155 void CheckDone(SBThreatType threat_type
) {
156 DownloadProtectionService::DownloadCheckResult result
=
157 IsDangerous(threat_type
) ?
158 DownloadProtectionService::DANGEROUS
:
159 DownloadProtectionService::SAFE
;
160 BrowserThread::PostTask(BrowserThread::UI
,
162 base::Bind(callback_
, result
));
163 UpdateDownloadCheckStats(total_type_
);
164 if (threat_type
!= SB_THREAT_TYPE_SAFE
) {
165 UpdateDownloadCheckStats(dangerous_type_
);
166 BrowserThread::PostTask(
169 base::Bind(&DownloadSBClient::ReportMalware
,
174 void ReportMalware(SBThreatType threat_type
) {
175 std::string post_data
;
176 if (!sha256_hash_
.empty())
177 post_data
+= base::HexEncode(sha256_hash_
.data(),
178 sha256_hash_
.size()) + "\n";
179 for (size_t i
= 0; i
< url_chain_
.size(); ++i
) {
180 post_data
+= url_chain_
[i
].spec() + "\n";
182 ui_manager_
->ReportSafeBrowsingHit(url_chain_
.back(), // malicious_url
183 url_chain_
.front(), // page_url
185 true, // is_subresource
188 is_extended_reporting_
);
191 void UpdateDownloadCheckStats(SBStatsType stat_type
) {
192 UMA_HISTOGRAM_ENUMERATION("SB2.DownloadChecks",
194 DOWNLOAD_CHECKS_MAX
);
197 std::string sha256_hash_
;
198 std::vector
<GURL
> url_chain_
;
200 DownloadProtectionService::CheckDownloadCallback callback_
;
201 scoped_refptr
<SafeBrowsingUIManager
> ui_manager_
;
202 base::TimeTicks start_time_
;
205 const SBStatsType total_type_
;
206 const SBStatsType dangerous_type_
;
207 bool is_extended_reporting_
;
209 DISALLOW_COPY_AND_ASSIGN(DownloadSBClient
);
212 class DownloadUrlSBClient
: public DownloadSBClient
{
215 const content::DownloadItem
& item
,
216 const DownloadProtectionService::CheckDownloadCallback
& callback
,
217 const scoped_refptr
<SafeBrowsingUIManager
>& ui_manager
,
218 const scoped_refptr
<SafeBrowsingDatabaseManager
>& database_manager
)
219 : DownloadSBClient(item
, callback
, ui_manager
,
220 DOWNLOAD_URL_CHECKS_TOTAL
,
221 DOWNLOAD_URL_CHECKS_MALWARE
),
222 database_manager_(database_manager
) { }
224 void StartCheck() override
{
225 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
226 if (!database_manager_
.get() ||
227 database_manager_
->CheckDownloadUrl(url_chain_
, this)) {
228 CheckDone(SB_THREAT_TYPE_SAFE
);
230 AddRef(); // SafeBrowsingService takes a pointer not a scoped_refptr.
234 bool IsDangerous(SBThreatType threat_type
) const override
{
235 return threat_type
== SB_THREAT_TYPE_BINARY_MALWARE_URL
;
238 void OnCheckDownloadUrlResult(const std::vector
<GURL
>& url_chain
,
239 SBThreatType threat_type
) override
{
240 CheckDone(threat_type
);
241 UMA_HISTOGRAM_TIMES("SB2.DownloadUrlCheckDuration",
242 base::TimeTicks::Now() - start_time_
);
247 ~DownloadUrlSBClient() override
{}
250 scoped_refptr
<SafeBrowsingDatabaseManager
> database_manager_
;
252 DISALLOW_COPY_AND_ASSIGN(DownloadUrlSBClient
);
255 class DownloadProtectionService::CheckClientDownloadRequest
256 : public base::RefCountedThreadSafe
<
257 DownloadProtectionService::CheckClientDownloadRequest
,
258 BrowserThread::DeleteOnUIThread
>,
259 public net::URLFetcherDelegate
,
260 public content::DownloadItem::Observer
{
262 CheckClientDownloadRequest(
263 content::DownloadItem
* item
,
264 const CheckDownloadCallback
& callback
,
265 DownloadProtectionService
* service
,
266 const scoped_refptr
<SafeBrowsingDatabaseManager
>& database_manager
,
267 BinaryFeatureExtractor
* binary_feature_extractor
)
269 url_chain_(item
->GetUrlChain()),
270 referrer_url_(item
->GetReferrerUrl()),
271 tab_url_(item
->GetTabUrl()),
272 tab_referrer_url_(item
->GetTabReferrerUrl()),
273 archived_executable_(false),
276 binary_feature_extractor_(binary_feature_extractor
),
277 database_manager_(database_manager
),
278 pingback_enabled_(service_
->enabled()),
280 type_(ClientDownloadRequest::WIN_EXECUTABLE
),
281 start_time_(base::TimeTicks::Now()),
282 weakptr_factory_(this) {
283 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
284 item_
->AddObserver(this);
288 DVLOG(2) << "Starting SafeBrowsing download check for: "
289 << item_
->DebugString(true);
290 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
291 // TODO(noelutz): implement some cache to make sure we don't issue the same
292 // request over and over again if a user downloads the same binary multiple
294 DownloadCheckResultReason reason
= REASON_MAX
;
295 if (!IsSupportedDownload(
296 *item_
, item_
->GetTargetFilePath(), &reason
, &type_
)) {
298 case REASON_EMPTY_URL_CHAIN
:
299 case REASON_INVALID_URL
:
300 case REASON_UNSUPPORTED_URL_SCHEME
:
301 PostFinishTask(UNKNOWN
, reason
);
304 case REASON_NOT_BINARY_FILE
:
305 RecordFileExtensionType(item_
->GetTargetFilePath());
306 PostFinishTask(UNKNOWN
, reason
);
310 // We only expect the reasons explicitly handled above.
314 RecordFileExtensionType(item_
->GetTargetFilePath());
316 // Compute features from the file contents. Note that we record histograms
317 // based on the result, so this runs regardless of whether the pingbacks
319 if (item_
->GetTargetFilePath().MatchesExtension(
320 FILE_PATH_LITERAL(".zip"))) {
321 StartExtractZipFeatures();
322 #if defined(OS_MACOSX)
323 } else if (item_
->GetTargetFilePath().MatchesExtension(
324 FILE_PATH_LITERAL(".dmg"))) {
325 StartExtractDmgFeatures();
328 StartExtractFileFeatures();
332 // Start a timeout to cancel the request if it takes too long.
333 // This should only be called after we have finished accessing the file.
334 void StartTimeout() {
335 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
337 // Request has already been cancelled.
340 timeout_start_time_
= base::TimeTicks::Now();
341 BrowserThread::PostDelayedTask(
344 base::Bind(&CheckClientDownloadRequest::Cancel
,
345 weakptr_factory_
.GetWeakPtr()),
346 base::TimeDelta::FromMilliseconds(
347 service_
->download_request_timeout_ms()));
350 // Canceling a request will cause us to always report the result as UNKNOWN
351 // unless a pending request is about to call FinishRequest.
353 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
354 if (fetcher_
.get()) {
355 // The DownloadProtectionService is going to release its reference, so we
356 // might be destroyed before the URLFetcher completes. Cancel the
357 // fetcher so it does not try to invoke OnURLFetchComplete.
360 // Note: If there is no fetcher, then some callback is still holding a
361 // reference to this object. We'll eventually wind up in some method on
362 // the UI thread that will call FinishRequest() again. If FinishRequest()
363 // is called a second time, it will be a no-op.
364 FinishRequest(UNKNOWN
, REASON_REQUEST_CANCELED
);
365 // Calling FinishRequest might delete this object, we may be deleted by
369 // content::DownloadItem::Observer implementation.
370 void OnDownloadDestroyed(content::DownloadItem
* download
) override
{
372 DCHECK(item_
== NULL
);
375 // From the net::URLFetcherDelegate interface.
376 void OnURLFetchComplete(const net::URLFetcher
* source
) override
{
377 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
378 DCHECK_EQ(source
, fetcher_
.get());
379 DVLOG(2) << "Received a response for URL: "
380 << item_
->GetUrlChain().back() << ": success="
381 << source
->GetStatus().is_success() << " response_code="
382 << source
->GetResponseCode();
383 if (source
->GetStatus().is_success()) {
384 UMA_HISTOGRAM_SPARSE_SLOWLY(
385 "SBClientDownload.DownloadRequestResponseCode",
386 source
->GetResponseCode());
388 UMA_HISTOGRAM_SPARSE_SLOWLY(
389 "SBClientDownload.DownloadRequestNetError",
390 -source
->GetStatus().error());
391 DownloadCheckResultReason reason
= REASON_SERVER_PING_FAILED
;
392 DownloadCheckResult result
= UNKNOWN
;
393 if (source
->GetStatus().is_success() &&
394 net::HTTP_OK
== source
->GetResponseCode()) {
395 ClientDownloadResponse response
;
397 bool got_data
= source
->GetResponseAsString(&data
);
399 if (!response
.ParseFromString(data
)) {
400 reason
= REASON_INVALID_RESPONSE_PROTO
;
402 } else if (response
.verdict() == ClientDownloadResponse::SAFE
) {
403 reason
= REASON_DOWNLOAD_SAFE
;
405 } else if (service_
&& !service_
->IsSupportedDownload(
406 *item_
, item_
->GetTargetFilePath())) {
407 // The client of the download protection service assumes that we don't
408 // support this download so we cannot return any other verdict than
409 // UNKNOWN even if the server says it's dangerous to download this file.
410 // Note: if service_ is NULL we already cancelled the request and
412 reason
= REASON_DOWNLOAD_NOT_SUPPORTED
;
414 } else if (response
.verdict() == ClientDownloadResponse::DANGEROUS
) {
415 reason
= REASON_DOWNLOAD_DANGEROUS
;
417 } else if (response
.verdict() == ClientDownloadResponse::UNCOMMON
) {
418 reason
= REASON_DOWNLOAD_UNCOMMON
;
420 } else if (response
.verdict() == ClientDownloadResponse::DANGEROUS_HOST
) {
421 reason
= REASON_DOWNLOAD_DANGEROUS_HOST
;
422 result
= DANGEROUS_HOST
;
424 response
.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED
) {
425 reason
= REASON_DOWNLOAD_POTENTIALLY_UNWANTED
;
426 result
= POTENTIALLY_UNWANTED
;
428 LOG(DFATAL
) << "Unknown download response verdict: "
429 << response
.verdict();
430 reason
= REASON_INVALID_RESPONSE_VERDICT
;
433 DownloadFeedbackService::MaybeStorePingsForDownload(
434 result
, item_
, client_download_request_data_
, data
);
436 // We don't need the fetcher anymore.
438 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration",
439 base::TimeTicks::Now() - start_time_
);
440 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestNetworkDuration",
441 base::TimeTicks::Now() - request_start_time_
);
442 FinishRequest(result
, reason
);
445 static bool IsSupportedDownload(const content::DownloadItem
& item
,
446 const base::FilePath
& target_path
,
447 DownloadCheckResultReason
* reason
,
448 ClientDownloadRequest::DownloadType
* type
) {
449 if (item
.GetUrlChain().empty()) {
450 *reason
= REASON_EMPTY_URL_CHAIN
;
453 const GURL
& final_url
= item
.GetUrlChain().back();
454 if (!final_url
.is_valid() || final_url
.is_empty()) {
455 *reason
= REASON_INVALID_URL
;
458 if (!download_protection_util::IsSupportedBinaryFile(target_path
)) {
459 *reason
= REASON_NOT_BINARY_FILE
;
462 if ((!final_url
.IsStandard() && !final_url
.SchemeIsBlob() &&
463 !final_url
.SchemeIs(url::kDataScheme
)) ||
464 final_url
.SchemeIsFile()) {
465 *reason
= REASON_UNSUPPORTED_URL_SCHEME
;
468 *type
= download_protection_util::GetDownloadType(target_path
);
473 friend struct BrowserThread::DeleteOnThread
<BrowserThread::UI
>;
474 friend class base::DeleteHelper
<CheckClientDownloadRequest
>;
476 ~CheckClientDownloadRequest() override
{
477 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
478 DCHECK(item_
== NULL
);
481 void OnFileFeatureExtractionDone() {
482 // This can run in any thread, since it just posts more messages.
484 // TODO(noelutz): DownloadInfo should also contain the IP address of
485 // every URL in the redirect chain. We also should check whether the
486 // download URL is hosted on the internal network.
487 BrowserThread::PostTask(
490 base::Bind(&CheckClientDownloadRequest::CheckWhitelists
, this));
492 // We wait until after the file checks finish to start the timeout, as
493 // windows can cause permissions errors if the timeout fired while we were
494 // checking the file signature and we tried to complete the download.
495 BrowserThread::PostTask(
498 base::Bind(&CheckClientDownloadRequest::StartTimeout
, this));
501 void StartExtractFileFeatures() {
502 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
503 DCHECK(item_
); // Called directly from Start(), item should still exist.
504 // Since we do blocking I/O, offload this to a worker thread.
505 // The task does not need to block shutdown.
506 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
508 base::Bind(&CheckClientDownloadRequest::ExtractFileFeatures
,
509 this, item_
->GetFullPath()),
510 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
513 void ExtractFileFeatures(const base::FilePath
& file_path
) {
514 base::TimeTicks start_time
= base::TimeTicks::Now();
515 binary_feature_extractor_
->CheckSignature(file_path
, &signature_info_
);
516 bool is_signed
= (signature_info_
.certificate_chain_size() > 0);
518 DVLOG(2) << "Downloaded a signed binary: " << file_path
.value();
520 DVLOG(2) << "Downloaded an unsigned binary: "
521 << file_path
.value();
523 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed
);
524 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime",
525 base::TimeTicks::Now() - start_time
);
527 start_time
= base::TimeTicks::Now();
528 image_headers_
.reset(new ClientDownloadRequest_ImageHeaders());
529 if (!binary_feature_extractor_
->ExtractImageFeatures(
531 BinaryFeatureExtractor::kDefaultOptions
,
532 image_headers_
.get(),
533 nullptr /* signed_data */)) {
534 image_headers_
.reset();
536 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractImageHeadersTime",
537 base::TimeTicks::Now() - start_time
);
539 OnFileFeatureExtractionDone();
542 void StartExtractZipFeatures() {
543 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
544 DCHECK(item_
); // Called directly from Start(), item should still exist.
545 zip_analysis_start_time_
= base::TimeTicks::Now();
546 // We give the zip analyzer a weak pointer to this object. Since the
547 // analyzer is refcounted, it might outlive the request.
548 analyzer_
= new SandboxedZipAnalyzer(
549 item_
->GetFullPath(),
550 base::Bind(&CheckClientDownloadRequest::OnZipAnalysisFinished
,
551 weakptr_factory_
.GetWeakPtr()));
555 void OnZipAnalysisFinished(const zip_analyzer::Results
& results
) {
556 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
557 DCHECK_EQ(ClientDownloadRequest::ZIPPED_EXECUTABLE
, type_
);
560 if (results
.success
) {
561 archived_executable_
= results
.has_executable
;
562 archived_binary_
.CopyFrom(results
.archived_binary
);
563 DVLOG(1) << "Zip analysis finished for " << item_
->GetFullPath().value()
564 << ", has_executable=" << results
.has_executable
565 << " has_archive=" << results
.has_archive
;
567 DVLOG(1) << "Zip analysis failed for " << item_
->GetFullPath().value();
569 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
570 archived_executable_
);
571 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable",
572 results
.has_archive
&& !archived_executable_
);
573 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime",
574 base::TimeTicks::Now() - zip_analysis_start_time_
);
575 for (const auto& file_extension
: results
.archived_archive_filetypes
)
576 RecordArchivedArchiveFileExtensionType(file_extension
);
578 if (!archived_executable_
&& !results
.has_archive
) {
579 PostFinishTask(UNKNOWN
, REASON_ARCHIVE_WITHOUT_BINARIES
);
583 if (!archived_executable_
&& results
.has_archive
)
584 type_
= ClientDownloadRequest::ZIPPED_ARCHIVE
;
585 OnFileFeatureExtractionDone();
588 #if defined(OS_MACOSX)
589 void StartExtractDmgFeatures() {
590 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
592 dmg_analyzer_
= new SandboxedDMGAnalyzer(
593 item_
->GetFullPath(),
594 base::Bind(&CheckClientDownloadRequest::OnDmgAnalysisFinished
,
595 weakptr_factory_
.GetWeakPtr()));
596 dmg_analyzer_
->Start();
597 dmg_analysis_start_time_
= base::TimeTicks::Now();
600 void OnDmgAnalysisFinished(const zip_analyzer::Results
& results
) {
601 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
602 DCHECK_EQ(ClientDownloadRequest::MAC_EXECUTABLE
, type_
);
606 if (results
.success
) {
607 archived_executable_
= results
.has_executable
;
608 archived_binary_
.CopyFrom(results
.archived_binary
);
609 DVLOG(1) << "DMG analysis has finished for "
610 << item_
->GetFullPath().value() << ", has_executable="
611 << results
.has_executable
;
613 DVLOG(1) << "DMG analysis failed for "<< item_
->GetFullPath().value();
616 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.DmgFileSuccess", results
.success
);
617 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.DmgFileHasExecutable",
618 archived_executable_
);
619 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractDmgFeaturesTime",
620 base::TimeTicks::Now() - dmg_analysis_start_time_
);
622 if (!archived_executable_
) {
623 PostFinishTask(UNKNOWN
, REASON_ARCHIVE_WITHOUT_BINARIES
);
627 OnFileFeatureExtractionDone();
629 #endif // defined(OS_MACOSX)
631 static void RecordCountOfSignedOrWhitelistedDownload() {
632 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
635 void CheckWhitelists() {
636 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
638 if (!database_manager_
.get()) {
639 PostFinishTask(UNKNOWN
, REASON_SB_DISABLED
);
643 const GURL
& url
= url_chain_
.back();
644 // TODO(asanka): This may acquire a lock on the SB DB on the IO thread.
645 if (url
.is_valid() && database_manager_
->MatchDownloadWhitelistUrl(url
)) {
646 DVLOG(2) << url
<< " is on the download whitelist.";
647 RecordCountOfSignedOrWhitelistedDownload();
648 // TODO(grt): Continue processing without uploading so that
649 // ClientDownloadRequest callbacks can be run even for this type of safe
651 PostFinishTask(SAFE
, REASON_WHITELISTED_URL
);
655 if (signature_info_
.trusted()) {
656 RecordCountOfSignedOrWhitelistedDownload();
657 for (int i
= 0; i
< signature_info_
.certificate_chain_size(); ++i
) {
658 if (CertificateChainIsWhitelisted(
659 signature_info_
.certificate_chain(i
))) {
660 // TODO(grt): Continue processing without uploading so that
661 // ClientDownloadRequest callbacks can be run even for this type of
663 PostFinishTask(SAFE
, REASON_TRUSTED_EXECUTABLE
);
669 if (!pingback_enabled_
) {
670 PostFinishTask(UNKNOWN
, REASON_PING_DISABLED
);
674 // Currently, the UI is only enabled on Windows and OSX so we don't even
675 // bother with pinging the server if we're not on one of those platforms.
676 // TODO(noelutz): change this code once the UI is done for Linux.
677 #if defined(OS_WIN) || defined(OS_MACOSX)
678 // The URLFetcher is owned by the UI thread, so post a message to
679 // start the pingback.
680 BrowserThread::PostTask(
683 base::Bind(&CheckClientDownloadRequest::GetTabRedirects
, this));
685 PostFinishTask(UNKNOWN
, REASON_OS_NOT_SUPPORTED
);
689 void GetTabRedirects() {
690 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
694 if (!tab_url_
.is_valid()) {
699 Profile
* profile
= Profile::FromBrowserContext(item_
->GetBrowserContext());
700 history::HistoryService
* history
= HistoryServiceFactory::GetForProfile(
701 profile
, ServiceAccessType::EXPLICIT_ACCESS
);
707 history
->QueryRedirectsTo(
709 base::Bind(&CheckClientDownloadRequest::OnGotTabRedirects
,
710 base::Unretained(this),
715 void OnGotTabRedirects(const GURL
& url
,
716 const history::RedirectList
* redirect_list
) {
717 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
718 DCHECK_EQ(url
, tab_url_
);
722 if (!redirect_list
->empty()) {
723 tab_redirects_
.insert(
724 tab_redirects_
.end(), redirect_list
->rbegin(), redirect_list
->rend());
731 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
733 // This is our last chance to check whether the request has been canceled
734 // before sending it.
737 bool is_extended_reporting
= false;
738 if (item_
->GetBrowserContext()) {
740 Profile::FromBrowserContext(item_
->GetBrowserContext());
741 is_extended_reporting
= profile
&&
742 profile
->GetPrefs()->GetBoolean(
743 prefs::kSafeBrowsingExtendedReportingEnabled
);
746 ClientDownloadRequest request
;
747 if (is_extended_reporting
) {
748 request
.mutable_population()->set_user_population(
749 ChromeUserPopulation::EXTENDED_REPORTING
);
751 request
.mutable_population()->set_user_population(
752 ChromeUserPopulation::SAFE_BROWSING
);
754 request
.set_url(SanitizeUrl(item_
->GetUrlChain().back()));
755 request
.mutable_digests()->set_sha256(item_
->GetHash());
756 request
.set_length(item_
->GetReceivedBytes());
757 for (size_t i
= 0; i
< item_
->GetUrlChain().size(); ++i
) {
758 ClientDownloadRequest::Resource
* resource
= request
.add_resources();
759 resource
->set_url(SanitizeUrl(item_
->GetUrlChain()[i
]));
760 if (i
== item_
->GetUrlChain().size() - 1) {
761 // The last URL in the chain is the download URL.
762 resource
->set_type(ClientDownloadRequest::DOWNLOAD_URL
);
763 resource
->set_referrer(SanitizeUrl(item_
->GetReferrerUrl()));
764 DVLOG(2) << "dl url " << resource
->url();
765 if (!item_
->GetRemoteAddress().empty()) {
766 resource
->set_remote_ip(item_
->GetRemoteAddress());
767 DVLOG(2) << " dl url remote addr: " << resource
->remote_ip();
769 DVLOG(2) << "dl referrer " << resource
->referrer();
771 DVLOG(2) << "dl redirect " << i
<< " " << resource
->url();
772 resource
->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT
);
774 // TODO(noelutz): fill out the remote IP addresses.
776 // TODO(mattm): fill out the remote IP addresses for tab resources.
777 for (size_t i
= 0; i
< tab_redirects_
.size(); ++i
) {
778 ClientDownloadRequest::Resource
* resource
= request
.add_resources();
779 DVLOG(2) << "tab redirect " << i
<< " " << tab_redirects_
[i
].spec();
780 resource
->set_url(SanitizeUrl(tab_redirects_
[i
]));
781 resource
->set_type(ClientDownloadRequest::TAB_REDIRECT
);
783 if (tab_url_
.is_valid()) {
784 ClientDownloadRequest::Resource
* resource
= request
.add_resources();
785 resource
->set_url(SanitizeUrl(tab_url_
));
786 DVLOG(2) << "tab url " << resource
->url();
787 resource
->set_type(ClientDownloadRequest::TAB_URL
);
788 if (tab_referrer_url_
.is_valid()) {
789 resource
->set_referrer(SanitizeUrl(tab_referrer_url_
));
790 DVLOG(2) << "tab referrer " << resource
->referrer();
794 request
.set_user_initiated(item_
->HasUserGesture());
795 request
.set_file_basename(
796 item_
->GetTargetFilePath().BaseName().AsUTF8Unsafe());
797 request
.set_download_type(type_
);
798 request
.mutable_signature()->CopyFrom(signature_info_
);
800 request
.set_allocated_image_headers(image_headers_
.release());
801 if (archived_executable_
)
802 request
.mutable_archived_binary()->Swap(&archived_binary_
);
803 if (!request
.SerializeToString(&client_download_request_data_
)) {
804 FinishRequest(UNKNOWN
, REASON_INVALID_REQUEST_PROTO
);
807 service_
->client_download_request_callbacks_
.Notify(item_
, &request
);
809 DVLOG(2) << "Sending a request for URL: "
810 << item_
->GetUrlChain().back();
811 DVLOG(2) << "Detected " << request
.archived_binary().size() << " archived "
813 fetcher_
= net::URLFetcher::Create(0 /* ID used for testing */,
814 GetDownloadRequestUrl(),
815 net::URLFetcher::POST
, this);
816 fetcher_
->SetLoadFlags(net::LOAD_DISABLE_CACHE
);
817 fetcher_
->SetAutomaticallyRetryOn5xx(false); // Don't retry on error.
818 fetcher_
->SetRequestContext(service_
->request_context_getter_
.get());
819 fetcher_
->SetUploadData("application/octet-stream",
820 client_download_request_data_
);
821 request_start_time_
= base::TimeTicks::Now();
822 UMA_HISTOGRAM_COUNTS("SBClientDownload.DownloadRequestPayloadSize",
823 client_download_request_data_
.size());
827 void PostFinishTask(DownloadCheckResult result
,
828 DownloadCheckResultReason reason
) {
829 BrowserThread::PostTask(
832 base::Bind(&CheckClientDownloadRequest::FinishRequest
, this, result
,
836 void FinishRequest(DownloadCheckResult result
,
837 DownloadCheckResultReason reason
) {
838 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
843 // Ensure the timeout task is cancelled while we still have a non-zero
844 // refcount. (crbug.com/240449)
845 weakptr_factory_
.InvalidateWeakPtrs();
846 if (!request_start_time_
.is_null()) {
847 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestNetworkStats",
851 if (!timeout_start_time_
.is_null()) {
852 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestTimeoutStats",
855 if (reason
!= REASON_REQUEST_CANCELED
) {
856 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestTimeoutDuration",
857 base::TimeTicks::Now() - timeout_start_time_
);
861 DVLOG(2) << "SafeBrowsing download verdict for: "
862 << item_
->DebugString(true) << " verdict:" << reason
863 << " result:" << result
;
864 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
867 callback_
.Run(result
);
868 item_
->RemoveObserver(this);
870 DownloadProtectionService
* service
= service_
;
872 service
->RequestFinished(this);
873 // DownloadProtectionService::RequestFinished will decrement our refcount,
874 // so we may be deleted now.
876 callback_
.Run(UNKNOWN
);
880 bool CertificateChainIsWhitelisted(
881 const ClientDownloadRequest_CertificateChain
& chain
) {
882 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
883 if (chain
.element_size() < 2) {
884 // We need to have both a signing certificate and its issuer certificate
885 // present to construct a whitelist entry.
888 scoped_refptr
<net::X509Certificate
> cert
=
889 net::X509Certificate::CreateFromBytes(
890 chain
.element(0).certificate().data(),
891 chain
.element(0).certificate().size());
896 for (int i
= 1; i
< chain
.element_size(); ++i
) {
897 scoped_refptr
<net::X509Certificate
> issuer
=
898 net::X509Certificate::CreateFromBytes(
899 chain
.element(i
).certificate().data(),
900 chain
.element(i
).certificate().size());
904 std::vector
<std::string
> whitelist_strings
;
905 DownloadProtectionService::GetCertificateWhitelistStrings(
906 *cert
.get(), *issuer
.get(), &whitelist_strings
);
907 for (size_t j
= 0; j
< whitelist_strings
.size(); ++j
) {
908 if (database_manager_
->MatchDownloadWhitelistString(
909 whitelist_strings
[j
])) {
910 DVLOG(2) << "Certificate matched whitelist, cert="
911 << cert
->subject().GetDisplayName()
912 << " issuer=" << issuer
->subject().GetDisplayName();
921 // The DownloadItem we are checking. Will be NULL if the request has been
922 // canceled. Must be accessed only on UI thread.
923 content::DownloadItem
* item_
;
924 // Copies of data from |item_| for access on other threads.
925 std::vector
<GURL
> url_chain_
;
927 // URL chain of redirects leading to (but not including) |tab_url|.
928 std::vector
<GURL
> tab_redirects_
;
929 // URL and referrer of the window the download was started from.
931 GURL tab_referrer_url_
;
933 bool archived_executable_
;
934 ClientDownloadRequest_SignatureInfo signature_info_
;
935 scoped_ptr
<ClientDownloadRequest_ImageHeaders
> image_headers_
;
936 google::protobuf::RepeatedPtrField
<ClientDownloadRequest_ArchivedBinary
>
938 CheckDownloadCallback callback_
;
939 // Will be NULL if the request has been canceled.
940 DownloadProtectionService
* service_
;
941 scoped_refptr
<BinaryFeatureExtractor
> binary_feature_extractor_
;
942 scoped_refptr
<SafeBrowsingDatabaseManager
> database_manager_
;
943 const bool pingback_enabled_
;
944 scoped_ptr
<net::URLFetcher
> fetcher_
;
945 scoped_refptr
<SandboxedZipAnalyzer
> analyzer_
;
946 base::TimeTicks zip_analysis_start_time_
;
947 #if defined(OS_MACOSX)
948 scoped_refptr
<SandboxedDMGAnalyzer
> dmg_analyzer_
;
949 base::TimeTicks dmg_analysis_start_time_
;
952 ClientDownloadRequest::DownloadType type_
;
953 std::string client_download_request_data_
;
954 base::CancelableTaskTracker request_tracker_
; // For HistoryService lookup.
955 base::TimeTicks start_time_
; // Used for stats.
956 base::TimeTicks timeout_start_time_
;
957 base::TimeTicks request_start_time_
;
958 base::WeakPtrFactory
<CheckClientDownloadRequest
> weakptr_factory_
;
960 DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest
);
963 DownloadProtectionService::DownloadProtectionService(
964 SafeBrowsingService
* sb_service
,
965 net::URLRequestContextGetter
* request_context_getter
)
966 : request_context_getter_(request_context_getter
),
968 binary_feature_extractor_(new BinaryFeatureExtractor()),
969 download_request_timeout_ms_(kDownloadRequestTimeoutMs
),
970 feedback_service_(new DownloadFeedbackService(
971 request_context_getter
, BrowserThread::GetBlockingPool())) {
974 ui_manager_
= sb_service
->ui_manager();
975 database_manager_
= sb_service
->database_manager();
979 DownloadProtectionService::~DownloadProtectionService() {
980 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
981 CancelPendingRequests();
984 void DownloadProtectionService::SetEnabled(bool enabled
) {
985 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
986 if (enabled
== enabled_
) {
991 CancelPendingRequests();
995 void DownloadProtectionService::CheckClientDownload(
996 content::DownloadItem
* item
,
997 const CheckDownloadCallback
& callback
) {
998 scoped_refptr
<CheckClientDownloadRequest
> request(
999 new CheckClientDownloadRequest(item
, callback
, this,
1001 binary_feature_extractor_
.get()));
1002 download_requests_
.insert(request
);
1006 void DownloadProtectionService::CheckDownloadUrl(
1007 const content::DownloadItem
& item
,
1008 const CheckDownloadCallback
& callback
) {
1009 DCHECK(!item
.GetUrlChain().empty());
1010 scoped_refptr
<DownloadUrlSBClient
> client(
1011 new DownloadUrlSBClient(item
, callback
, ui_manager_
, database_manager_
));
1012 // The client will release itself once it is done.
1013 BrowserThread::PostTask(
1016 base::Bind(&DownloadUrlSBClient::StartCheck
, client
));
1019 bool DownloadProtectionService::IsSupportedDownload(
1020 const content::DownloadItem
& item
,
1021 const base::FilePath
& target_path
) const {
1022 // Currently, the UI is only enabled on Windows and OSX. On Linux we still
1023 // want to show the dangerous file type warning if the file is possibly
1024 // dangerous which means we have to always return false here.
1025 #if defined(OS_WIN) || defined(OS_MACOSX)
1026 DownloadCheckResultReason reason
= REASON_MAX
;
1027 ClientDownloadRequest::DownloadType type
=
1028 ClientDownloadRequest::WIN_EXECUTABLE
;
1029 return (CheckClientDownloadRequest::IsSupportedDownload(
1030 item
, target_path
, &reason
, &type
) &&
1031 (ClientDownloadRequest::CHROME_EXTENSION
!= type
));
1037 DownloadProtectionService::ClientDownloadRequestSubscription
1038 DownloadProtectionService::RegisterClientDownloadRequestCallback(
1039 const ClientDownloadRequestCallback
& callback
) {
1040 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1041 return client_download_request_callbacks_
.Add(callback
);
1044 void DownloadProtectionService::CancelPendingRequests() {
1045 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1046 for (std::set
<scoped_refptr
<CheckClientDownloadRequest
> >::iterator it
=
1047 download_requests_
.begin();
1048 it
!= download_requests_
.end();) {
1049 // We need to advance the iterator before we cancel because canceling
1050 // the request will invalidate it when RequestFinished is called below.
1051 scoped_refptr
<CheckClientDownloadRequest
> tmp
= *it
++;
1054 DCHECK(download_requests_
.empty());
1057 void DownloadProtectionService::RequestFinished(
1058 CheckClientDownloadRequest
* request
) {
1059 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
1060 std::set
<scoped_refptr
<CheckClientDownloadRequest
> >::iterator it
=
1061 download_requests_
.find(request
);
1062 DCHECK(it
!= download_requests_
.end());
1063 download_requests_
.erase(*it
);
1066 void DownloadProtectionService::ShowDetailsForDownload(
1067 const content::DownloadItem
& item
,
1068 content::PageNavigator
* navigator
) {
1069 GURL
learn_more_url(chrome::kDownloadScanningLearnMoreURL
);
1070 learn_more_url
= google_util::AppendGoogleLocaleParam(
1071 learn_more_url
, g_browser_process
->GetApplicationLocale());
1072 learn_more_url
= net::AppendQueryParameter(
1073 learn_more_url
, "ctx",
1074 base::IntToString(static_cast<int>(item
.GetDangerType())));
1076 content::OpenURLParams(learn_more_url
,
1077 content::Referrer(),
1079 ui::PAGE_TRANSITION_LINK
,
1084 // Escapes a certificate attribute so that it can be used in a whitelist
1085 // entry. Currently, we only escape slashes, since they are used as a
1086 // separator between attributes.
1087 std::string
EscapeCertAttribute(const std::string
& attribute
) {
1088 std::string escaped
;
1089 for (size_t i
= 0; i
< attribute
.size(); ++i
) {
1090 if (attribute
[i
] == '%') {
1091 escaped
.append("%25");
1092 } else if (attribute
[i
] == '/') {
1093 escaped
.append("%2F");
1095 escaped
.push_back(attribute
[i
]);
1103 void DownloadProtectionService::GetCertificateWhitelistStrings(
1104 const net::X509Certificate
& certificate
,
1105 const net::X509Certificate
& issuer
,
1106 std::vector
<std::string
>* whitelist_strings
) {
1107 // The whitelist paths are in the format:
1108 // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit]
1110 // Any of CN, O, or OU may be omitted from the whitelist entry, in which
1111 // case they match anything. However, the attributes that do appear will
1112 // always be in the order shown above. At least one attribute will always
1115 const net::CertPrincipal
& subject
= certificate
.subject();
1116 std::vector
<std::string
> ou_tokens
;
1117 for (size_t i
= 0; i
< subject
.organization_unit_names
.size(); ++i
) {
1118 ou_tokens
.push_back(
1119 "/OU=" + EscapeCertAttribute(subject
.organization_unit_names
[i
]));
1122 std::vector
<std::string
> o_tokens
;
1123 for (size_t i
= 0; i
< subject
.organization_names
.size(); ++i
) {
1125 "/O=" + EscapeCertAttribute(subject
.organization_names
[i
]));
1128 std::string cn_token
;
1129 if (!subject
.common_name
.empty()) {
1130 cn_token
= "/CN=" + EscapeCertAttribute(subject
.common_name
);
1133 std::set
<std::string
> paths_to_check
;
1134 if (!cn_token
.empty()) {
1135 paths_to_check
.insert(cn_token
);
1137 for (size_t i
= 0; i
< o_tokens
.size(); ++i
) {
1138 paths_to_check
.insert(cn_token
+ o_tokens
[i
]);
1139 paths_to_check
.insert(o_tokens
[i
]);
1140 for (size_t j
= 0; j
< ou_tokens
.size(); ++j
) {
1141 paths_to_check
.insert(cn_token
+ o_tokens
[i
] + ou_tokens
[j
]);
1142 paths_to_check
.insert(o_tokens
[i
] + ou_tokens
[j
]);
1145 for (size_t i
= 0; i
< ou_tokens
.size(); ++i
) {
1146 paths_to_check
.insert(cn_token
+ ou_tokens
[i
]);
1147 paths_to_check
.insert(ou_tokens
[i
]);
1150 std::string issuer_fp
= base::HexEncode(issuer
.fingerprint().data
,
1151 sizeof(issuer
.fingerprint().data
));
1152 for (std::set
<std::string
>::iterator it
= paths_to_check
.begin();
1153 it
!= paths_to_check
.end(); ++it
) {
1154 whitelist_strings
->push_back("cert/" + issuer_fp
+ *it
);
1159 GURL
DownloadProtectionService::GetDownloadRequestUrl() {
1160 GURL
url(kDownloadRequestUrl
);
1161 std::string api_key
= google_apis::GetAPIKey();
1162 if (!api_key
.empty())
1163 url
= url
.Resolve("?key=" + net::EscapeQueryParamValue(api_key
, true));
1168 } // namespace safe_browsing