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/histogram.h"
13 #include "base/metrics/sparse_histogram.h"
14 #include "base/sequenced_task_runner_helpers.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/threading/sequenced_worker_pool.h"
20 #include "base/time/time.h"
21 #include "chrome/browser/safe_browsing/download_feedback_service.h"
22 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
23 #include "chrome/browser/safe_browsing/sandboxed_zip_analyzer.h"
24 #include "chrome/browser/safe_browsing/signature_util.h"
25 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/browser_list.h"
27 #include "chrome/common/safe_browsing/csd.pb.h"
28 #include "chrome/common/safe_browsing/download_protection_util.h"
29 #include "chrome/common/safe_browsing/zip_analyzer.h"
30 #include "chrome/common/url_constants.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/download_item.h"
33 #include "content/public/browser/page_navigator.h"
34 #include "google_apis/google_api_keys.h"
35 #include "net/base/escape.h"
36 #include "net/base/load_flags.h"
37 #include "net/cert/x509_cert_types.h"
38 #include "net/cert/x509_certificate.h"
39 #include "net/http/http_status_code.h"
40 #include "net/url_request/url_fetcher.h"
41 #include "net/url_request/url_fetcher_delegate.h"
42 #include "net/url_request/url_request_context_getter.h"
43 #include "net/url_request/url_request_status.h"
45 using content::BrowserThread
;
48 static const int64 kDownloadRequestTimeoutMs
= 3000;
51 namespace safe_browsing
{
53 const char DownloadProtectionService::kDownloadRequestUrl
[] =
54 "https://sb-ssl.google.com/safebrowsing/clientreport/download";
57 ClientDownloadRequest::DownloadType
GetDownloadType(
58 const base::FilePath
& file
) {
59 DCHECK(download_protection_util::IsBinaryFile(file
));
60 if (file
.MatchesExtension(FILE_PATH_LITERAL(".apk")))
61 return ClientDownloadRequest::ANDROID_APK
;
62 else if (file
.MatchesExtension(FILE_PATH_LITERAL(".crx")))
63 return ClientDownloadRequest::CHROME_EXTENSION
;
64 // For zip files, we use the ZIPPED_EXECUTABLE type since we will only send
65 // the pingback if we find an executable inside the zip archive.
66 else if (file
.MatchesExtension(FILE_PATH_LITERAL(".zip")))
67 return ClientDownloadRequest::ZIPPED_EXECUTABLE
;
68 return ClientDownloadRequest::WIN_EXECUTABLE
;
71 // List of extensions for which we track some UMA stats.
72 enum MaliciousExtensionType
{
91 EXTENSION_OTHER
, // Groups all other extensions into one bucket.
97 MaliciousExtensionType
GetExtensionType(const base::FilePath
& f
) {
98 if (f
.MatchesExtension(FILE_PATH_LITERAL(".exe"))) return EXTENSION_EXE
;
99 if (f
.MatchesExtension(FILE_PATH_LITERAL(".msi"))) return EXTENSION_MSI
;
100 if (f
.MatchesExtension(FILE_PATH_LITERAL(".cab"))) return EXTENSION_CAB
;
101 if (f
.MatchesExtension(FILE_PATH_LITERAL(".sys"))) return EXTENSION_SYS
;
102 if (f
.MatchesExtension(FILE_PATH_LITERAL(".scr"))) return EXTENSION_SCR
;
103 if (f
.MatchesExtension(FILE_PATH_LITERAL(".drv"))) return EXTENSION_DRV
;
104 if (f
.MatchesExtension(FILE_PATH_LITERAL(".bat"))) return EXTENSION_BAT
;
105 if (f
.MatchesExtension(FILE_PATH_LITERAL(".zip"))) return EXTENSION_ZIP
;
106 if (f
.MatchesExtension(FILE_PATH_LITERAL(".rar"))) return EXTENSION_RAR
;
107 if (f
.MatchesExtension(FILE_PATH_LITERAL(".dll"))) return EXTENSION_DLL
;
108 if (f
.MatchesExtension(FILE_PATH_LITERAL(".pif"))) return EXTENSION_PIF
;
109 if (f
.MatchesExtension(FILE_PATH_LITERAL(".com"))) return EXTENSION_COM
;
110 if (f
.MatchesExtension(FILE_PATH_LITERAL(".jar"))) return EXTENSION_JAR
;
111 if (f
.MatchesExtension(FILE_PATH_LITERAL(".class"))) return EXTENSION_CLASS
;
112 if (f
.MatchesExtension(FILE_PATH_LITERAL(".pdf"))) return EXTENSION_PDF
;
113 if (f
.MatchesExtension(FILE_PATH_LITERAL(".vb"))) return EXTENSION_VB
;
114 if (f
.MatchesExtension(FILE_PATH_LITERAL(".reg"))) return EXTENSION_REG
;
115 if (f
.MatchesExtension(FILE_PATH_LITERAL(".grp"))) return EXTENSION_GRP
;
116 if (f
.MatchesExtension(FILE_PATH_LITERAL(".crx"))) return EXTENSION_CRX
;
117 if (f
.MatchesExtension(FILE_PATH_LITERAL(".apk"))) return EXTENSION_APK
;
118 return EXTENSION_OTHER
;
121 void RecordFileExtensionType(const base::FilePath
& file
) {
122 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadExtensions",
123 GetExtensionType(file
),
127 // Enumerate for histogramming purposes.
128 // DO NOT CHANGE THE ORDERING OF THESE VALUES (different histogram data will
129 // be mixed together based on their values).
131 DOWNLOAD_URL_CHECKS_TOTAL
,
132 DOWNLOAD_URL_CHECKS_CANCELED
,
133 DOWNLOAD_URL_CHECKS_MALWARE
,
135 DOWNLOAD_HASH_CHECKS_TOTAL
,
136 DOWNLOAD_HASH_CHECKS_MALWARE
,
138 // Memory space for histograms is determined by the max.
139 // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
144 // Parent SafeBrowsing::Client class used to lookup the bad binary
145 // URL and digest list. There are two sub-classes (one for each list).
146 class DownloadSBClient
147 : public SafeBrowsingDatabaseManager::Client
,
148 public base::RefCountedThreadSafe
<DownloadSBClient
> {
151 const content::DownloadItem
& item
,
152 const DownloadProtectionService::CheckDownloadCallback
& callback
,
153 const scoped_refptr
<SafeBrowsingUIManager
>& ui_manager
,
154 SBStatsType total_type
,
155 SBStatsType dangerous_type
)
156 : sha256_hash_(item
.GetHash()),
157 url_chain_(item
.GetUrlChain()),
158 referrer_url_(item
.GetReferrerUrl()),
160 ui_manager_(ui_manager
),
161 start_time_(base::TimeTicks::Now()),
162 total_type_(total_type
),
163 dangerous_type_(dangerous_type
) {}
165 virtual void StartCheck() = 0;
166 virtual bool IsDangerous(SBThreatType threat_type
) const = 0;
169 friend class base::RefCountedThreadSafe
<DownloadSBClient
>;
170 virtual ~DownloadSBClient() {}
172 void CheckDone(SBThreatType threat_type
) {
173 DownloadProtectionService::DownloadCheckResult result
=
174 IsDangerous(threat_type
) ?
175 DownloadProtectionService::DANGEROUS
:
176 DownloadProtectionService::SAFE
;
177 BrowserThread::PostTask(BrowserThread::UI
,
179 base::Bind(callback_
, result
));
180 UpdateDownloadCheckStats(total_type_
);
181 if (threat_type
!= SB_THREAT_TYPE_SAFE
) {
182 UpdateDownloadCheckStats(dangerous_type_
);
183 BrowserThread::PostTask(
186 base::Bind(&DownloadSBClient::ReportMalware
,
191 void ReportMalware(SBThreatType threat_type
) {
192 std::string post_data
;
193 if (!sha256_hash_
.empty())
194 post_data
+= base::HexEncode(sha256_hash_
.data(),
195 sha256_hash_
.size()) + "\n";
196 for (size_t i
= 0; i
< url_chain_
.size(); ++i
) {
197 post_data
+= url_chain_
[i
].spec() + "\n";
199 ui_manager_
->ReportSafeBrowsingHit(
200 url_chain_
.back(), // malicious_url
201 url_chain_
.front(), // page_url
203 true, // is_subresource
208 void UpdateDownloadCheckStats(SBStatsType stat_type
) {
209 UMA_HISTOGRAM_ENUMERATION("SB2.DownloadChecks",
211 DOWNLOAD_CHECKS_MAX
);
214 std::string sha256_hash_
;
215 std::vector
<GURL
> url_chain_
;
217 DownloadProtectionService::CheckDownloadCallback callback_
;
218 scoped_refptr
<SafeBrowsingUIManager
> ui_manager_
;
219 base::TimeTicks start_time_
;
222 const SBStatsType total_type_
;
223 const SBStatsType dangerous_type_
;
225 DISALLOW_COPY_AND_ASSIGN(DownloadSBClient
);
228 class DownloadUrlSBClient
: public DownloadSBClient
{
231 const content::DownloadItem
& item
,
232 const DownloadProtectionService::CheckDownloadCallback
& callback
,
233 const scoped_refptr
<SafeBrowsingUIManager
>& ui_manager
,
234 const scoped_refptr
<SafeBrowsingDatabaseManager
>& database_manager
)
235 : DownloadSBClient(item
, callback
, ui_manager
,
236 DOWNLOAD_URL_CHECKS_TOTAL
,
237 DOWNLOAD_URL_CHECKS_MALWARE
),
238 database_manager_(database_manager
) { }
240 virtual void StartCheck() OVERRIDE
{
241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
242 if (!database_manager_
.get() ||
243 database_manager_
->CheckDownloadUrl(url_chain_
, this)) {
244 CheckDone(SB_THREAT_TYPE_SAFE
);
246 AddRef(); // SafeBrowsingService takes a pointer not a scoped_refptr.
250 virtual bool IsDangerous(SBThreatType threat_type
) const OVERRIDE
{
251 return threat_type
== SB_THREAT_TYPE_BINARY_MALWARE_URL
;
254 virtual void OnCheckDownloadUrlResult(const std::vector
<GURL
>& url_chain
,
255 SBThreatType threat_type
) OVERRIDE
{
256 CheckDone(threat_type
);
257 UMA_HISTOGRAM_TIMES("SB2.DownloadUrlCheckDuration",
258 base::TimeTicks::Now() - start_time_
);
263 virtual ~DownloadUrlSBClient() {}
266 scoped_refptr
<SafeBrowsingDatabaseManager
> database_manager_
;
268 DISALLOW_COPY_AND_ASSIGN(DownloadUrlSBClient
);
271 class DownloadProtectionService::CheckClientDownloadRequest
272 : public base::RefCountedThreadSafe
<
273 DownloadProtectionService::CheckClientDownloadRequest
,
274 BrowserThread::DeleteOnUIThread
>,
275 public net::URLFetcherDelegate
,
276 public content::DownloadItem::Observer
{
278 CheckClientDownloadRequest(
279 content::DownloadItem
* item
,
280 const CheckDownloadCallback
& callback
,
281 DownloadProtectionService
* service
,
282 const scoped_refptr
<SafeBrowsingDatabaseManager
>& database_manager
,
283 SignatureUtil
* signature_util
)
285 url_chain_(item
->GetUrlChain()),
286 referrer_url_(item
->GetReferrerUrl()),
287 zipped_executable_(false),
290 signature_util_(signature_util
),
291 database_manager_(database_manager
),
292 pingback_enabled_(service_
->enabled()),
294 type_(ClientDownloadRequest::WIN_EXECUTABLE
),
295 weakptr_factory_(this),
296 start_time_(base::TimeTicks::Now()) {
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
298 item_
->AddObserver(this);
302 VLOG(2) << "Starting SafeBrowsing download check for: "
303 << item_
->DebugString(true);
304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
305 // TODO(noelutz): implement some cache to make sure we don't issue the same
306 // request over and over again if a user downloads the same binary multiple
308 DownloadCheckResultReason reason
= REASON_MAX
;
309 if (!IsSupportedDownload(
310 *item_
, item_
->GetTargetFilePath(), &reason
, &type_
)) {
312 case REASON_EMPTY_URL_CHAIN
:
313 case REASON_INVALID_URL
:
314 PostFinishTask(SAFE
, reason
);
317 case REASON_NOT_BINARY_FILE
:
318 RecordFileExtensionType(item_
->GetTargetFilePath());
319 PostFinishTask(SAFE
, reason
);
323 // We only expect the reasons explicitly handled above.
327 RecordFileExtensionType(item_
->GetTargetFilePath());
329 // Compute features from the file contents. Note that we record histograms
330 // based on the result, so this runs regardless of whether the pingbacks
332 if (item_
->GetTargetFilePath().MatchesExtension(
333 FILE_PATH_LITERAL(".zip"))) {
334 StartExtractZipFeatures();
336 DCHECK(!download_protection_util::IsArchiveFile(
337 item_
->GetTargetFilePath()));
338 StartExtractSignatureFeatures();
342 // Start a timeout to cancel the request if it takes too long.
343 // This should only be called after we have finished accessing the file.
344 void StartTimeout() {
345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
347 // Request has already been cancelled.
350 BrowserThread::PostDelayedTask(
353 base::Bind(&CheckClientDownloadRequest::Cancel
,
354 weakptr_factory_
.GetWeakPtr()),
355 base::TimeDelta::FromMilliseconds(
356 service_
->download_request_timeout_ms()));
359 // Canceling a request will cause us to always report the result as SAFE
360 // unless a pending request is about to call FinishRequest.
362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
363 if (fetcher_
.get()) {
364 // The DownloadProtectionService is going to release its reference, so we
365 // might be destroyed before the URLFetcher completes. Cancel the
366 // fetcher so it does not try to invoke OnURLFetchComplete.
369 // Note: If there is no fetcher, then some callback is still holding a
370 // reference to this object. We'll eventually wind up in some method on
371 // the UI thread that will call FinishRequest() again. If FinishRequest()
372 // is called a second time, it will be a no-op.
373 FinishRequest(SAFE
, REASON_REQUEST_CANCELED
);
374 // Calling FinishRequest might delete this object, we may be deleted by
378 // content::DownloadItem::Observer implementation.
379 virtual void OnDownloadDestroyed(content::DownloadItem
* download
) OVERRIDE
{
381 DCHECK(item_
== NULL
);
384 // From the net::URLFetcherDelegate interface.
385 virtual void OnURLFetchComplete(const net::URLFetcher
* source
) OVERRIDE
{
386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
387 DCHECK_EQ(source
, fetcher_
.get());
388 VLOG(2) << "Received a response for URL: "
389 << item_
->GetUrlChain().back() << ": success="
390 << source
->GetStatus().is_success() << " response_code="
391 << source
->GetResponseCode();
392 if (source
->GetStatus().is_success()) {
393 UMA_HISTOGRAM_SPARSE_SLOWLY(
394 "SBClientDownload.DownloadRequestResponseCode",
395 source
->GetResponseCode());
397 UMA_HISTOGRAM_SPARSE_SLOWLY(
398 "SBClientDownload.DownloadRequestNetError",
399 -source
->GetStatus().error());
400 DownloadCheckResultReason reason
= REASON_SERVER_PING_FAILED
;
401 DownloadCheckResult result
= SAFE
;
402 if (source
->GetStatus().is_success() &&
403 net::HTTP_OK
== source
->GetResponseCode()) {
404 ClientDownloadResponse response
;
406 bool got_data
= source
->GetResponseAsString(&data
);
408 if (!response
.ParseFromString(data
)) {
409 reason
= REASON_INVALID_RESPONSE_PROTO
;
410 } else if (response
.verdict() == ClientDownloadResponse::SAFE
) {
411 reason
= REASON_DOWNLOAD_SAFE
;
412 } else if (service_
&& !service_
->IsSupportedDownload(
413 *item_
, item_
->GetTargetFilePath())) {
414 // The client of the download protection service assumes that we don't
415 // support this download so we cannot return any other verdict than
416 // SAFE even if the server says it's dangerous to download this file.
417 // Note: if service_ is NULL we already cancelled the request and
419 reason
= REASON_DOWNLOAD_NOT_SUPPORTED
;
420 } else if (response
.verdict() == ClientDownloadResponse::DANGEROUS
) {
421 reason
= REASON_DOWNLOAD_DANGEROUS
;
423 } else if (response
.verdict() == ClientDownloadResponse::UNCOMMON
) {
424 reason
= REASON_DOWNLOAD_UNCOMMON
;
426 } else if (response
.verdict() == ClientDownloadResponse::DANGEROUS_HOST
) {
427 reason
= REASON_DOWNLOAD_DANGEROUS_HOST
;
428 result
= DANGEROUS_HOST
;
430 response
.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED
) {
431 reason
= REASON_DOWNLOAD_POTENTIALLY_UNWANTED
;
432 result
= POTENTIALLY_UNWANTED
;
434 LOG(DFATAL
) << "Unknown download response verdict: "
435 << response
.verdict();
436 reason
= REASON_INVALID_RESPONSE_VERDICT
;
438 DownloadFeedbackService::MaybeStorePingsForDownload(
439 result
, item_
, client_download_request_data_
, data
);
441 // We don't need the fetcher anymore.
443 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration",
444 base::TimeTicks::Now() - start_time_
);
445 FinishRequest(result
, reason
);
448 static bool IsSupportedDownload(const content::DownloadItem
& item
,
449 const base::FilePath
& target_path
,
450 DownloadCheckResultReason
* reason
,
451 ClientDownloadRequest::DownloadType
* type
) {
452 if (item
.GetUrlChain().empty()) {
453 *reason
= REASON_EMPTY_URL_CHAIN
;
456 const GURL
& final_url
= item
.GetUrlChain().back();
457 if (!final_url
.is_valid() || final_url
.is_empty() ||
458 !final_url
.IsStandard() || final_url
.SchemeIsFile()) {
459 *reason
= REASON_INVALID_URL
;
462 if (!download_protection_util::IsBinaryFile(target_path
)) {
463 *reason
= REASON_NOT_BINARY_FILE
;
466 *type
= GetDownloadType(target_path
);
471 friend struct BrowserThread::DeleteOnThread
<BrowserThread::UI
>;
472 friend class base::DeleteHelper
<CheckClientDownloadRequest
>;
474 virtual ~CheckClientDownloadRequest() {
475 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
476 DCHECK(item_
== NULL
);
479 void OnFileFeatureExtractionDone() {
480 // This can run in any thread, since it just posts more messages.
482 // TODO(noelutz): DownloadInfo should also contain the IP address of
483 // every URL in the redirect chain. We also should check whether the
484 // download URL is hosted on the internal network.
485 BrowserThread::PostTask(
488 base::Bind(&CheckClientDownloadRequest::CheckWhitelists
, this));
490 // We wait until after the file checks finish to start the timeout, as
491 // windows can cause permissions errors if the timeout fired while we were
492 // checking the file signature and we tried to complete the download.
493 BrowserThread::PostTask(
496 base::Bind(&CheckClientDownloadRequest::StartTimeout
, this));
499 void StartExtractSignatureFeatures() {
500 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
501 DCHECK(item_
); // Called directly from Start(), item should still exist.
502 // Since we do blocking I/O, offload this to a worker thread.
503 // The task does not need to block shutdown.
504 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
506 base::Bind(&CheckClientDownloadRequest::ExtractSignatureFeatures
,
507 this, item_
->GetFullPath()),
508 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
511 void ExtractSignatureFeatures(const base::FilePath
& file_path
) {
512 base::TimeTicks start_time
= base::TimeTicks::Now();
513 signature_util_
->CheckSignature(file_path
, &signature_info_
);
514 bool is_signed
= (signature_info_
.certificate_chain_size() > 0);
516 VLOG(2) << "Downloaded a signed binary: " << file_path
.value();
518 VLOG(2) << "Downloaded an unsigned binary: "
519 << file_path
.value();
521 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed
);
522 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime",
523 base::TimeTicks::Now() - start_time
);
525 OnFileFeatureExtractionDone();
528 void StartExtractZipFeatures() {
529 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
530 DCHECK(item_
); // Called directly from Start(), item should still exist.
531 zip_analysis_start_time_
= base::TimeTicks::Now();
532 // We give the zip analyzer a weak pointer to this object. Since the
533 // analyzer is refcounted, it might outlive the request.
534 analyzer_
= new SandboxedZipAnalyzer(
535 item_
->GetFullPath(),
536 base::Bind(&CheckClientDownloadRequest::OnZipAnalysisFinished
,
537 weakptr_factory_
.GetWeakPtr()));
541 void OnZipAnalysisFinished(const zip_analyzer::Results
& results
) {
542 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
545 if (results
.success
) {
546 zipped_executable_
= results
.has_executable
;
547 VLOG(1) << "Zip analysis finished for " << item_
->GetFullPath().value()
548 << ", has_executable=" << results
.has_executable
549 << " has_archive=" << results
.has_archive
;
551 VLOG(1) << "Zip analysis failed for " << item_
->GetFullPath().value();
553 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
555 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable",
556 results
.has_archive
&& !zipped_executable_
);
557 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime",
558 base::TimeTicks::Now() - zip_analysis_start_time_
);
560 if (!zipped_executable_
) {
561 PostFinishTask(SAFE
, REASON_ARCHIVE_WITHOUT_BINARIES
);
564 OnFileFeatureExtractionDone();
567 void CheckWhitelists() {
568 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
569 DownloadCheckResultReason reason
= REASON_MAX
;
570 if (!database_manager_
.get()) {
571 reason
= REASON_SB_DISABLED
;
573 const GURL
& url
= url_chain_
.back();
574 if (url
.is_valid() && database_manager_
->MatchDownloadWhitelistUrl(url
)) {
575 VLOG(2) << url
<< " is on the download whitelist.";
576 reason
= REASON_WHITELISTED_URL
;
578 if (reason
!= REASON_MAX
|| signature_info_
.trusted()) {
579 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
582 if (reason
== REASON_MAX
&& signature_info_
.trusted()) {
583 for (int i
= 0; i
< signature_info_
.certificate_chain_size(); ++i
) {
584 if (CertificateChainIsWhitelisted(
585 signature_info_
.certificate_chain(i
))) {
586 reason
= REASON_TRUSTED_EXECUTABLE
;
591 if (reason
!= REASON_MAX
) {
592 PostFinishTask(SAFE
, reason
);
593 } else if (!pingback_enabled_
) {
594 PostFinishTask(SAFE
, REASON_PING_DISABLED
);
596 // Currently, the UI only works on Windows so we don't even bother
597 // with pinging the server if we're not on Windows. TODO(noelutz):
598 // change this code once the UI is done for Linux and Mac.
600 // The URLFetcher is owned by the UI thread, so post a message to
601 // start the pingback.
602 BrowserThread::PostTask(
605 base::Bind(&CheckClientDownloadRequest::SendRequest
, this));
607 PostFinishTask(SAFE
, REASON_OS_NOT_SUPPORTED
);
613 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
615 // This is our last chance to check whether the request has been canceled
616 // before sending it.
620 ClientDownloadRequest request
;
621 request
.set_url(item_
->GetUrlChain().back().spec());
622 request
.mutable_digests()->set_sha256(item_
->GetHash());
623 request
.set_length(item_
->GetReceivedBytes());
624 for (size_t i
= 0; i
< item_
->GetUrlChain().size(); ++i
) {
625 ClientDownloadRequest::Resource
* resource
= request
.add_resources();
626 resource
->set_url(item_
->GetUrlChain()[i
].spec());
627 if (i
== item_
->GetUrlChain().size() - 1) {
628 // The last URL in the chain is the download URL.
629 resource
->set_type(ClientDownloadRequest::DOWNLOAD_URL
);
630 resource
->set_referrer(item_
->GetReferrerUrl().spec());
631 if (!item_
->GetRemoteAddress().empty()) {
632 resource
->set_remote_ip(item_
->GetRemoteAddress());
635 resource
->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT
);
637 // TODO(noelutz): fill out the remote IP addresses.
639 request
.set_user_initiated(item_
->HasUserGesture());
640 request
.set_file_basename(
641 item_
->GetTargetFilePath().BaseName().AsUTF8Unsafe());
642 request
.set_download_type(type_
);
643 request
.mutable_signature()->CopyFrom(signature_info_
);
644 if (!request
.SerializeToString(&client_download_request_data_
)) {
645 FinishRequest(SAFE
, REASON_INVALID_REQUEST_PROTO
);
649 VLOG(2) << "Sending a request for URL: "
650 << item_
->GetUrlChain().back();
651 fetcher_
.reset(net::URLFetcher::Create(0 /* ID used for testing */,
652 GetDownloadRequestUrl(),
653 net::URLFetcher::POST
,
655 fetcher_
->SetLoadFlags(net::LOAD_DISABLE_CACHE
);
656 fetcher_
->SetAutomaticallyRetryOn5xx(false); // Don't retry on error.
657 fetcher_
->SetRequestContext(service_
->request_context_getter_
.get());
658 fetcher_
->SetUploadData("application/octet-stream",
659 client_download_request_data_
);
660 UMA_HISTOGRAM_COUNTS("SBClientDownload.DownloadRequestPayloadSize",
661 client_download_request_data_
.size());
665 void PostFinishTask(DownloadCheckResult result
,
666 DownloadCheckResultReason reason
) {
667 BrowserThread::PostTask(
670 base::Bind(&CheckClientDownloadRequest::FinishRequest
, this, result
,
674 void FinishRequest(DownloadCheckResult result
,
675 DownloadCheckResultReason reason
) {
676 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
681 // Ensure the timeout task is cancelled while we still have a non-zero
682 // refcount. (crbug.com/240449)
683 weakptr_factory_
.InvalidateWeakPtrs();
685 VLOG(2) << "SafeBrowsing download verdict for: "
686 << item_
->DebugString(true) << " verdict:" << reason
;
687 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
690 callback_
.Run(result
);
691 item_
->RemoveObserver(this);
693 DownloadProtectionService
* service
= service_
;
695 service
->RequestFinished(this);
696 // DownloadProtectionService::RequestFinished will decrement our refcount,
697 // so we may be deleted now.
703 bool CertificateChainIsWhitelisted(
704 const ClientDownloadRequest_CertificateChain
& chain
) {
705 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
706 if (chain
.element_size() < 2) {
707 // We need to have both a signing certificate and its issuer certificate
708 // present to construct a whitelist entry.
711 scoped_refptr
<net::X509Certificate
> cert
=
712 net::X509Certificate::CreateFromBytes(
713 chain
.element(0).certificate().data(),
714 chain
.element(0).certificate().size());
719 for (int i
= 1; i
< chain
.element_size(); ++i
) {
720 scoped_refptr
<net::X509Certificate
> issuer
=
721 net::X509Certificate::CreateFromBytes(
722 chain
.element(i
).certificate().data(),
723 chain
.element(i
).certificate().size());
727 std::vector
<std::string
> whitelist_strings
;
728 DownloadProtectionService::GetCertificateWhitelistStrings(
729 *cert
.get(), *issuer
.get(), &whitelist_strings
);
730 for (size_t j
= 0; j
< whitelist_strings
.size(); ++j
) {
731 if (database_manager_
->MatchDownloadWhitelistString(
732 whitelist_strings
[j
])) {
733 VLOG(2) << "Certificate matched whitelist, cert="
734 << cert
->subject().GetDisplayName()
735 << " issuer=" << issuer
->subject().GetDisplayName();
744 // The DownloadItem we are checking. Will be NULL if the request has been
745 // canceled. Must be accessed only on UI thread.
746 content::DownloadItem
* item_
;
747 // Copies of data from |item_| for access on other threads.
748 std::vector
<GURL
> url_chain_
;
751 bool zipped_executable_
;
752 ClientDownloadRequest_SignatureInfo signature_info_
;
753 CheckDownloadCallback callback_
;
754 // Will be NULL if the request has been canceled.
755 DownloadProtectionService
* service_
;
756 scoped_refptr
<SignatureUtil
> signature_util_
;
757 scoped_refptr
<SafeBrowsingDatabaseManager
> database_manager_
;
758 const bool pingback_enabled_
;
759 scoped_ptr
<net::URLFetcher
> fetcher_
;
760 scoped_refptr
<SandboxedZipAnalyzer
> analyzer_
;
761 base::TimeTicks zip_analysis_start_time_
;
763 ClientDownloadRequest::DownloadType type_
;
764 std::string client_download_request_data_
;
765 base::WeakPtrFactory
<CheckClientDownloadRequest
> weakptr_factory_
;
766 base::TimeTicks start_time_
; // Used for stats.
768 DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest
);
771 DownloadProtectionService::DownloadProtectionService(
772 SafeBrowsingService
* sb_service
,
773 net::URLRequestContextGetter
* request_context_getter
)
774 : request_context_getter_(request_context_getter
),
776 signature_util_(new SignatureUtil()),
777 download_request_timeout_ms_(kDownloadRequestTimeoutMs
),
778 feedback_service_(new DownloadFeedbackService(
779 request_context_getter
, BrowserThread::GetBlockingPool())) {
782 ui_manager_
= sb_service
->ui_manager();
783 database_manager_
= sb_service
->database_manager();
787 DownloadProtectionService::~DownloadProtectionService() {
788 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
789 CancelPendingRequests();
792 void DownloadProtectionService::SetEnabled(bool enabled
) {
793 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
794 if (enabled
== enabled_
) {
799 CancelPendingRequests();
803 void DownloadProtectionService::CheckClientDownload(
804 content::DownloadItem
* item
,
805 const CheckDownloadCallback
& callback
) {
806 scoped_refptr
<CheckClientDownloadRequest
> request(
807 new CheckClientDownloadRequest(item
, callback
, this,
808 database_manager_
, signature_util_
.get()));
809 download_requests_
.insert(request
);
813 void DownloadProtectionService::CheckDownloadUrl(
814 const content::DownloadItem
& item
,
815 const CheckDownloadCallback
& callback
) {
816 DCHECK(!item
.GetUrlChain().empty());
817 scoped_refptr
<DownloadUrlSBClient
> client(
818 new DownloadUrlSBClient(item
, callback
, ui_manager_
, database_manager_
));
819 // The client will release itself once it is done.
820 BrowserThread::PostTask(
823 base::Bind(&DownloadUrlSBClient::StartCheck
, client
));
826 bool DownloadProtectionService::IsSupportedDownload(
827 const content::DownloadItem
& item
,
828 const base::FilePath
& target_path
) const {
829 // Currently, the UI only works on Windows. On Linux and Mac we still
830 // want to show the dangerous file type warning if the file is possibly
831 // dangerous which means we have to always return false here.
833 DownloadCheckResultReason reason
= REASON_MAX
;
834 ClientDownloadRequest::DownloadType type
=
835 ClientDownloadRequest::WIN_EXECUTABLE
;
836 return (CheckClientDownloadRequest::IsSupportedDownload(item
, target_path
,
838 (ClientDownloadRequest::ANDROID_APK
== type
||
839 ClientDownloadRequest::WIN_EXECUTABLE
== type
||
840 ClientDownloadRequest::ZIPPED_EXECUTABLE
== type
));
846 void DownloadProtectionService::CancelPendingRequests() {
847 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
848 for (std::set
<scoped_refptr
<CheckClientDownloadRequest
> >::iterator it
=
849 download_requests_
.begin();
850 it
!= download_requests_
.end();) {
851 // We need to advance the iterator before we cancel because canceling
852 // the request will invalidate it when RequestFinished is called below.
853 scoped_refptr
<CheckClientDownloadRequest
> tmp
= *it
++;
856 DCHECK(download_requests_
.empty());
859 void DownloadProtectionService::RequestFinished(
860 CheckClientDownloadRequest
* request
) {
861 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
862 std::set
<scoped_refptr
<CheckClientDownloadRequest
> >::iterator it
=
863 download_requests_
.find(request
);
864 DCHECK(it
!= download_requests_
.end());
865 download_requests_
.erase(*it
);
868 void DownloadProtectionService::ShowDetailsForDownload(
869 const content::DownloadItem
& item
,
870 content::PageNavigator
* navigator
) {
871 GURL
learn_more_url(chrome::kDownloadScanningLearnMoreURL
);
873 content::OpenURLParams(learn_more_url
,
876 content::PAGE_TRANSITION_LINK
,
881 // Escapes a certificate attribute so that it can be used in a whitelist
882 // entry. Currently, we only escape slashes, since they are used as a
883 // separator between attributes.
884 std::string
EscapeCertAttribute(const std::string
& attribute
) {
886 for (size_t i
= 0; i
< attribute
.size(); ++i
) {
887 if (attribute
[i
] == '%') {
888 escaped
.append("%25");
889 } else if (attribute
[i
] == '/') {
890 escaped
.append("%2F");
892 escaped
.push_back(attribute
[i
]);
900 void DownloadProtectionService::GetCertificateWhitelistStrings(
901 const net::X509Certificate
& certificate
,
902 const net::X509Certificate
& issuer
,
903 std::vector
<std::string
>* whitelist_strings
) {
904 // The whitelist paths are in the format:
905 // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit]
907 // Any of CN, O, or OU may be omitted from the whitelist entry, in which
908 // case they match anything. However, the attributes that do appear will
909 // always be in the order shown above. At least one attribute will always
912 const net::CertPrincipal
& subject
= certificate
.subject();
913 std::vector
<std::string
> ou_tokens
;
914 for (size_t i
= 0; i
< subject
.organization_unit_names
.size(); ++i
) {
916 "/OU=" + EscapeCertAttribute(subject
.organization_unit_names
[i
]));
919 std::vector
<std::string
> o_tokens
;
920 for (size_t i
= 0; i
< subject
.organization_names
.size(); ++i
) {
922 "/O=" + EscapeCertAttribute(subject
.organization_names
[i
]));
925 std::string cn_token
;
926 if (!subject
.common_name
.empty()) {
927 cn_token
= "/CN=" + EscapeCertAttribute(subject
.common_name
);
930 std::set
<std::string
> paths_to_check
;
931 if (!cn_token
.empty()) {
932 paths_to_check
.insert(cn_token
);
934 for (size_t i
= 0; i
< o_tokens
.size(); ++i
) {
935 paths_to_check
.insert(cn_token
+ o_tokens
[i
]);
936 paths_to_check
.insert(o_tokens
[i
]);
937 for (size_t j
= 0; j
< ou_tokens
.size(); ++j
) {
938 paths_to_check
.insert(cn_token
+ o_tokens
[i
] + ou_tokens
[j
]);
939 paths_to_check
.insert(o_tokens
[i
] + ou_tokens
[j
]);
942 for (size_t i
= 0; i
< ou_tokens
.size(); ++i
) {
943 paths_to_check
.insert(cn_token
+ ou_tokens
[i
]);
944 paths_to_check
.insert(ou_tokens
[i
]);
947 std::string issuer_fp
= base::HexEncode(issuer
.fingerprint().data
,
948 sizeof(issuer
.fingerprint().data
));
949 for (std::set
<std::string
>::iterator it
= paths_to_check
.begin();
950 it
!= paths_to_check
.end(); ++it
) {
951 whitelist_strings
->push_back("cert/" + issuer_fp
+ *it
);
956 GURL
DownloadProtectionService::GetDownloadRequestUrl() {
957 GURL
url(kDownloadRequestUrl
);
958 std::string api_key
= google_apis::GetAPIKey();
959 if (!api_key
.empty())
960 url
= url
.Resolve("?key=" + net::EscapeQueryParamValue(api_key
, true));
965 } // namespace safe_browsing