Fire an error if a pref used in the UI is missing once all prefs are fetched.
[chromium-blink-merge.git] / chrome / browser / safe_browsing / download_protection_service.cc
blobba2592be3c90b04e8d8615780447a0b9a8737ab8
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/safe_browsing/download_protection_service.h"
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "base/format_macros.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/metrics/histogram.h"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/sequenced_task_runner_helpers.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/task/cancelable_task_tracker.h"
21 #include "base/threading/sequenced_worker_pool.h"
22 #include "base/time/time.h"
23 #include "chrome/browser/browser_process.h"
24 #include "chrome/browser/history/history_service_factory.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/safe_browsing/download_feedback_service.h"
27 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
28 #include "chrome/browser/safe_browsing/sandboxed_zip_analyzer.h"
29 #include "chrome/browser/ui/browser.h"
30 #include "chrome/browser/ui/browser_list.h"
31 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
32 #include "chrome/common/safe_browsing/csd.pb.h"
33 #include "chrome/common/safe_browsing/download_protection_util.h"
34 #include "chrome/common/safe_browsing/zip_analyzer_results.h"
35 #include "chrome/common/url_constants.h"
36 #include "components/google/core/browser/google_util.h"
37 #include "components/history/core/browser/history_service.h"
38 #include "content/public/browser/browser_thread.h"
39 #include "content/public/browser/download_item.h"
40 #include "content/public/browser/page_navigator.h"
41 #include "crypto/sha2.h"
42 #include "google_apis/google_api_keys.h"
43 #include "net/base/escape.h"
44 #include "net/base/load_flags.h"
45 #include "net/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;
55 namespace {
56 static const int64 kDownloadRequestTimeoutMs = 7000;
57 } // namespace
59 namespace safe_browsing {
61 const char DownloadProtectionService::kDownloadRequestUrl[] =
62 "https://sb-ssl.google.com/safebrowsing/clientreport/download";
64 namespace {
65 // List of extensions for which we track some UMA stats.
66 enum MaliciousExtensionType {
67 EXTENSION_EXE,
68 EXTENSION_MSI,
69 EXTENSION_CAB,
70 EXTENSION_SYS,
71 EXTENSION_SCR,
72 EXTENSION_DRV,
73 EXTENSION_BAT,
74 EXTENSION_ZIP,
75 EXTENSION_RAR,
76 EXTENSION_DLL,
77 EXTENSION_PIF,
78 EXTENSION_COM,
79 EXTENSION_JAR,
80 EXTENSION_CLASS,
81 EXTENSION_PDF,
82 EXTENSION_VB,
83 EXTENSION_REG,
84 EXTENSION_GRP,
85 EXTENSION_OTHER, // Groups all other extensions into one bucket.
86 EXTENSION_CRX,
87 EXTENSION_APK,
88 EXTENSION_DMG,
89 EXTENSION_PKG,
90 EXTENSION_TORRENT,
91 EXTENSION_MAX,
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),
125 EXTENSION_MAX);
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).
131 enum SBStatsType {
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.
141 DOWNLOAD_CHECKS_MAX
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());
156 return spec;
159 } // namespace
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> {
166 public:
167 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()),
176 callback_(callback),
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;
185 protected:
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,
195 FROM_HERE,
196 base::Bind(callback_, result));
197 UpdateDownloadCheckStats(total_type_);
198 if (threat_type != SB_THREAT_TYPE_SAFE) {
199 UpdateDownloadCheckStats(dangerous_type_);
200 BrowserThread::PostTask(
201 BrowserThread::UI,
202 FROM_HERE,
203 base::Bind(&DownloadSBClient::ReportMalware,
204 this, threat_type));
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
219 referrer_url_,
220 true, // is_subresource
221 threat_type,
222 post_data);
225 void UpdateDownloadCheckStats(SBStatsType stat_type) {
226 UMA_HISTOGRAM_ENUMERATION("SB2.DownloadChecks",
227 stat_type,
228 DOWNLOAD_CHECKS_MAX);
231 std::string sha256_hash_;
232 std::vector<GURL> url_chain_;
233 GURL referrer_url_;
234 DownloadProtectionService::CheckDownloadCallback callback_;
235 scoped_refptr<SafeBrowsingUIManager> ui_manager_;
236 base::TimeTicks start_time_;
238 private:
239 const SBStatsType total_type_;
240 const SBStatsType dangerous_type_;
242 DISALLOW_COPY_AND_ASSIGN(DownloadSBClient);
245 class DownloadUrlSBClient : public DownloadSBClient {
246 public:
247 DownloadUrlSBClient(
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);
262 } else {
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_);
276 Release();
279 protected:
280 ~DownloadUrlSBClient() override {}
282 private:
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 {
294 public:
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)
301 : item_(item),
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),
307 callback_(callback),
308 service_(service),
309 binary_feature_extractor_(binary_feature_extractor),
310 database_manager_(database_manager),
311 pingback_enabled_(service_->enabled()),
312 finished_(false),
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);
320 void Start() {
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
326 // times.
327 DownloadCheckResultReason reason = REASON_MAX;
328 if (!IsSupportedDownload(
329 *item_, item_->GetTargetFilePath(), &reason, &type_)) {
330 switch (reason) {
331 case REASON_EMPTY_URL_CHAIN:
332 case REASON_INVALID_URL:
333 case REASON_UNSUPPORTED_URL_SCHEME:
334 PostFinishTask(UNKNOWN, reason);
335 return;
337 case REASON_NOT_BINARY_FILE:
338 RecordFileExtensionType(item_->GetTargetFilePath());
339 PostFinishTask(UNKNOWN, reason);
340 return;
342 default:
343 // We only expect the reasons explicitly handled above.
344 NOTREACHED();
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
351 // are enabled.
352 if (item_->GetTargetFilePath().MatchesExtension(
353 FILE_PATH_LITERAL(".zip"))) {
354 StartExtractZipFeatures();
355 } else {
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));
366 if (!service_) {
367 // Request has already been cancelled.
368 return;
370 timeout_start_time_ = base::TimeTicks::Now();
371 BrowserThread::PostDelayedTask(
372 BrowserThread::UI,
373 FROM_HERE,
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.
382 void Cancel() {
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.
388 fetcher_.reset();
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
396 // this point.
399 // content::DownloadItem::Observer implementation.
400 void OnDownloadDestroyed(content::DownloadItem* download) override {
401 Cancel();
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;
426 std::string data;
427 bool got_data = source->GetResponseAsString(&data);
428 DCHECK(got_data);
429 if (!response.ParseFromString(data)) {
430 reason = REASON_INVALID_RESPONSE_PROTO;
431 result = UNKNOWN;
432 } else if (response.verdict() == ClientDownloadResponse::SAFE) {
433 reason = REASON_DOWNLOAD_SAFE;
434 result = 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
441 // returned UNKNOWN.
442 reason = REASON_DOWNLOAD_NOT_SUPPORTED;
443 result = UNKNOWN;
444 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS) {
445 reason = REASON_DOWNLOAD_DANGEROUS;
446 result = DANGEROUS;
447 } else if (response.verdict() == ClientDownloadResponse::UNCOMMON) {
448 reason = REASON_DOWNLOAD_UNCOMMON;
449 result = UNCOMMON;
450 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS_HOST) {
451 reason = REASON_DOWNLOAD_DANGEROUS_HOST;
452 result = DANGEROUS_HOST;
453 } else if (
454 response.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED) {
455 reason = REASON_DOWNLOAD_POTENTIALLY_UNWANTED;
456 result = POTENTIALLY_UNWANTED;
457 } else {
458 LOG(DFATAL) << "Unknown download response verdict: "
459 << response.verdict();
460 reason = REASON_INVALID_RESPONSE_VERDICT;
461 result = UNKNOWN;
463 DownloadFeedbackService::MaybeStorePingsForDownload(
464 result, item_, client_download_request_data_, data);
466 // We don't need the fetcher anymore.
467 fetcher_.reset();
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;
481 return false;
483 const GURL& final_url = item.GetUrlChain().back();
484 if (!final_url.is_valid() || final_url.is_empty()) {
485 *reason = REASON_INVALID_URL;
486 return false;
488 if (!download_protection_util::IsBinaryFile(target_path)) {
489 *reason = REASON_NOT_BINARY_FILE;
490 return false;
492 if ((!final_url.IsStandard() && !final_url.SchemeIsBlob() &&
493 !final_url.SchemeIs(url::kDataScheme)) ||
494 final_url.SchemeIsFile()) {
495 *reason = REASON_UNSUPPORTED_URL_SCHEME;
496 return false;
498 *type = download_protection_util::GetDownloadType(target_path);
499 return true;
502 private:
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(
518 BrowserThread::IO,
519 FROM_HERE,
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(
526 BrowserThread::UI,
527 FROM_HERE,
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(
537 FROM_HERE,
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);
547 if (is_signed) {
548 DVLOG(2) << "Downloaded a signed binary: " << file_path.value();
549 } else {
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 image_headers_.reset(new ClientDownloadRequest_ImageHeaders());
559 if (!binary_feature_extractor_->ExtractImageHeaders(
560 file_path,
561 BinaryFeatureExtractor::kDefaultOptions,
562 image_headers_.get())) {
563 image_headers_.reset();
565 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractImageHeadersTime",
566 base::TimeTicks::Now() - start_time);
568 OnFileFeatureExtractionDone();
571 void StartExtractZipFeatures() {
572 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
573 DCHECK(item_); // Called directly from Start(), item should still exist.
574 zip_analysis_start_time_ = base::TimeTicks::Now();
575 // We give the zip analyzer a weak pointer to this object. Since the
576 // analyzer is refcounted, it might outlive the request.
577 analyzer_ = new SandboxedZipAnalyzer(
578 item_->GetFullPath(),
579 base::Bind(&CheckClientDownloadRequest::OnZipAnalysisFinished,
580 weakptr_factory_.GetWeakPtr()));
581 analyzer_->Start();
584 void OnZipAnalysisFinished(const zip_analyzer::Results& results) {
585 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
586 if (!service_)
587 return;
588 if (results.success) {
589 zipped_executable_ = results.has_executable;
590 archived_binary_.CopyFrom(results.archived_binary);
591 DVLOG(1) << "Zip analysis finished for " << item_->GetFullPath().value()
592 << ", has_executable=" << results.has_executable
593 << " has_archive=" << results.has_archive;
594 } else {
595 DVLOG(1) << "Zip analysis failed for " << item_->GetFullPath().value();
597 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
598 zipped_executable_);
599 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable",
600 results.has_archive && !zipped_executable_);
601 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime",
602 base::TimeTicks::Now() - zip_analysis_start_time_);
604 if (!zipped_executable_) {
605 PostFinishTask(UNKNOWN, REASON_ARCHIVE_WITHOUT_BINARIES);
606 return;
608 OnFileFeatureExtractionDone();
611 static void RecordCountOfSignedOrWhitelistedDownload() {
612 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
615 void CheckWhitelists() {
616 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
618 if (!database_manager_.get()) {
619 PostFinishTask(UNKNOWN, REASON_SB_DISABLED);
620 return;
623 const GURL& url = url_chain_.back();
624 if (url.is_valid() && database_manager_->MatchDownloadWhitelistUrl(url)) {
625 DVLOG(2) << url << " is on the download whitelist.";
626 RecordCountOfSignedOrWhitelistedDownload();
627 // TODO(grt): Continue processing without uploading so that
628 // ClientDownloadRequest callbacks can be run even for this type of safe
629 // download.
630 PostFinishTask(SAFE, REASON_WHITELISTED_URL);
631 return;
634 if (signature_info_.trusted()) {
635 RecordCountOfSignedOrWhitelistedDownload();
636 for (int i = 0; i < signature_info_.certificate_chain_size(); ++i) {
637 if (CertificateChainIsWhitelisted(
638 signature_info_.certificate_chain(i))) {
639 // TODO(grt): Continue processing without uploading so that
640 // ClientDownloadRequest callbacks can be run even for this type of
641 // safe download.
642 PostFinishTask(SAFE, REASON_TRUSTED_EXECUTABLE);
643 return;
648 if (!pingback_enabled_) {
649 PostFinishTask(UNKNOWN, REASON_PING_DISABLED);
650 return;
653 // Currently, the UI only works on Windows so we don't even bother with
654 // pinging the server if we're not on Windows.
655 // TODO(noelutz): change this code once the UI is done for Linux and Mac.
656 #if defined(OS_MACOSX)
657 // TODO(mattm): remove this (see crbug.com/414834).
658 if (base::FieldTrialList::FindFullName("SafeBrowsingOSXClientDownloadPings")
659 != "Enabled") {
660 PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED);
661 return;
663 #endif
664 #if defined(OS_WIN) || defined(OS_MACOSX)
665 // The URLFetcher is owned by the UI thread, so post a message to
666 // start the pingback.
667 BrowserThread::PostTask(
668 BrowserThread::UI,
669 FROM_HERE,
670 base::Bind(&CheckClientDownloadRequest::GetTabRedirects, this));
671 #else
672 PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED);
673 #endif
676 void GetTabRedirects() {
677 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
678 if (!service_)
679 return;
681 if (!tab_url_.is_valid()) {
682 SendRequest();
683 return;
686 Profile* profile = Profile::FromBrowserContext(item_->GetBrowserContext());
687 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
688 profile, ServiceAccessType::EXPLICIT_ACCESS);
689 if (!history) {
690 SendRequest();
691 return;
694 history->QueryRedirectsTo(
695 tab_url_,
696 base::Bind(&CheckClientDownloadRequest::OnGotTabRedirects,
697 base::Unretained(this),
698 tab_url_),
699 &request_tracker_);
702 void OnGotTabRedirects(const GURL& url,
703 const history::RedirectList* redirect_list) {
704 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
705 DCHECK_EQ(url, tab_url_);
706 if (!service_)
707 return;
709 if (!redirect_list->empty()) {
710 tab_redirects_.insert(
711 tab_redirects_.end(), redirect_list->rbegin(), redirect_list->rend());
714 SendRequest();
717 void SendRequest() {
718 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
720 // This is our last chance to check whether the request has been canceled
721 // before sending it.
722 if (!service_)
723 return;
725 ClientDownloadRequest request;
726 request.set_url(SanitizeUrl(item_->GetUrlChain().back()));
727 request.mutable_digests()->set_sha256(item_->GetHash());
728 request.set_length(item_->GetReceivedBytes());
729 for (size_t i = 0; i < item_->GetUrlChain().size(); ++i) {
730 ClientDownloadRequest::Resource* resource = request.add_resources();
731 resource->set_url(SanitizeUrl(item_->GetUrlChain()[i]));
732 if (i == item_->GetUrlChain().size() - 1) {
733 // The last URL in the chain is the download URL.
734 resource->set_type(ClientDownloadRequest::DOWNLOAD_URL);
735 resource->set_referrer(SanitizeUrl(item_->GetReferrerUrl()));
736 DVLOG(2) << "dl url " << resource->url();
737 if (!item_->GetRemoteAddress().empty()) {
738 resource->set_remote_ip(item_->GetRemoteAddress());
739 DVLOG(2) << " dl url remote addr: " << resource->remote_ip();
741 DVLOG(2) << "dl referrer " << resource->referrer();
742 } else {
743 DVLOG(2) << "dl redirect " << i << " " << resource->url();
744 resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT);
746 // TODO(noelutz): fill out the remote IP addresses.
748 // TODO(mattm): fill out the remote IP addresses for tab resources.
749 for (size_t i = 0; i < tab_redirects_.size(); ++i) {
750 ClientDownloadRequest::Resource* resource = request.add_resources();
751 DVLOG(2) << "tab redirect " << i << " " << tab_redirects_[i].spec();
752 resource->set_url(SanitizeUrl(tab_redirects_[i]));
753 resource->set_type(ClientDownloadRequest::TAB_REDIRECT);
755 if (tab_url_.is_valid()) {
756 ClientDownloadRequest::Resource* resource = request.add_resources();
757 resource->set_url(SanitizeUrl(tab_url_));
758 DVLOG(2) << "tab url " << resource->url();
759 resource->set_type(ClientDownloadRequest::TAB_URL);
760 if (tab_referrer_url_.is_valid()) {
761 resource->set_referrer(SanitizeUrl(tab_referrer_url_));
762 DVLOG(2) << "tab referrer " << resource->referrer();
766 request.set_user_initiated(item_->HasUserGesture());
767 request.set_file_basename(
768 item_->GetTargetFilePath().BaseName().AsUTF8Unsafe());
769 request.set_download_type(type_);
770 request.mutable_signature()->CopyFrom(signature_info_);
771 if (image_headers_)
772 request.set_allocated_image_headers(image_headers_.release());
773 if (!request.SerializeToString(&client_download_request_data_)) {
774 FinishRequest(UNKNOWN, REASON_INVALID_REQUEST_PROTO);
775 return;
777 if (zipped_executable_)
778 request.mutable_archived_binary()->Swap(&archived_binary_);
779 service_->client_download_request_callbacks_.Notify(item_, &request);
781 DVLOG(2) << "Sending a request for URL: "
782 << item_->GetUrlChain().back();
783 fetcher_.reset(net::URLFetcher::Create(0 /* ID used for testing */,
784 GetDownloadRequestUrl(),
785 net::URLFetcher::POST,
786 this));
787 fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
788 fetcher_->SetAutomaticallyRetryOn5xx(false); // Don't retry on error.
789 fetcher_->SetRequestContext(service_->request_context_getter_.get());
790 fetcher_->SetUploadData("application/octet-stream",
791 client_download_request_data_);
792 request_start_time_ = base::TimeTicks::Now();
793 UMA_HISTOGRAM_COUNTS("SBClientDownload.DownloadRequestPayloadSize",
794 client_download_request_data_.size());
795 fetcher_->Start();
798 void PostFinishTask(DownloadCheckResult result,
799 DownloadCheckResultReason reason) {
800 BrowserThread::PostTask(
801 BrowserThread::UI,
802 FROM_HERE,
803 base::Bind(&CheckClientDownloadRequest::FinishRequest, this, result,
804 reason));
807 void FinishRequest(DownloadCheckResult result,
808 DownloadCheckResultReason reason) {
809 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
810 if (finished_) {
811 return;
813 finished_ = true;
814 // Ensure the timeout task is cancelled while we still have a non-zero
815 // refcount. (crbug.com/240449)
816 weakptr_factory_.InvalidateWeakPtrs();
817 if (!request_start_time_.is_null()) {
818 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestNetworkStats",
819 reason,
820 REASON_MAX);
822 if (!timeout_start_time_.is_null()) {
823 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestTimeoutStats",
824 reason,
825 REASON_MAX);
826 if (reason != REASON_REQUEST_CANCELED) {
827 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestTimeoutDuration",
828 base::TimeTicks::Now() - timeout_start_time_);
831 if (result == SAFE && (reason == REASON_WHITELISTED_URL ||
832 reason == REASON_TRUSTED_EXECUTABLE)) {
833 // Due to the short-circuit logic in CheckWhitelists (see TODOs there), a
834 // ClientDownloadRequest was not generated for this download and callbacks
835 // were not run. Run them now with null to indicate that a download has
836 // taken place.
837 // TODO(grt): persist metadata for these downloads as well.
838 service_->client_download_request_callbacks_.Notify(item_, nullptr);
840 if (service_) {
841 DVLOG(2) << "SafeBrowsing download verdict for: "
842 << item_->DebugString(true) << " verdict:" << reason
843 << " result:" << result;
844 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
845 reason,
846 REASON_MAX);
847 #if defined(OS_MACOSX)
848 // OSX is currently sending pings only for evaluation purposes, ignore
849 // the result for now.
850 // TODO(mattm): remove this and update the ifdef in
851 // DownloadItemImpl::IsDangerous (see crbug.com/413968).
852 result = UNKNOWN;
853 #endif
854 callback_.Run(result);
855 item_->RemoveObserver(this);
856 item_ = NULL;
857 DownloadProtectionService* service = service_;
858 service_ = NULL;
859 service->RequestFinished(this);
860 // DownloadProtectionService::RequestFinished will decrement our refcount,
861 // so we may be deleted now.
862 } else {
863 callback_.Run(UNKNOWN);
867 bool CertificateChainIsWhitelisted(
868 const ClientDownloadRequest_CertificateChain& chain) {
869 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
870 if (chain.element_size() < 2) {
871 // We need to have both a signing certificate and its issuer certificate
872 // present to construct a whitelist entry.
873 return false;
875 scoped_refptr<net::X509Certificate> cert =
876 net::X509Certificate::CreateFromBytes(
877 chain.element(0).certificate().data(),
878 chain.element(0).certificate().size());
879 if (!cert.get()) {
880 return false;
883 for (int i = 1; i < chain.element_size(); ++i) {
884 scoped_refptr<net::X509Certificate> issuer =
885 net::X509Certificate::CreateFromBytes(
886 chain.element(i).certificate().data(),
887 chain.element(i).certificate().size());
888 if (!issuer.get()) {
889 return false;
891 std::vector<std::string> whitelist_strings;
892 DownloadProtectionService::GetCertificateWhitelistStrings(
893 *cert.get(), *issuer.get(), &whitelist_strings);
894 for (size_t j = 0; j < whitelist_strings.size(); ++j) {
895 if (database_manager_->MatchDownloadWhitelistString(
896 whitelist_strings[j])) {
897 DVLOG(2) << "Certificate matched whitelist, cert="
898 << cert->subject().GetDisplayName()
899 << " issuer=" << issuer->subject().GetDisplayName();
900 return true;
903 cert = issuer;
905 return false;
908 // The DownloadItem we are checking. Will be NULL if the request has been
909 // canceled. Must be accessed only on UI thread.
910 content::DownloadItem* item_;
911 // Copies of data from |item_| for access on other threads.
912 std::vector<GURL> url_chain_;
913 GURL referrer_url_;
914 // URL chain of redirects leading to (but not including) |tab_url|.
915 std::vector<GURL> tab_redirects_;
916 // URL and referrer of the window the download was started from.
917 GURL tab_url_;
918 GURL tab_referrer_url_;
920 bool zipped_executable_;
921 ClientDownloadRequest_SignatureInfo signature_info_;
922 scoped_ptr<ClientDownloadRequest_ImageHeaders> image_headers_;
923 google::protobuf::RepeatedPtrField<ClientDownloadRequest_ArchivedBinary>
924 archived_binary_;
925 CheckDownloadCallback callback_;
926 // Will be NULL if the request has been canceled.
927 DownloadProtectionService* service_;
928 scoped_refptr<BinaryFeatureExtractor> binary_feature_extractor_;
929 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
930 const bool pingback_enabled_;
931 scoped_ptr<net::URLFetcher> fetcher_;
932 scoped_refptr<SandboxedZipAnalyzer> analyzer_;
933 base::TimeTicks zip_analysis_start_time_;
934 bool finished_;
935 ClientDownloadRequest::DownloadType type_;
936 std::string client_download_request_data_;
937 base::CancelableTaskTracker request_tracker_; // For HistoryService lookup.
938 base::TimeTicks start_time_; // Used for stats.
939 base::TimeTicks timeout_start_time_;
940 base::TimeTicks request_start_time_;
941 base::WeakPtrFactory<CheckClientDownloadRequest> weakptr_factory_;
943 DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest);
946 DownloadProtectionService::DownloadProtectionService(
947 SafeBrowsingService* sb_service,
948 net::URLRequestContextGetter* request_context_getter)
949 : request_context_getter_(request_context_getter),
950 enabled_(false),
951 binary_feature_extractor_(new BinaryFeatureExtractor()),
952 download_request_timeout_ms_(kDownloadRequestTimeoutMs),
953 feedback_service_(new DownloadFeedbackService(
954 request_context_getter, BrowserThread::GetBlockingPool())) {
956 if (sb_service) {
957 ui_manager_ = sb_service->ui_manager();
958 database_manager_ = sb_service->database_manager();
962 DownloadProtectionService::~DownloadProtectionService() {
963 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
964 CancelPendingRequests();
967 void DownloadProtectionService::SetEnabled(bool enabled) {
968 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
969 if (enabled == enabled_) {
970 return;
972 enabled_ = enabled;
973 if (!enabled_) {
974 CancelPendingRequests();
978 void DownloadProtectionService::CheckClientDownload(
979 content::DownloadItem* item,
980 const CheckDownloadCallback& callback) {
981 scoped_refptr<CheckClientDownloadRequest> request(
982 new CheckClientDownloadRequest(item, callback, this,
983 database_manager_,
984 binary_feature_extractor_.get()));
985 download_requests_.insert(request);
986 request->Start();
989 void DownloadProtectionService::CheckDownloadUrl(
990 const content::DownloadItem& item,
991 const CheckDownloadCallback& callback) {
992 DCHECK(!item.GetUrlChain().empty());
993 scoped_refptr<DownloadUrlSBClient> client(
994 new DownloadUrlSBClient(item, callback, ui_manager_, database_manager_));
995 // The client will release itself once it is done.
996 BrowserThread::PostTask(
997 BrowserThread::IO,
998 FROM_HERE,
999 base::Bind(&DownloadUrlSBClient::StartCheck, client));
1002 bool DownloadProtectionService::IsSupportedDownload(
1003 const content::DownloadItem& item,
1004 const base::FilePath& target_path) const {
1005 // Currently, the UI is only enabled on Windows. On Mac we send the ping but
1006 // ignore the result (see ifdef in FinishRequest). On Linux we still
1007 // want to show the dangerous file type warning if the file is possibly
1008 // dangerous which means we have to always return false here.
1009 #if defined(OS_WIN)
1010 DownloadCheckResultReason reason = REASON_MAX;
1011 ClientDownloadRequest::DownloadType type =
1012 ClientDownloadRequest::WIN_EXECUTABLE;
1013 return (CheckClientDownloadRequest::IsSupportedDownload(
1014 item, target_path, &reason, &type) &&
1015 (ClientDownloadRequest::CHROME_EXTENSION != type));
1016 #else
1017 return false;
1018 #endif
1021 DownloadProtectionService::ClientDownloadRequestSubscription
1022 DownloadProtectionService::RegisterClientDownloadRequestCallback(
1023 const ClientDownloadRequestCallback& callback) {
1024 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1025 return client_download_request_callbacks_.Add(callback);
1028 void DownloadProtectionService::CancelPendingRequests() {
1029 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1030 for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
1031 download_requests_.begin();
1032 it != download_requests_.end();) {
1033 // We need to advance the iterator before we cancel because canceling
1034 // the request will invalidate it when RequestFinished is called below.
1035 scoped_refptr<CheckClientDownloadRequest> tmp = *it++;
1036 tmp->Cancel();
1038 DCHECK(download_requests_.empty());
1041 void DownloadProtectionService::RequestFinished(
1042 CheckClientDownloadRequest* request) {
1043 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1044 std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
1045 download_requests_.find(request);
1046 DCHECK(it != download_requests_.end());
1047 download_requests_.erase(*it);
1050 void DownloadProtectionService::ShowDetailsForDownload(
1051 const content::DownloadItem& item,
1052 content::PageNavigator* navigator) {
1053 GURL learn_more_url(chrome::kDownloadScanningLearnMoreURL);
1054 learn_more_url = google_util::AppendGoogleLocaleParam(
1055 learn_more_url, g_browser_process->GetApplicationLocale());
1056 navigator->OpenURL(
1057 content::OpenURLParams(learn_more_url,
1058 content::Referrer(),
1059 NEW_FOREGROUND_TAB,
1060 ui::PAGE_TRANSITION_LINK,
1061 false));
1064 namespace {
1065 // Escapes a certificate attribute so that it can be used in a whitelist
1066 // entry. Currently, we only escape slashes, since they are used as a
1067 // separator between attributes.
1068 std::string EscapeCertAttribute(const std::string& attribute) {
1069 std::string escaped;
1070 for (size_t i = 0; i < attribute.size(); ++i) {
1071 if (attribute[i] == '%') {
1072 escaped.append("%25");
1073 } else if (attribute[i] == '/') {
1074 escaped.append("%2F");
1075 } else {
1076 escaped.push_back(attribute[i]);
1079 return escaped;
1081 } // namespace
1083 // static
1084 void DownloadProtectionService::GetCertificateWhitelistStrings(
1085 const net::X509Certificate& certificate,
1086 const net::X509Certificate& issuer,
1087 std::vector<std::string>* whitelist_strings) {
1088 // The whitelist paths are in the format:
1089 // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit]
1091 // Any of CN, O, or OU may be omitted from the whitelist entry, in which
1092 // case they match anything. However, the attributes that do appear will
1093 // always be in the order shown above. At least one attribute will always
1094 // be present.
1096 const net::CertPrincipal& subject = certificate.subject();
1097 std::vector<std::string> ou_tokens;
1098 for (size_t i = 0; i < subject.organization_unit_names.size(); ++i) {
1099 ou_tokens.push_back(
1100 "/OU=" + EscapeCertAttribute(subject.organization_unit_names[i]));
1103 std::vector<std::string> o_tokens;
1104 for (size_t i = 0; i < subject.organization_names.size(); ++i) {
1105 o_tokens.push_back(
1106 "/O=" + EscapeCertAttribute(subject.organization_names[i]));
1109 std::string cn_token;
1110 if (!subject.common_name.empty()) {
1111 cn_token = "/CN=" + EscapeCertAttribute(subject.common_name);
1114 std::set<std::string> paths_to_check;
1115 if (!cn_token.empty()) {
1116 paths_to_check.insert(cn_token);
1118 for (size_t i = 0; i < o_tokens.size(); ++i) {
1119 paths_to_check.insert(cn_token + o_tokens[i]);
1120 paths_to_check.insert(o_tokens[i]);
1121 for (size_t j = 0; j < ou_tokens.size(); ++j) {
1122 paths_to_check.insert(cn_token + o_tokens[i] + ou_tokens[j]);
1123 paths_to_check.insert(o_tokens[i] + ou_tokens[j]);
1126 for (size_t i = 0; i < ou_tokens.size(); ++i) {
1127 paths_to_check.insert(cn_token + ou_tokens[i]);
1128 paths_to_check.insert(ou_tokens[i]);
1131 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data,
1132 sizeof(issuer.fingerprint().data));
1133 for (std::set<std::string>::iterator it = paths_to_check.begin();
1134 it != paths_to_check.end(); ++it) {
1135 whitelist_strings->push_back("cert/" + issuer_fp + *it);
1139 // static
1140 GURL DownloadProtectionService::GetDownloadRequestUrl() {
1141 GURL url(kDownloadRequestUrl);
1142 std::string api_key = google_apis::GetAPIKey();
1143 if (!api_key.empty())
1144 url = url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
1146 return url;
1149 } // namespace safe_browsing