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/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/profiles/profile.h"
27 #include "chrome/browser/safe_browsing/binary_feature_extractor.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/safe_browsing/csd.pb.h"
34 #include "chrome/common/safe_browsing/download_protection_util.h"
35 #include "chrome/common/safe_browsing/zip_analyzer.h"
36 #include "chrome/common/url_constants.h"
37 #include "components/google/core/browser/google_util.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/cert/x509_cert_types.h"
46 #include "net/cert/x509_certificate.h"
47 #include "net/http/http_status_code.h"
48 #include "net/url_request/url_fetcher.h"
49 #include "net/url_request/url_fetcher_delegate.h"
50 #include "net/url_request/url_request_context_getter.h"
51 #include "net/url_request/url_request_status.h"
53 using content::BrowserThread
;
56 static const int64 kDownloadRequestTimeoutMs
= 7000;
59 namespace safe_browsing
{
61 const char DownloadProtectionService::kDownloadRequestUrl
[] =
62 "https://sb-ssl.google.com/safebrowsing/clientreport/download";
65 // List of extensions for which we track some UMA stats.
66 enum MaliciousExtensionType
{
85 EXTENSION_OTHER
, // Groups all other extensions into one bucket.
94 MaliciousExtensionType
GetExtensionType(const base::FilePath
& f
) {
95 if (f
.MatchesExtension(FILE_PATH_LITERAL(".exe"))) return EXTENSION_EXE
;
96 if (f
.MatchesExtension(FILE_PATH_LITERAL(".msi"))) return EXTENSION_MSI
;
97 if (f
.MatchesExtension(FILE_PATH_LITERAL(".cab"))) return EXTENSION_CAB
;
98 if (f
.MatchesExtension(FILE_PATH_LITERAL(".sys"))) return EXTENSION_SYS
;
99 if (f
.MatchesExtension(FILE_PATH_LITERAL(".scr"))) return EXTENSION_SCR
;
100 if (f
.MatchesExtension(FILE_PATH_LITERAL(".drv"))) return EXTENSION_DRV
;
101 if (f
.MatchesExtension(FILE_PATH_LITERAL(".bat"))) return EXTENSION_BAT
;
102 if (f
.MatchesExtension(FILE_PATH_LITERAL(".zip"))) return EXTENSION_ZIP
;
103 if (f
.MatchesExtension(FILE_PATH_LITERAL(".rar"))) return EXTENSION_RAR
;
104 if (f
.MatchesExtension(FILE_PATH_LITERAL(".dll"))) return EXTENSION_DLL
;
105 if (f
.MatchesExtension(FILE_PATH_LITERAL(".pif"))) return EXTENSION_PIF
;
106 if (f
.MatchesExtension(FILE_PATH_LITERAL(".com"))) return EXTENSION_COM
;
107 if (f
.MatchesExtension(FILE_PATH_LITERAL(".jar"))) return EXTENSION_JAR
;
108 if (f
.MatchesExtension(FILE_PATH_LITERAL(".class"))) return EXTENSION_CLASS
;
109 if (f
.MatchesExtension(FILE_PATH_LITERAL(".pdf"))) return EXTENSION_PDF
;
110 if (f
.MatchesExtension(FILE_PATH_LITERAL(".vb"))) return EXTENSION_VB
;
111 if (f
.MatchesExtension(FILE_PATH_LITERAL(".reg"))) return EXTENSION_REG
;
112 if (f
.MatchesExtension(FILE_PATH_LITERAL(".grp"))) return EXTENSION_GRP
;
113 if (f
.MatchesExtension(FILE_PATH_LITERAL(".crx"))) return EXTENSION_CRX
;
114 if (f
.MatchesExtension(FILE_PATH_LITERAL(".apk"))) return EXTENSION_APK
;
115 if (f
.MatchesExtension(FILE_PATH_LITERAL(".dmg"))) return EXTENSION_DMG
;
116 if (f
.MatchesExtension(FILE_PATH_LITERAL(".pkg"))) return EXTENSION_PKG
;
117 if (f
.MatchesExtension(FILE_PATH_LITERAL(".torrent")))
118 return EXTENSION_TORRENT
;
119 return EXTENSION_OTHER
;
122 void RecordFileExtensionType(const base::FilePath
& file
) {
123 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadExtensions",
124 GetExtensionType(file
),
128 // Enumerate for histogramming purposes.
129 // DO NOT CHANGE THE ORDERING OF THESE VALUES (different histogram data will
130 // be mixed together based on their values).
132 DOWNLOAD_URL_CHECKS_TOTAL
,
133 DOWNLOAD_URL_CHECKS_CANCELED
,
134 DOWNLOAD_URL_CHECKS_MALWARE
,
136 DOWNLOAD_HASH_CHECKS_TOTAL
,
137 DOWNLOAD_HASH_CHECKS_MALWARE
,
139 // Memory space for histograms is determined by the max.
140 // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
144 // Prepares URLs to be put into a ping message. Currently this just shortens
145 // data: URIs, other URLs are included verbatim.
146 std::string
SanitizeUrl(const GURL
& url
) {
147 std::string spec
= url
.spec();
148 if (url
.SchemeIs(url::kDataScheme
)) {
149 size_t comma_pos
= spec
.find(',');
150 if (comma_pos
!= std::string::npos
&& comma_pos
!= spec
.size() - 1) {
151 std::string hash_value
= crypto::SHA256HashString(spec
);
152 spec
.erase(comma_pos
+ 1);
153 spec
+= base::HexEncode(hash_value
.data(), hash_value
.size());
161 // Parent SafeBrowsing::Client class used to lookup the bad binary
162 // URL and digest list. There are two sub-classes (one for each list).
163 class DownloadSBClient
164 : public SafeBrowsingDatabaseManager::Client
,
165 public base::RefCountedThreadSafe
<DownloadSBClient
> {
168 const content::DownloadItem
& item
,
169 const DownloadProtectionService::CheckDownloadCallback
& callback
,
170 const scoped_refptr
<SafeBrowsingUIManager
>& ui_manager
,
171 SBStatsType total_type
,
172 SBStatsType dangerous_type
)
173 : sha256_hash_(item
.GetHash()),
174 url_chain_(item
.GetUrlChain()),
175 referrer_url_(item
.GetReferrerUrl()),
177 ui_manager_(ui_manager
),
178 start_time_(base::TimeTicks::Now()),
179 total_type_(total_type
),
180 dangerous_type_(dangerous_type
) {}
182 virtual void StartCheck() = 0;
183 virtual bool IsDangerous(SBThreatType threat_type
) const = 0;
186 friend class base::RefCountedThreadSafe
<DownloadSBClient
>;
187 ~DownloadSBClient() override
{}
189 void CheckDone(SBThreatType threat_type
) {
190 DownloadProtectionService::DownloadCheckResult result
=
191 IsDangerous(threat_type
) ?
192 DownloadProtectionService::DANGEROUS
:
193 DownloadProtectionService::SAFE
;
194 BrowserThread::PostTask(BrowserThread::UI
,
196 base::Bind(callback_
, result
));
197 UpdateDownloadCheckStats(total_type_
);
198 if (threat_type
!= SB_THREAT_TYPE_SAFE
) {
199 UpdateDownloadCheckStats(dangerous_type_
);
200 BrowserThread::PostTask(
203 base::Bind(&DownloadSBClient::ReportMalware
,
208 void ReportMalware(SBThreatType threat_type
) {
209 std::string post_data
;
210 if (!sha256_hash_
.empty())
211 post_data
+= base::HexEncode(sha256_hash_
.data(),
212 sha256_hash_
.size()) + "\n";
213 for (size_t i
= 0; i
< url_chain_
.size(); ++i
) {
214 post_data
+= url_chain_
[i
].spec() + "\n";
216 ui_manager_
->ReportSafeBrowsingHit(
217 url_chain_
.back(), // malicious_url
218 url_chain_
.front(), // page_url
220 true, // is_subresource
225 void UpdateDownloadCheckStats(SBStatsType stat_type
) {
226 UMA_HISTOGRAM_ENUMERATION("SB2.DownloadChecks",
228 DOWNLOAD_CHECKS_MAX
);
231 std::string sha256_hash_
;
232 std::vector
<GURL
> url_chain_
;
234 DownloadProtectionService::CheckDownloadCallback callback_
;
235 scoped_refptr
<SafeBrowsingUIManager
> ui_manager_
;
236 base::TimeTicks start_time_
;
239 const SBStatsType total_type_
;
240 const SBStatsType dangerous_type_
;
242 DISALLOW_COPY_AND_ASSIGN(DownloadSBClient
);
245 class DownloadUrlSBClient
: public DownloadSBClient
{
248 const content::DownloadItem
& item
,
249 const DownloadProtectionService::CheckDownloadCallback
& callback
,
250 const scoped_refptr
<SafeBrowsingUIManager
>& ui_manager
,
251 const scoped_refptr
<SafeBrowsingDatabaseManager
>& database_manager
)
252 : DownloadSBClient(item
, callback
, ui_manager
,
253 DOWNLOAD_URL_CHECKS_TOTAL
,
254 DOWNLOAD_URL_CHECKS_MALWARE
),
255 database_manager_(database_manager
) { }
257 void StartCheck() override
{
258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
259 if (!database_manager_
.get() ||
260 database_manager_
->CheckDownloadUrl(url_chain_
, this)) {
261 CheckDone(SB_THREAT_TYPE_SAFE
);
263 AddRef(); // SafeBrowsingService takes a pointer not a scoped_refptr.
267 bool IsDangerous(SBThreatType threat_type
) const override
{
268 return threat_type
== SB_THREAT_TYPE_BINARY_MALWARE_URL
;
271 void OnCheckDownloadUrlResult(const std::vector
<GURL
>& url_chain
,
272 SBThreatType threat_type
) override
{
273 CheckDone(threat_type
);
274 UMA_HISTOGRAM_TIMES("SB2.DownloadUrlCheckDuration",
275 base::TimeTicks::Now() - start_time_
);
280 ~DownloadUrlSBClient() override
{}
283 scoped_refptr
<SafeBrowsingDatabaseManager
> database_manager_
;
285 DISALLOW_COPY_AND_ASSIGN(DownloadUrlSBClient
);
288 class DownloadProtectionService::CheckClientDownloadRequest
289 : public base::RefCountedThreadSafe
<
290 DownloadProtectionService::CheckClientDownloadRequest
,
291 BrowserThread::DeleteOnUIThread
>,
292 public net::URLFetcherDelegate
,
293 public content::DownloadItem::Observer
{
295 CheckClientDownloadRequest(
296 content::DownloadItem
* item
,
297 const CheckDownloadCallback
& callback
,
298 DownloadProtectionService
* service
,
299 const scoped_refptr
<SafeBrowsingDatabaseManager
>& database_manager
,
300 BinaryFeatureExtractor
* binary_feature_extractor
)
302 url_chain_(item
->GetUrlChain()),
303 referrer_url_(item
->GetReferrerUrl()),
304 tab_url_(item
->GetTabUrl()),
305 tab_referrer_url_(item
->GetTabReferrerUrl()),
306 zipped_executable_(false),
309 binary_feature_extractor_(binary_feature_extractor
),
310 database_manager_(database_manager
),
311 pingback_enabled_(service_
->enabled()),
313 type_(ClientDownloadRequest::WIN_EXECUTABLE
),
314 start_time_(base::TimeTicks::Now()),
315 weakptr_factory_(this) {
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
317 item_
->AddObserver(this);
321 DVLOG(2) << "Starting SafeBrowsing download check for: "
322 << item_
->DebugString(true);
323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
324 // TODO(noelutz): implement some cache to make sure we don't issue the same
325 // request over and over again if a user downloads the same binary multiple
327 DownloadCheckResultReason reason
= REASON_MAX
;
328 if (!IsSupportedDownload(
329 *item_
, item_
->GetTargetFilePath(), &reason
, &type_
)) {
331 case REASON_EMPTY_URL_CHAIN
:
332 case REASON_INVALID_URL
:
333 case REASON_UNSUPPORTED_URL_SCHEME
:
334 PostFinishTask(UNKNOWN
, reason
);
337 case REASON_NOT_BINARY_FILE
:
338 RecordFileExtensionType(item_
->GetTargetFilePath());
339 PostFinishTask(UNKNOWN
, reason
);
343 // We only expect the reasons explicitly handled above.
347 RecordFileExtensionType(item_
->GetTargetFilePath());
349 // Compute features from the file contents. Note that we record histograms
350 // based on the result, so this runs regardless of whether the pingbacks
352 if (item_
->GetTargetFilePath().MatchesExtension(
353 FILE_PATH_LITERAL(".zip"))) {
354 StartExtractZipFeatures();
356 DCHECK(!download_protection_util::IsArchiveFile(
357 item_
->GetTargetFilePath()));
358 StartExtractFileFeatures();
362 // Start a timeout to cancel the request if it takes too long.
363 // This should only be called after we have finished accessing the file.
364 void StartTimeout() {
365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
367 // Request has already been cancelled.
370 timeout_start_time_
= base::TimeTicks::Now();
371 BrowserThread::PostDelayedTask(
374 base::Bind(&CheckClientDownloadRequest::Cancel
,
375 weakptr_factory_
.GetWeakPtr()),
376 base::TimeDelta::FromMilliseconds(
377 service_
->download_request_timeout_ms()));
380 // Canceling a request will cause us to always report the result as UNKNOWN
381 // unless a pending request is about to call FinishRequest.
383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
384 if (fetcher_
.get()) {
385 // The DownloadProtectionService is going to release its reference, so we
386 // might be destroyed before the URLFetcher completes. Cancel the
387 // fetcher so it does not try to invoke OnURLFetchComplete.
390 // Note: If there is no fetcher, then some callback is still holding a
391 // reference to this object. We'll eventually wind up in some method on
392 // the UI thread that will call FinishRequest() again. If FinishRequest()
393 // is called a second time, it will be a no-op.
394 FinishRequest(UNKNOWN
, REASON_REQUEST_CANCELED
);
395 // Calling FinishRequest might delete this object, we may be deleted by
399 // content::DownloadItem::Observer implementation.
400 void OnDownloadDestroyed(content::DownloadItem
* download
) override
{
402 DCHECK(item_
== NULL
);
405 // From the net::URLFetcherDelegate interface.
406 void OnURLFetchComplete(const net::URLFetcher
* source
) override
{
407 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
408 DCHECK_EQ(source
, fetcher_
.get());
409 DVLOG(2) << "Received a response for URL: "
410 << item_
->GetUrlChain().back() << ": success="
411 << source
->GetStatus().is_success() << " response_code="
412 << source
->GetResponseCode();
413 if (source
->GetStatus().is_success()) {
414 UMA_HISTOGRAM_SPARSE_SLOWLY(
415 "SBClientDownload.DownloadRequestResponseCode",
416 source
->GetResponseCode());
418 UMA_HISTOGRAM_SPARSE_SLOWLY(
419 "SBClientDownload.DownloadRequestNetError",
420 -source
->GetStatus().error());
421 DownloadCheckResultReason reason
= REASON_SERVER_PING_FAILED
;
422 DownloadCheckResult result
= UNKNOWN
;
423 if (source
->GetStatus().is_success() &&
424 net::HTTP_OK
== source
->GetResponseCode()) {
425 ClientDownloadResponse response
;
427 bool got_data
= source
->GetResponseAsString(&data
);
429 if (!response
.ParseFromString(data
)) {
430 reason
= REASON_INVALID_RESPONSE_PROTO
;
432 } else if (response
.verdict() == ClientDownloadResponse::SAFE
) {
433 reason
= REASON_DOWNLOAD_SAFE
;
435 } else if (service_
&& !service_
->IsSupportedDownload(
436 *item_
, item_
->GetTargetFilePath())) {
437 // The client of the download protection service assumes that we don't
438 // support this download so we cannot return any other verdict than
439 // UNKNOWN even if the server says it's dangerous to download this file.
440 // Note: if service_ is NULL we already cancelled the request and
442 reason
= REASON_DOWNLOAD_NOT_SUPPORTED
;
444 } else if (response
.verdict() == ClientDownloadResponse::DANGEROUS
) {
445 reason
= REASON_DOWNLOAD_DANGEROUS
;
447 } else if (response
.verdict() == ClientDownloadResponse::UNCOMMON
) {
448 reason
= REASON_DOWNLOAD_UNCOMMON
;
450 } else if (response
.verdict() == ClientDownloadResponse::DANGEROUS_HOST
) {
451 reason
= REASON_DOWNLOAD_DANGEROUS_HOST
;
452 result
= DANGEROUS_HOST
;
454 response
.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED
) {
455 reason
= REASON_DOWNLOAD_POTENTIALLY_UNWANTED
;
456 result
= POTENTIALLY_UNWANTED
;
458 LOG(DFATAL
) << "Unknown download response verdict: "
459 << response
.verdict();
460 reason
= REASON_INVALID_RESPONSE_VERDICT
;
463 DownloadFeedbackService::MaybeStorePingsForDownload(
464 result
, item_
, client_download_request_data_
, data
);
466 // We don't need the fetcher anymore.
468 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration",
469 base::TimeTicks::Now() - start_time_
);
470 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestNetworkDuration",
471 base::TimeTicks::Now() - request_start_time_
);
472 FinishRequest(result
, reason
);
475 static bool IsSupportedDownload(const content::DownloadItem
& item
,
476 const base::FilePath
& target_path
,
477 DownloadCheckResultReason
* reason
,
478 ClientDownloadRequest::DownloadType
* type
) {
479 if (item
.GetUrlChain().empty()) {
480 *reason
= REASON_EMPTY_URL_CHAIN
;
483 const GURL
& final_url
= item
.GetUrlChain().back();
484 if (!final_url
.is_valid() || final_url
.is_empty()) {
485 *reason
= REASON_INVALID_URL
;
488 if (!download_protection_util::IsBinaryFile(target_path
)) {
489 *reason
= REASON_NOT_BINARY_FILE
;
492 if ((!final_url
.IsStandard() && !final_url
.SchemeIsBlob() &&
493 !final_url
.SchemeIs(url::kDataScheme
)) ||
494 final_url
.SchemeIsFile()) {
495 *reason
= REASON_UNSUPPORTED_URL_SCHEME
;
498 *type
= download_protection_util::GetDownloadType(target_path
);
503 friend struct BrowserThread::DeleteOnThread
<BrowserThread::UI
>;
504 friend class base::DeleteHelper
<CheckClientDownloadRequest
>;
506 ~CheckClientDownloadRequest() override
{
507 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
508 DCHECK(item_
== NULL
);
511 void OnFileFeatureExtractionDone() {
512 // This can run in any thread, since it just posts more messages.
514 // TODO(noelutz): DownloadInfo should also contain the IP address of
515 // every URL in the redirect chain. We also should check whether the
516 // download URL is hosted on the internal network.
517 BrowserThread::PostTask(
520 base::Bind(&CheckClientDownloadRequest::CheckWhitelists
, this));
522 // We wait until after the file checks finish to start the timeout, as
523 // windows can cause permissions errors if the timeout fired while we were
524 // checking the file signature and we tried to complete the download.
525 BrowserThread::PostTask(
528 base::Bind(&CheckClientDownloadRequest::StartTimeout
, this));
531 void StartExtractFileFeatures() {
532 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
533 DCHECK(item_
); // Called directly from Start(), item should still exist.
534 // Since we do blocking I/O, offload this to a worker thread.
535 // The task does not need to block shutdown.
536 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
538 base::Bind(&CheckClientDownloadRequest::ExtractFileFeatures
,
539 this, item_
->GetFullPath()),
540 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
543 void ExtractFileFeatures(const base::FilePath
& file_path
) {
544 base::TimeTicks start_time
= base::TimeTicks::Now();
545 binary_feature_extractor_
->CheckSignature(file_path
, &signature_info_
);
546 bool is_signed
= (signature_info_
.certificate_chain_size() > 0);
548 DVLOG(2) << "Downloaded a signed binary: " << file_path
.value();
550 DVLOG(2) << "Downloaded an unsigned binary: "
551 << file_path
.value();
553 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed
);
554 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime",
555 base::TimeTicks::Now() - start_time
);
557 start_time
= base::TimeTicks::Now();
558 binary_feature_extractor_
->ExtractImageHeaders(file_path
, &image_headers_
);
559 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractImageHeadersTime",
560 base::TimeTicks::Now() - start_time
);
562 OnFileFeatureExtractionDone();
565 void StartExtractZipFeatures() {
566 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
567 DCHECK(item_
); // Called directly from Start(), item should still exist.
568 zip_analysis_start_time_
= base::TimeTicks::Now();
569 // We give the zip analyzer a weak pointer to this object. Since the
570 // analyzer is refcounted, it might outlive the request.
571 analyzer_
= new SandboxedZipAnalyzer(
572 item_
->GetFullPath(),
573 base::Bind(&CheckClientDownloadRequest::OnZipAnalysisFinished
,
574 weakptr_factory_
.GetWeakPtr()));
578 void OnZipAnalysisFinished(const zip_analyzer::Results
& results
) {
579 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
582 if (results
.success
) {
583 zipped_executable_
= results
.has_executable
;
584 DVLOG(1) << "Zip analysis finished for " << item_
->GetFullPath().value()
585 << ", has_executable=" << results
.has_executable
586 << " has_archive=" << results
.has_archive
;
588 DVLOG(1) << "Zip analysis failed for " << item_
->GetFullPath().value();
590 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
592 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable",
593 results
.has_archive
&& !zipped_executable_
);
594 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime",
595 base::TimeTicks::Now() - zip_analysis_start_time_
);
597 if (!zipped_executable_
) {
598 PostFinishTask(UNKNOWN
, REASON_ARCHIVE_WITHOUT_BINARIES
);
601 OnFileFeatureExtractionDone();
604 static void RecordCountOfSignedOrWhitelistedDownload() {
605 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
608 void CheckWhitelists() {
609 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
611 if (!database_manager_
.get()) {
612 PostFinishTask(UNKNOWN
, REASON_SB_DISABLED
);
616 const GURL
& url
= url_chain_
.back();
617 if (url
.is_valid() && database_manager_
->MatchDownloadWhitelistUrl(url
)) {
618 DVLOG(2) << url
<< " is on the download whitelist.";
619 RecordCountOfSignedOrWhitelistedDownload();
620 // TODO(grt): Continue processing without uploading so that
621 // ClientDownloadRequest callbacks can be run even for this type of safe
623 PostFinishTask(SAFE
, REASON_WHITELISTED_URL
);
627 if (signature_info_
.trusted()) {
628 RecordCountOfSignedOrWhitelistedDownload();
629 for (int i
= 0; i
< signature_info_
.certificate_chain_size(); ++i
) {
630 if (CertificateChainIsWhitelisted(
631 signature_info_
.certificate_chain(i
))) {
632 // TODO(grt): Continue processing without uploading so that
633 // ClientDownloadRequest callbacks can be run even for this type of
635 PostFinishTask(SAFE
, REASON_TRUSTED_EXECUTABLE
);
641 if (!pingback_enabled_
) {
642 PostFinishTask(UNKNOWN
, REASON_PING_DISABLED
);
646 // Currently, the UI only works on Windows so we don't even bother with
647 // pinging the server if we're not on Windows.
648 // TODO(noelutz): change this code once the UI is done for Linux and Mac.
649 #if defined(OS_MACOSX)
650 // TODO(mattm): remove this (see crbug.com/414834).
651 if (base::FieldTrialList::FindFullName("SafeBrowsingOSXClientDownloadPings")
653 PostFinishTask(UNKNOWN
, REASON_OS_NOT_SUPPORTED
);
657 #if defined(OS_WIN) || defined(OS_MACOSX)
658 // The URLFetcher is owned by the UI thread, so post a message to
659 // start the pingback.
660 BrowserThread::PostTask(
663 base::Bind(&CheckClientDownloadRequest::GetTabRedirects
, this));
665 PostFinishTask(UNKNOWN
, REASON_OS_NOT_SUPPORTED
);
669 void GetTabRedirects() {
670 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
674 if (!tab_url_
.is_valid()) {
679 Profile
* profile
= Profile::FromBrowserContext(item_
->GetBrowserContext());
680 HistoryService
* history
= HistoryServiceFactory::GetForProfile(
681 profile
, ServiceAccessType::EXPLICIT_ACCESS
);
687 history
->QueryRedirectsTo(
689 base::Bind(&CheckClientDownloadRequest::OnGotTabRedirects
,
690 base::Unretained(this),
695 void OnGotTabRedirects(const GURL
& url
,
696 const history::RedirectList
* redirect_list
) {
697 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
698 DCHECK_EQ(url
, tab_url_
);
702 if (!redirect_list
->empty()) {
703 tab_redirects_
.insert(
704 tab_redirects_
.end(), redirect_list
->rbegin(), redirect_list
->rend());
711 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
713 // This is our last chance to check whether the request has been canceled
714 // before sending it.
718 ClientDownloadRequest request
;
719 request
.set_url(SanitizeUrl(item_
->GetUrlChain().back()));
720 request
.mutable_digests()->set_sha256(item_
->GetHash());
721 request
.set_length(item_
->GetReceivedBytes());
722 for (size_t i
= 0; i
< item_
->GetUrlChain().size(); ++i
) {
723 ClientDownloadRequest::Resource
* resource
= request
.add_resources();
724 resource
->set_url(SanitizeUrl(item_
->GetUrlChain()[i
]));
725 if (i
== item_
->GetUrlChain().size() - 1) {
726 // The last URL in the chain is the download URL.
727 resource
->set_type(ClientDownloadRequest::DOWNLOAD_URL
);
728 resource
->set_referrer(SanitizeUrl(item_
->GetReferrerUrl()));
729 DVLOG(2) << "dl url " << resource
->url();
730 if (!item_
->GetRemoteAddress().empty()) {
731 resource
->set_remote_ip(item_
->GetRemoteAddress());
732 DVLOG(2) << " dl url remote addr: " << resource
->remote_ip();
734 DVLOG(2) << "dl referrer " << resource
->referrer();
736 DVLOG(2) << "dl redirect " << i
<< " " << resource
->url();
737 resource
->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT
);
739 // TODO(noelutz): fill out the remote IP addresses.
741 // TODO(mattm): fill out the remote IP addresses for tab resources.
742 for (size_t i
= 0; i
< tab_redirects_
.size(); ++i
) {
743 ClientDownloadRequest::Resource
* resource
= request
.add_resources();
744 DVLOG(2) << "tab redirect " << i
<< " " << tab_redirects_
[i
].spec();
745 resource
->set_url(SanitizeUrl(tab_redirects_
[i
]));
746 resource
->set_type(ClientDownloadRequest::TAB_REDIRECT
);
748 if (tab_url_
.is_valid()) {
749 ClientDownloadRequest::Resource
* resource
= request
.add_resources();
750 resource
->set_url(SanitizeUrl(tab_url_
));
751 DVLOG(2) << "tab url " << resource
->url();
752 resource
->set_type(ClientDownloadRequest::TAB_URL
);
753 if (tab_referrer_url_
.is_valid()) {
754 resource
->set_referrer(SanitizeUrl(tab_referrer_url_
));
755 DVLOG(2) << "tab referrer " << resource
->referrer();
759 request
.set_user_initiated(item_
->HasUserGesture());
760 request
.set_file_basename(
761 item_
->GetTargetFilePath().BaseName().AsUTF8Unsafe());
762 request
.set_download_type(type_
);
763 request
.mutable_signature()->CopyFrom(signature_info_
);
764 request
.mutable_image_headers()->CopyFrom(image_headers_
);
765 if (!request
.SerializeToString(&client_download_request_data_
)) {
766 FinishRequest(UNKNOWN
, REASON_INVALID_REQUEST_PROTO
);
770 service_
->client_download_request_callbacks_
.Notify(item_
, &request
);
772 DVLOG(2) << "Sending a request for URL: "
773 << item_
->GetUrlChain().back();
774 fetcher_
.reset(net::URLFetcher::Create(0 /* ID used for testing */,
775 GetDownloadRequestUrl(),
776 net::URLFetcher::POST
,
778 fetcher_
->SetLoadFlags(net::LOAD_DISABLE_CACHE
);
779 fetcher_
->SetAutomaticallyRetryOn5xx(false); // Don't retry on error.
780 fetcher_
->SetRequestContext(service_
->request_context_getter_
.get());
781 fetcher_
->SetUploadData("application/octet-stream",
782 client_download_request_data_
);
783 request_start_time_
= base::TimeTicks::Now();
784 UMA_HISTOGRAM_COUNTS("SBClientDownload.DownloadRequestPayloadSize",
785 client_download_request_data_
.size());
789 void PostFinishTask(DownloadCheckResult result
,
790 DownloadCheckResultReason reason
) {
791 BrowserThread::PostTask(
794 base::Bind(&CheckClientDownloadRequest::FinishRequest
, this, result
,
798 void FinishRequest(DownloadCheckResult result
,
799 DownloadCheckResultReason reason
) {
800 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
805 // Ensure the timeout task is cancelled while we still have a non-zero
806 // refcount. (crbug.com/240449)
807 weakptr_factory_
.InvalidateWeakPtrs();
808 if (!request_start_time_
.is_null()) {
809 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestNetworkStats",
813 if (!timeout_start_time_
.is_null()) {
814 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestTimeoutStats",
817 if (reason
!= REASON_REQUEST_CANCELED
) {
818 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestTimeoutDuration",
819 base::TimeTicks::Now() - timeout_start_time_
);
822 if (result
== SAFE
&& (reason
== REASON_WHITELISTED_URL
||
823 reason
== REASON_TRUSTED_EXECUTABLE
)) {
824 // Due to the short-circuit logic in CheckWhitelists (see TODOs there), a
825 // ClientDownloadRequest was not generated for this download and callbacks
826 // were not run. Run them now with null to indicate that a download has
828 // TODO(grt): persist metadata for these downloads as well.
829 service_
->client_download_request_callbacks_
.Notify(item_
, nullptr);
832 DVLOG(2) << "SafeBrowsing download verdict for: "
833 << item_
->DebugString(true) << " verdict:" << reason
834 << " result:" << result
;
835 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
838 #if defined(OS_MACOSX)
839 // OSX is currently sending pings only for evaluation purposes, ignore
840 // the result for now.
841 // TODO(mattm): remove this and update the ifdef in
842 // DownloadItemImpl::IsDangerous (see crbug.com/413968).
845 callback_
.Run(result
);
846 item_
->RemoveObserver(this);
848 DownloadProtectionService
* service
= service_
;
850 service
->RequestFinished(this);
851 // DownloadProtectionService::RequestFinished will decrement our refcount,
852 // so we may be deleted now.
854 callback_
.Run(UNKNOWN
);
858 bool CertificateChainIsWhitelisted(
859 const ClientDownloadRequest_CertificateChain
& chain
) {
860 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
861 if (chain
.element_size() < 2) {
862 // We need to have both a signing certificate and its issuer certificate
863 // present to construct a whitelist entry.
866 scoped_refptr
<net::X509Certificate
> cert
=
867 net::X509Certificate::CreateFromBytes(
868 chain
.element(0).certificate().data(),
869 chain
.element(0).certificate().size());
874 for (int i
= 1; i
< chain
.element_size(); ++i
) {
875 scoped_refptr
<net::X509Certificate
> issuer
=
876 net::X509Certificate::CreateFromBytes(
877 chain
.element(i
).certificate().data(),
878 chain
.element(i
).certificate().size());
882 std::vector
<std::string
> whitelist_strings
;
883 DownloadProtectionService::GetCertificateWhitelistStrings(
884 *cert
.get(), *issuer
.get(), &whitelist_strings
);
885 for (size_t j
= 0; j
< whitelist_strings
.size(); ++j
) {
886 if (database_manager_
->MatchDownloadWhitelistString(
887 whitelist_strings
[j
])) {
888 DVLOG(2) << "Certificate matched whitelist, cert="
889 << cert
->subject().GetDisplayName()
890 << " issuer=" << issuer
->subject().GetDisplayName();
899 // The DownloadItem we are checking. Will be NULL if the request has been
900 // canceled. Must be accessed only on UI thread.
901 content::DownloadItem
* item_
;
902 // Copies of data from |item_| for access on other threads.
903 std::vector
<GURL
> url_chain_
;
905 // URL chain of redirects leading to (but not including) |tab_url|.
906 std::vector
<GURL
> tab_redirects_
;
907 // URL and referrer of the window the download was started from.
909 GURL tab_referrer_url_
;
911 bool zipped_executable_
;
912 ClientDownloadRequest_SignatureInfo signature_info_
;
913 ClientDownloadRequest_ImageHeaders image_headers_
;
914 CheckDownloadCallback callback_
;
915 // Will be NULL if the request has been canceled.
916 DownloadProtectionService
* service_
;
917 scoped_refptr
<BinaryFeatureExtractor
> binary_feature_extractor_
;
918 scoped_refptr
<SafeBrowsingDatabaseManager
> database_manager_
;
919 const bool pingback_enabled_
;
920 scoped_ptr
<net::URLFetcher
> fetcher_
;
921 scoped_refptr
<SandboxedZipAnalyzer
> analyzer_
;
922 base::TimeTicks zip_analysis_start_time_
;
924 ClientDownloadRequest::DownloadType type_
;
925 std::string client_download_request_data_
;
926 base::CancelableTaskTracker request_tracker_
; // For HistoryService lookup.
927 base::TimeTicks start_time_
; // Used for stats.
928 base::TimeTicks timeout_start_time_
;
929 base::TimeTicks request_start_time_
;
930 base::WeakPtrFactory
<CheckClientDownloadRequest
> weakptr_factory_
;
932 DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest
);
935 DownloadProtectionService::DownloadProtectionService(
936 SafeBrowsingService
* sb_service
,
937 net::URLRequestContextGetter
* request_context_getter
)
938 : request_context_getter_(request_context_getter
),
940 binary_feature_extractor_(new BinaryFeatureExtractor()),
941 download_request_timeout_ms_(kDownloadRequestTimeoutMs
),
942 feedback_service_(new DownloadFeedbackService(
943 request_context_getter
, BrowserThread::GetBlockingPool())) {
946 ui_manager_
= sb_service
->ui_manager();
947 database_manager_
= sb_service
->database_manager();
951 DownloadProtectionService::~DownloadProtectionService() {
952 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
953 CancelPendingRequests();
956 void DownloadProtectionService::SetEnabled(bool enabled
) {
957 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
958 if (enabled
== enabled_
) {
963 CancelPendingRequests();
967 void DownloadProtectionService::CheckClientDownload(
968 content::DownloadItem
* item
,
969 const CheckDownloadCallback
& callback
) {
970 scoped_refptr
<CheckClientDownloadRequest
> request(
971 new CheckClientDownloadRequest(item
, callback
, this,
973 binary_feature_extractor_
.get()));
974 download_requests_
.insert(request
);
978 void DownloadProtectionService::CheckDownloadUrl(
979 const content::DownloadItem
& item
,
980 const CheckDownloadCallback
& callback
) {
981 DCHECK(!item
.GetUrlChain().empty());
982 scoped_refptr
<DownloadUrlSBClient
> client(
983 new DownloadUrlSBClient(item
, callback
, ui_manager_
, database_manager_
));
984 // The client will release itself once it is done.
985 BrowserThread::PostTask(
988 base::Bind(&DownloadUrlSBClient::StartCheck
, client
));
991 bool DownloadProtectionService::IsSupportedDownload(
992 const content::DownloadItem
& item
,
993 const base::FilePath
& target_path
) const {
994 // Currently, the UI is only enabled on Windows. On Mac we send the ping but
995 // ignore the result (see ifdef in FinishRequest). On Linux we still
996 // want to show the dangerous file type warning if the file is possibly
997 // dangerous which means we have to always return false here.
999 DownloadCheckResultReason reason
= REASON_MAX
;
1000 ClientDownloadRequest::DownloadType type
=
1001 ClientDownloadRequest::WIN_EXECUTABLE
;
1002 return (CheckClientDownloadRequest::IsSupportedDownload(
1003 item
, target_path
, &reason
, &type
) &&
1004 (ClientDownloadRequest::CHROME_EXTENSION
!= type
));
1010 DownloadProtectionService::ClientDownloadRequestSubscription
1011 DownloadProtectionService::RegisterClientDownloadRequestCallback(
1012 const ClientDownloadRequestCallback
& callback
) {
1013 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1014 return client_download_request_callbacks_
.Add(callback
);
1017 void DownloadProtectionService::CancelPendingRequests() {
1018 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1019 for (std::set
<scoped_refptr
<CheckClientDownloadRequest
> >::iterator it
=
1020 download_requests_
.begin();
1021 it
!= download_requests_
.end();) {
1022 // We need to advance the iterator before we cancel because canceling
1023 // the request will invalidate it when RequestFinished is called below.
1024 scoped_refptr
<CheckClientDownloadRequest
> tmp
= *it
++;
1027 DCHECK(download_requests_
.empty());
1030 void DownloadProtectionService::RequestFinished(
1031 CheckClientDownloadRequest
* request
) {
1032 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1033 std::set
<scoped_refptr
<CheckClientDownloadRequest
> >::iterator it
=
1034 download_requests_
.find(request
);
1035 DCHECK(it
!= download_requests_
.end());
1036 download_requests_
.erase(*it
);
1039 void DownloadProtectionService::ShowDetailsForDownload(
1040 const content::DownloadItem
& item
,
1041 content::PageNavigator
* navigator
) {
1042 GURL
learn_more_url(chrome::kDownloadScanningLearnMoreURL
);
1043 learn_more_url
= google_util::AppendGoogleLocaleParam(
1044 learn_more_url
, g_browser_process
->GetApplicationLocale());
1046 content::OpenURLParams(learn_more_url
,
1047 content::Referrer(),
1049 ui::PAGE_TRANSITION_LINK
,
1054 // Escapes a certificate attribute so that it can be used in a whitelist
1055 // entry. Currently, we only escape slashes, since they are used as a
1056 // separator between attributes.
1057 std::string
EscapeCertAttribute(const std::string
& attribute
) {
1058 std::string escaped
;
1059 for (size_t i
= 0; i
< attribute
.size(); ++i
) {
1060 if (attribute
[i
] == '%') {
1061 escaped
.append("%25");
1062 } else if (attribute
[i
] == '/') {
1063 escaped
.append("%2F");
1065 escaped
.push_back(attribute
[i
]);
1073 void DownloadProtectionService::GetCertificateWhitelistStrings(
1074 const net::X509Certificate
& certificate
,
1075 const net::X509Certificate
& issuer
,
1076 std::vector
<std::string
>* whitelist_strings
) {
1077 // The whitelist paths are in the format:
1078 // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit]
1080 // Any of CN, O, or OU may be omitted from the whitelist entry, in which
1081 // case they match anything. However, the attributes that do appear will
1082 // always be in the order shown above. At least one attribute will always
1085 const net::CertPrincipal
& subject
= certificate
.subject();
1086 std::vector
<std::string
> ou_tokens
;
1087 for (size_t i
= 0; i
< subject
.organization_unit_names
.size(); ++i
) {
1088 ou_tokens
.push_back(
1089 "/OU=" + EscapeCertAttribute(subject
.organization_unit_names
[i
]));
1092 std::vector
<std::string
> o_tokens
;
1093 for (size_t i
= 0; i
< subject
.organization_names
.size(); ++i
) {
1095 "/O=" + EscapeCertAttribute(subject
.organization_names
[i
]));
1098 std::string cn_token
;
1099 if (!subject
.common_name
.empty()) {
1100 cn_token
= "/CN=" + EscapeCertAttribute(subject
.common_name
);
1103 std::set
<std::string
> paths_to_check
;
1104 if (!cn_token
.empty()) {
1105 paths_to_check
.insert(cn_token
);
1107 for (size_t i
= 0; i
< o_tokens
.size(); ++i
) {
1108 paths_to_check
.insert(cn_token
+ o_tokens
[i
]);
1109 paths_to_check
.insert(o_tokens
[i
]);
1110 for (size_t j
= 0; j
< ou_tokens
.size(); ++j
) {
1111 paths_to_check
.insert(cn_token
+ o_tokens
[i
] + ou_tokens
[j
]);
1112 paths_to_check
.insert(o_tokens
[i
] + ou_tokens
[j
]);
1115 for (size_t i
= 0; i
< ou_tokens
.size(); ++i
) {
1116 paths_to_check
.insert(cn_token
+ ou_tokens
[i
]);
1117 paths_to_check
.insert(ou_tokens
[i
]);
1120 std::string issuer_fp
= base::HexEncode(issuer
.fingerprint().data
,
1121 sizeof(issuer
.fingerprint().data
));
1122 for (std::set
<std::string
>::iterator it
= paths_to_check
.begin();
1123 it
!= paths_to_check
.end(); ++it
) {
1124 whitelist_strings
->push_back("cert/" + issuer_fp
+ *it
);
1129 GURL
DownloadProtectionService::GetDownloadRequestUrl() {
1130 GURL
url(kDownloadRequestUrl
);
1131 std::string api_key
= google_apis::GetAPIKey();
1132 if (!api_key
.empty())
1133 url
= url
.Resolve("?key=" + net::EscapeQueryParamValue(api_key
, true));
1138 } // namespace safe_browsing