Refactor WebsiteSettings to operate on a SecurityInfo
[chromium-blink-merge.git] / chrome / browser / safe_browsing / download_protection_service.cc
blob5a27937e104cfcca7b07090d0aa210e3de19605d
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/safe_browsing/download_protection_service.h"
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "base/format_macros.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/metrics/histogram.h"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/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"
59 #endif
61 using content::BrowserThread;
63 namespace {
64 static const int64 kDownloadRequestTimeoutMs = 7000;
65 } // namespace
67 namespace safe_browsing {
69 const char DownloadProtectionService::kDownloadRequestUrl[] =
70 "https://sb-ssl.google.com/safebrowsing/clientreport/download";
72 namespace {
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).
92 enum SBStatsType {
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.
102 DOWNLOAD_CHECKS_MAX
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());
117 return spec;
120 } // namespace
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> {
127 public:
128 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()),
137 callback_(callback),
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;
151 protected:
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,
161 FROM_HERE,
162 base::Bind(callback_, result));
163 UpdateDownloadCheckStats(total_type_);
164 if (threat_type != SB_THREAT_TYPE_SAFE) {
165 UpdateDownloadCheckStats(dangerous_type_);
166 BrowserThread::PostTask(
167 BrowserThread::UI,
168 FROM_HERE,
169 base::Bind(&DownloadSBClient::ReportMalware,
170 this, threat_type));
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
184 referrer_url_,
185 true, // is_subresource
186 threat_type,
187 post_data,
188 is_extended_reporting_);
191 void UpdateDownloadCheckStats(SBStatsType stat_type) {
192 UMA_HISTOGRAM_ENUMERATION("SB2.DownloadChecks",
193 stat_type,
194 DOWNLOAD_CHECKS_MAX);
197 std::string sha256_hash_;
198 std::vector<GURL> url_chain_;
199 GURL referrer_url_;
200 DownloadProtectionService::CheckDownloadCallback callback_;
201 scoped_refptr<SafeBrowsingUIManager> ui_manager_;
202 base::TimeTicks start_time_;
204 private:
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 {
213 public:
214 DownloadUrlSBClient(
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);
229 } else {
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_);
243 Release();
246 protected:
247 ~DownloadUrlSBClient() override {}
249 private:
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 {
261 public:
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)
268 : item_(item),
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),
274 callback_(callback),
275 service_(service),
276 binary_feature_extractor_(binary_feature_extractor),
277 database_manager_(database_manager),
278 pingback_enabled_(service_->enabled()),
279 finished_(false),
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);
287 void Start() {
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
293 // times.
294 DownloadCheckResultReason reason = REASON_MAX;
295 if (!IsSupportedDownload(
296 *item_, item_->GetTargetFilePath(), &reason, &type_)) {
297 switch (reason) {
298 case REASON_EMPTY_URL_CHAIN:
299 case REASON_INVALID_URL:
300 case REASON_UNSUPPORTED_URL_SCHEME:
301 PostFinishTask(UNKNOWN, reason);
302 return;
304 case REASON_NOT_BINARY_FILE:
305 RecordFileExtensionType(item_->GetTargetFilePath());
306 PostFinishTask(UNKNOWN, reason);
307 return;
309 default:
310 // We only expect the reasons explicitly handled above.
311 NOTREACHED();
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
318 // are enabled.
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();
326 #endif
327 } else {
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);
336 if (!service_) {
337 // Request has already been cancelled.
338 return;
340 timeout_start_time_ = base::TimeTicks::Now();
341 BrowserThread::PostDelayedTask(
342 BrowserThread::UI,
343 FROM_HERE,
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.
352 void Cancel() {
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.
358 fetcher_.reset();
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
366 // this point.
369 // content::DownloadItem::Observer implementation.
370 void OnDownloadDestroyed(content::DownloadItem* download) override {
371 Cancel();
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;
396 std::string data;
397 bool got_data = source->GetResponseAsString(&data);
398 DCHECK(got_data);
399 if (!response.ParseFromString(data)) {
400 reason = REASON_INVALID_RESPONSE_PROTO;
401 result = UNKNOWN;
402 } else if (response.verdict() == ClientDownloadResponse::SAFE) {
403 reason = REASON_DOWNLOAD_SAFE;
404 result = 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
411 // returned UNKNOWN.
412 reason = REASON_DOWNLOAD_NOT_SUPPORTED;
413 result = UNKNOWN;
414 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS) {
415 reason = REASON_DOWNLOAD_DANGEROUS;
416 result = DANGEROUS;
417 } else if (response.verdict() == ClientDownloadResponse::UNCOMMON) {
418 reason = REASON_DOWNLOAD_UNCOMMON;
419 result = UNCOMMON;
420 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS_HOST) {
421 reason = REASON_DOWNLOAD_DANGEROUS_HOST;
422 result = DANGEROUS_HOST;
423 } else if (
424 response.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED) {
425 reason = REASON_DOWNLOAD_POTENTIALLY_UNWANTED;
426 result = POTENTIALLY_UNWANTED;
427 } else {
428 LOG(DFATAL) << "Unknown download response verdict: "
429 << response.verdict();
430 reason = REASON_INVALID_RESPONSE_VERDICT;
431 result = UNKNOWN;
433 DownloadFeedbackService::MaybeStorePingsForDownload(
434 result, item_, client_download_request_data_, data);
436 // We don't need the fetcher anymore.
437 fetcher_.reset();
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;
451 return false;
453 const GURL& final_url = item.GetUrlChain().back();
454 if (!final_url.is_valid() || final_url.is_empty()) {
455 *reason = REASON_INVALID_URL;
456 return false;
458 if (!download_protection_util::IsSupportedBinaryFile(target_path)) {
459 *reason = REASON_NOT_BINARY_FILE;
460 return false;
462 if ((!final_url.IsStandard() && !final_url.SchemeIsBlob() &&
463 !final_url.SchemeIs(url::kDataScheme)) ||
464 final_url.SchemeIsFile()) {
465 *reason = REASON_UNSUPPORTED_URL_SCHEME;
466 return false;
468 *type = download_protection_util::GetDownloadType(target_path);
469 return true;
472 private:
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(
488 BrowserThread::IO,
489 FROM_HERE,
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(
496 BrowserThread::UI,
497 FROM_HERE,
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(
507 FROM_HERE,
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);
517 if (is_signed) {
518 DVLOG(2) << "Downloaded a signed binary: " << file_path.value();
519 } else {
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(
530 file_path,
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()));
552 analyzer_->Start();
555 void OnZipAnalysisFinished(const zip_analyzer::Results& results) {
556 DCHECK_CURRENTLY_ON(BrowserThread::UI);
557 DCHECK_EQ(ClientDownloadRequest::ZIPPED_EXECUTABLE, type_);
558 if (!service_)
559 return;
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;
566 } else {
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);
580 return;
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);
591 DCHECK(item_);
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_);
603 if (!service_)
604 return;
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;
612 } else {
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);
624 return;
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);
640 return;
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
650 // download.
651 PostFinishTask(SAFE, REASON_WHITELISTED_URL);
652 return;
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
662 // safe download.
663 PostFinishTask(SAFE, REASON_TRUSTED_EXECUTABLE);
664 return;
669 if (!pingback_enabled_) {
670 PostFinishTask(UNKNOWN, REASON_PING_DISABLED);
671 return;
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(
681 BrowserThread::UI,
682 FROM_HERE,
683 base::Bind(&CheckClientDownloadRequest::GetTabRedirects, this));
684 #else
685 PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED);
686 #endif
689 void GetTabRedirects() {
690 DCHECK_CURRENTLY_ON(BrowserThread::UI);
691 if (!service_)
692 return;
694 if (!tab_url_.is_valid()) {
695 SendRequest();
696 return;
699 Profile* profile = Profile::FromBrowserContext(item_->GetBrowserContext());
700 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
701 profile, ServiceAccessType::EXPLICIT_ACCESS);
702 if (!history) {
703 SendRequest();
704 return;
707 history->QueryRedirectsTo(
708 tab_url_,
709 base::Bind(&CheckClientDownloadRequest::OnGotTabRedirects,
710 base::Unretained(this),
711 tab_url_),
712 &request_tracker_);
715 void OnGotTabRedirects(const GURL& url,
716 const history::RedirectList* redirect_list) {
717 DCHECK_CURRENTLY_ON(BrowserThread::UI);
718 DCHECK_EQ(url, tab_url_);
719 if (!service_)
720 return;
722 if (!redirect_list->empty()) {
723 tab_redirects_.insert(
724 tab_redirects_.end(), redirect_list->rbegin(), redirect_list->rend());
727 SendRequest();
730 void SendRequest() {
731 DCHECK_CURRENTLY_ON(BrowserThread::UI);
733 // This is our last chance to check whether the request has been canceled
734 // before sending it.
735 if (!service_)
736 return;
737 bool is_extended_reporting = false;
738 if (item_->GetBrowserContext()) {
739 Profile* profile =
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);
750 } else {
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();
770 } else {
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_);
799 if (image_headers_)
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);
805 return;
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 "
812 << "binaries";
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());
824 fetcher_->Start();
827 void PostFinishTask(DownloadCheckResult result,
828 DownloadCheckResultReason reason) {
829 BrowserThread::PostTask(
830 BrowserThread::UI,
831 FROM_HERE,
832 base::Bind(&CheckClientDownloadRequest::FinishRequest, this, result,
833 reason));
836 void FinishRequest(DownloadCheckResult result,
837 DownloadCheckResultReason reason) {
838 DCHECK_CURRENTLY_ON(BrowserThread::UI);
839 if (finished_) {
840 return;
842 finished_ = true;
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",
848 reason,
849 REASON_MAX);
851 if (!timeout_start_time_.is_null()) {
852 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestTimeoutStats",
853 reason,
854 REASON_MAX);
855 if (reason != REASON_REQUEST_CANCELED) {
856 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestTimeoutDuration",
857 base::TimeTicks::Now() - timeout_start_time_);
860 if (service_) {
861 DVLOG(2) << "SafeBrowsing download verdict for: "
862 << item_->DebugString(true) << " verdict:" << reason
863 << " result:" << result;
864 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
865 reason,
866 REASON_MAX);
867 callback_.Run(result);
868 item_->RemoveObserver(this);
869 item_ = NULL;
870 DownloadProtectionService* service = service_;
871 service_ = NULL;
872 service->RequestFinished(this);
873 // DownloadProtectionService::RequestFinished will decrement our refcount,
874 // so we may be deleted now.
875 } else {
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.
886 return false;
888 scoped_refptr<net::X509Certificate> cert =
889 net::X509Certificate::CreateFromBytes(
890 chain.element(0).certificate().data(),
891 chain.element(0).certificate().size());
892 if (!cert.get()) {
893 return false;
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());
901 if (!issuer.get()) {
902 return false;
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();
913 return true;
916 cert = issuer;
918 return false;
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_;
926 GURL referrer_url_;
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.
930 GURL tab_url_;
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>
937 archived_binary_;
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_;
950 #endif
951 bool finished_;
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),
967 enabled_(false),
968 binary_feature_extractor_(new BinaryFeatureExtractor()),
969 download_request_timeout_ms_(kDownloadRequestTimeoutMs),
970 feedback_service_(new DownloadFeedbackService(
971 request_context_getter, BrowserThread::GetBlockingPool())) {
973 if (sb_service) {
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_) {
987 return;
989 enabled_ = enabled;
990 if (!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,
1000 database_manager_,
1001 binary_feature_extractor_.get()));
1002 download_requests_.insert(request);
1003 request->Start();
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(
1014 BrowserThread::IO,
1015 FROM_HERE,
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));
1032 #else
1033 return false;
1034 #endif
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++;
1052 tmp->Cancel();
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())));
1075 navigator->OpenURL(
1076 content::OpenURLParams(learn_more_url,
1077 content::Referrer(),
1078 NEW_FOREGROUND_TAB,
1079 ui::PAGE_TRANSITION_LINK,
1080 false));
1083 namespace {
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");
1094 } else {
1095 escaped.push_back(attribute[i]);
1098 return escaped;
1100 } // namespace
1102 // static
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
1113 // be present.
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) {
1124 o_tokens.push_back(
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);
1158 // static
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));
1165 return url;
1168 } // namespace safe_browsing