[sql] Remove _HAS_EXCEPTIONS=0 from build info.
[chromium-blink-merge.git] / chrome / browser / safe_browsing / download_protection_service.cc
blob1215b2fa17da40ebfa5625c43ec08cb22778e924
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 using content::BrowserThread;
59 namespace {
60 static const int64 kDownloadRequestTimeoutMs = 7000;
61 } // namespace
63 namespace safe_browsing {
65 const char DownloadProtectionService::kDownloadRequestUrl[] =
66 "https://sb-ssl.google.com/safebrowsing/clientreport/download";
68 namespace {
69 void RecordFileExtensionType(const base::FilePath& file) {
70 UMA_HISTOGRAM_ENUMERATION(
71 "SBClientDownload.DownloadExtensions",
72 download_protection_util::GetSBClientDownloadExtensionValueForUMA(file),
73 download_protection_util::kSBClientDownloadExtensionsMax);
76 void RecordArchivedArchiveFileExtensionType(
77 const base::FilePath::StringType& extension) {
78 UMA_HISTOGRAM_ENUMERATION(
79 "SBClientDownload.ArchivedArchiveExtensions",
80 download_protection_util::GetSBClientDownloadExtensionValueForUMA(
81 base::FilePath(extension)),
82 download_protection_util::kSBClientDownloadExtensionsMax);
85 // Enumerate for histogramming purposes.
86 // DO NOT CHANGE THE ORDERING OF THESE VALUES (different histogram data will
87 // be mixed together based on their values).
88 enum SBStatsType {
89 DOWNLOAD_URL_CHECKS_TOTAL,
90 DOWNLOAD_URL_CHECKS_CANCELED,
91 DOWNLOAD_URL_CHECKS_MALWARE,
93 DOWNLOAD_HASH_CHECKS_TOTAL,
94 DOWNLOAD_HASH_CHECKS_MALWARE,
96 // Memory space for histograms is determined by the max.
97 // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
98 DOWNLOAD_CHECKS_MAX
101 // Prepares URLs to be put into a ping message. Currently this just shortens
102 // data: URIs, other URLs are included verbatim.
103 std::string SanitizeUrl(const GURL& url) {
104 std::string spec = url.spec();
105 if (url.SchemeIs(url::kDataScheme)) {
106 size_t comma_pos = spec.find(',');
107 if (comma_pos != std::string::npos && comma_pos != spec.size() - 1) {
108 std::string hash_value = crypto::SHA256HashString(spec);
109 spec.erase(comma_pos + 1);
110 spec += base::HexEncode(hash_value.data(), hash_value.size());
113 return spec;
116 } // namespace
118 // Parent SafeBrowsing::Client class used to lookup the bad binary
119 // URL and digest list. There are two sub-classes (one for each list).
120 class DownloadSBClient
121 : public SafeBrowsingDatabaseManager::Client,
122 public base::RefCountedThreadSafe<DownloadSBClient> {
123 public:
124 DownloadSBClient(
125 const content::DownloadItem& item,
126 const DownloadProtectionService::CheckDownloadCallback& callback,
127 const scoped_refptr<SafeBrowsingUIManager>& ui_manager,
128 SBStatsType total_type,
129 SBStatsType dangerous_type)
130 : sha256_hash_(item.GetHash()),
131 url_chain_(item.GetUrlChain()),
132 referrer_url_(item.GetReferrerUrl()),
133 callback_(callback),
134 ui_manager_(ui_manager),
135 start_time_(base::TimeTicks::Now()),
136 total_type_(total_type),
137 dangerous_type_(dangerous_type) {
138 Profile* profile = Profile::FromBrowserContext(item.GetBrowserContext());
139 is_extended_reporting_ = profile &&
140 profile->GetPrefs()->GetBoolean(
141 prefs::kSafeBrowsingExtendedReportingEnabled);
144 virtual void StartCheck() = 0;
145 virtual bool IsDangerous(SBThreatType threat_type) const = 0;
147 protected:
148 friend class base::RefCountedThreadSafe<DownloadSBClient>;
149 ~DownloadSBClient() override {}
151 void CheckDone(SBThreatType threat_type) {
152 DownloadProtectionService::DownloadCheckResult result =
153 IsDangerous(threat_type) ?
154 DownloadProtectionService::DANGEROUS :
155 DownloadProtectionService::SAFE;
156 BrowserThread::PostTask(BrowserThread::UI,
157 FROM_HERE,
158 base::Bind(callback_, result));
159 UpdateDownloadCheckStats(total_type_);
160 if (threat_type != SB_THREAT_TYPE_SAFE) {
161 UpdateDownloadCheckStats(dangerous_type_);
162 BrowserThread::PostTask(
163 BrowserThread::UI,
164 FROM_HERE,
165 base::Bind(&DownloadSBClient::ReportMalware,
166 this, threat_type));
170 void ReportMalware(SBThreatType threat_type) {
171 std::string post_data;
172 if (!sha256_hash_.empty())
173 post_data += base::HexEncode(sha256_hash_.data(),
174 sha256_hash_.size()) + "\n";
175 for (size_t i = 0; i < url_chain_.size(); ++i) {
176 post_data += url_chain_[i].spec() + "\n";
178 ui_manager_->ReportSafeBrowsingHit(url_chain_.back(), // malicious_url
179 url_chain_.front(), // page_url
180 referrer_url_,
181 true, // is_subresource
182 threat_type,
183 post_data,
184 is_extended_reporting_);
187 void UpdateDownloadCheckStats(SBStatsType stat_type) {
188 UMA_HISTOGRAM_ENUMERATION("SB2.DownloadChecks",
189 stat_type,
190 DOWNLOAD_CHECKS_MAX);
193 std::string sha256_hash_;
194 std::vector<GURL> url_chain_;
195 GURL referrer_url_;
196 DownloadProtectionService::CheckDownloadCallback callback_;
197 scoped_refptr<SafeBrowsingUIManager> ui_manager_;
198 base::TimeTicks start_time_;
200 private:
201 const SBStatsType total_type_;
202 const SBStatsType dangerous_type_;
203 bool is_extended_reporting_;
205 DISALLOW_COPY_AND_ASSIGN(DownloadSBClient);
208 class DownloadUrlSBClient : public DownloadSBClient {
209 public:
210 DownloadUrlSBClient(
211 const content::DownloadItem& item,
212 const DownloadProtectionService::CheckDownloadCallback& callback,
213 const scoped_refptr<SafeBrowsingUIManager>& ui_manager,
214 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager)
215 : DownloadSBClient(item, callback, ui_manager,
216 DOWNLOAD_URL_CHECKS_TOTAL,
217 DOWNLOAD_URL_CHECKS_MALWARE),
218 database_manager_(database_manager) { }
220 void StartCheck() override {
221 DCHECK_CURRENTLY_ON(BrowserThread::IO);
222 if (!database_manager_.get() ||
223 database_manager_->CheckDownloadUrl(url_chain_, this)) {
224 CheckDone(SB_THREAT_TYPE_SAFE);
225 } else {
226 AddRef(); // SafeBrowsingService takes a pointer not a scoped_refptr.
230 bool IsDangerous(SBThreatType threat_type) const override {
231 return threat_type == SB_THREAT_TYPE_BINARY_MALWARE_URL;
234 void OnCheckDownloadUrlResult(const std::vector<GURL>& url_chain,
235 SBThreatType threat_type) override {
236 CheckDone(threat_type);
237 UMA_HISTOGRAM_TIMES("SB2.DownloadUrlCheckDuration",
238 base::TimeTicks::Now() - start_time_);
239 Release();
242 protected:
243 ~DownloadUrlSBClient() override {}
245 private:
246 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
248 DISALLOW_COPY_AND_ASSIGN(DownloadUrlSBClient);
251 class DownloadProtectionService::CheckClientDownloadRequest
252 : public base::RefCountedThreadSafe<
253 DownloadProtectionService::CheckClientDownloadRequest,
254 BrowserThread::DeleteOnUIThread>,
255 public net::URLFetcherDelegate,
256 public content::DownloadItem::Observer {
257 public:
258 CheckClientDownloadRequest(
259 content::DownloadItem* item,
260 const CheckDownloadCallback& callback,
261 DownloadProtectionService* service,
262 const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
263 BinaryFeatureExtractor* binary_feature_extractor)
264 : item_(item),
265 url_chain_(item->GetUrlChain()),
266 referrer_url_(item->GetReferrerUrl()),
267 tab_url_(item->GetTabUrl()),
268 tab_referrer_url_(item->GetTabReferrerUrl()),
269 zipped_executable_(false),
270 callback_(callback),
271 service_(service),
272 binary_feature_extractor_(binary_feature_extractor),
273 database_manager_(database_manager),
274 pingback_enabled_(service_->enabled()),
275 finished_(false),
276 type_(ClientDownloadRequest::WIN_EXECUTABLE),
277 start_time_(base::TimeTicks::Now()),
278 weakptr_factory_(this) {
279 DCHECK_CURRENTLY_ON(BrowserThread::UI);
280 item_->AddObserver(this);
283 void Start() {
284 DVLOG(2) << "Starting SafeBrowsing download check for: "
285 << item_->DebugString(true);
286 DCHECK_CURRENTLY_ON(BrowserThread::UI);
287 // TODO(noelutz): implement some cache to make sure we don't issue the same
288 // request over and over again if a user downloads the same binary multiple
289 // times.
290 DownloadCheckResultReason reason = REASON_MAX;
291 if (!IsSupportedDownload(
292 *item_, item_->GetTargetFilePath(), &reason, &type_)) {
293 switch (reason) {
294 case REASON_EMPTY_URL_CHAIN:
295 case REASON_INVALID_URL:
296 case REASON_UNSUPPORTED_URL_SCHEME:
297 PostFinishTask(UNKNOWN, reason);
298 return;
300 case REASON_NOT_BINARY_FILE:
301 RecordFileExtensionType(item_->GetTargetFilePath());
302 PostFinishTask(UNKNOWN, reason);
303 return;
305 default:
306 // We only expect the reasons explicitly handled above.
307 NOTREACHED();
310 RecordFileExtensionType(item_->GetTargetFilePath());
312 // Compute features from the file contents. Note that we record histograms
313 // based on the result, so this runs regardless of whether the pingbacks
314 // are enabled.
315 if (item_->GetTargetFilePath().MatchesExtension(
316 FILE_PATH_LITERAL(".zip"))) {
317 StartExtractZipFeatures();
318 } else {
319 DCHECK(!download_protection_util::IsArchiveFile(
320 item_->GetTargetFilePath()));
321 StartExtractFileFeatures();
325 // Start a timeout to cancel the request if it takes too long.
326 // This should only be called after we have finished accessing the file.
327 void StartTimeout() {
328 DCHECK_CURRENTLY_ON(BrowserThread::UI);
329 if (!service_) {
330 // Request has already been cancelled.
331 return;
333 timeout_start_time_ = base::TimeTicks::Now();
334 BrowserThread::PostDelayedTask(
335 BrowserThread::UI,
336 FROM_HERE,
337 base::Bind(&CheckClientDownloadRequest::Cancel,
338 weakptr_factory_.GetWeakPtr()),
339 base::TimeDelta::FromMilliseconds(
340 service_->download_request_timeout_ms()));
343 // Canceling a request will cause us to always report the result as UNKNOWN
344 // unless a pending request is about to call FinishRequest.
345 void Cancel() {
346 DCHECK_CURRENTLY_ON(BrowserThread::UI);
347 if (fetcher_.get()) {
348 // The DownloadProtectionService is going to release its reference, so we
349 // might be destroyed before the URLFetcher completes. Cancel the
350 // fetcher so it does not try to invoke OnURLFetchComplete.
351 fetcher_.reset();
353 // Note: If there is no fetcher, then some callback is still holding a
354 // reference to this object. We'll eventually wind up in some method on
355 // the UI thread that will call FinishRequest() again. If FinishRequest()
356 // is called a second time, it will be a no-op.
357 FinishRequest(UNKNOWN, REASON_REQUEST_CANCELED);
358 // Calling FinishRequest might delete this object, we may be deleted by
359 // this point.
362 // content::DownloadItem::Observer implementation.
363 void OnDownloadDestroyed(content::DownloadItem* download) override {
364 Cancel();
365 DCHECK(item_ == NULL);
368 // From the net::URLFetcherDelegate interface.
369 void OnURLFetchComplete(const net::URLFetcher* source) override {
370 DCHECK_CURRENTLY_ON(BrowserThread::UI);
371 DCHECK_EQ(source, fetcher_.get());
372 DVLOG(2) << "Received a response for URL: "
373 << item_->GetUrlChain().back() << ": success="
374 << source->GetStatus().is_success() << " response_code="
375 << source->GetResponseCode();
376 if (source->GetStatus().is_success()) {
377 UMA_HISTOGRAM_SPARSE_SLOWLY(
378 "SBClientDownload.DownloadRequestResponseCode",
379 source->GetResponseCode());
381 UMA_HISTOGRAM_SPARSE_SLOWLY(
382 "SBClientDownload.DownloadRequestNetError",
383 -source->GetStatus().error());
384 DownloadCheckResultReason reason = REASON_SERVER_PING_FAILED;
385 DownloadCheckResult result = UNKNOWN;
386 if (source->GetStatus().is_success() &&
387 net::HTTP_OK == source->GetResponseCode()) {
388 ClientDownloadResponse response;
389 std::string data;
390 bool got_data = source->GetResponseAsString(&data);
391 DCHECK(got_data);
392 if (!response.ParseFromString(data)) {
393 reason = REASON_INVALID_RESPONSE_PROTO;
394 result = UNKNOWN;
395 } else if (response.verdict() == ClientDownloadResponse::SAFE) {
396 reason = REASON_DOWNLOAD_SAFE;
397 result = SAFE;
398 } else if (service_ && !service_->IsSupportedDownload(
399 *item_, item_->GetTargetFilePath())) {
400 // The client of the download protection service assumes that we don't
401 // support this download so we cannot return any other verdict than
402 // UNKNOWN even if the server says it's dangerous to download this file.
403 // Note: if service_ is NULL we already cancelled the request and
404 // returned UNKNOWN.
405 reason = REASON_DOWNLOAD_NOT_SUPPORTED;
406 result = UNKNOWN;
407 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS) {
408 reason = REASON_DOWNLOAD_DANGEROUS;
409 result = DANGEROUS;
410 } else if (response.verdict() == ClientDownloadResponse::UNCOMMON) {
411 reason = REASON_DOWNLOAD_UNCOMMON;
412 result = UNCOMMON;
413 } else if (response.verdict() == ClientDownloadResponse::DANGEROUS_HOST) {
414 reason = REASON_DOWNLOAD_DANGEROUS_HOST;
415 result = DANGEROUS_HOST;
416 } else if (
417 response.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED) {
418 reason = REASON_DOWNLOAD_POTENTIALLY_UNWANTED;
419 result = POTENTIALLY_UNWANTED;
420 } else {
421 LOG(DFATAL) << "Unknown download response verdict: "
422 << response.verdict();
423 reason = REASON_INVALID_RESPONSE_VERDICT;
424 result = UNKNOWN;
426 DownloadFeedbackService::MaybeStorePingsForDownload(
427 result, item_, client_download_request_data_, data);
429 // We don't need the fetcher anymore.
430 fetcher_.reset();
431 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration",
432 base::TimeTicks::Now() - start_time_);
433 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestNetworkDuration",
434 base::TimeTicks::Now() - request_start_time_);
435 FinishRequest(result, reason);
438 static bool IsSupportedDownload(const content::DownloadItem& item,
439 const base::FilePath& target_path,
440 DownloadCheckResultReason* reason,
441 ClientDownloadRequest::DownloadType* type) {
442 if (item.GetUrlChain().empty()) {
443 *reason = REASON_EMPTY_URL_CHAIN;
444 return false;
446 const GURL& final_url = item.GetUrlChain().back();
447 if (!final_url.is_valid() || final_url.is_empty()) {
448 *reason = REASON_INVALID_URL;
449 return false;
451 if (!download_protection_util::IsSupportedBinaryFile(target_path)) {
452 *reason = REASON_NOT_BINARY_FILE;
453 return false;
455 if ((!final_url.IsStandard() && !final_url.SchemeIsBlob() &&
456 !final_url.SchemeIs(url::kDataScheme)) ||
457 final_url.SchemeIsFile()) {
458 *reason = REASON_UNSUPPORTED_URL_SCHEME;
459 return false;
461 *type = download_protection_util::GetDownloadType(target_path);
462 return true;
465 private:
466 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
467 friend class base::DeleteHelper<CheckClientDownloadRequest>;
469 ~CheckClientDownloadRequest() override {
470 DCHECK_CURRENTLY_ON(BrowserThread::UI);
471 DCHECK(item_ == NULL);
474 void OnFileFeatureExtractionDone() {
475 // This can run in any thread, since it just posts more messages.
477 // TODO(noelutz): DownloadInfo should also contain the IP address of
478 // every URL in the redirect chain. We also should check whether the
479 // download URL is hosted on the internal network.
480 BrowserThread::PostTask(
481 BrowserThread::IO,
482 FROM_HERE,
483 base::Bind(&CheckClientDownloadRequest::CheckWhitelists, this));
485 // We wait until after the file checks finish to start the timeout, as
486 // windows can cause permissions errors if the timeout fired while we were
487 // checking the file signature and we tried to complete the download.
488 BrowserThread::PostTask(
489 BrowserThread::UI,
490 FROM_HERE,
491 base::Bind(&CheckClientDownloadRequest::StartTimeout, this));
494 void StartExtractFileFeatures() {
495 DCHECK_CURRENTLY_ON(BrowserThread::UI);
496 DCHECK(item_); // Called directly from Start(), item should still exist.
497 // Since we do blocking I/O, offload this to a worker thread.
498 // The task does not need to block shutdown.
499 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
500 FROM_HERE,
501 base::Bind(&CheckClientDownloadRequest::ExtractFileFeatures,
502 this, item_->GetFullPath()),
503 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
506 void ExtractFileFeatures(const base::FilePath& file_path) {
507 base::TimeTicks start_time = base::TimeTicks::Now();
508 binary_feature_extractor_->CheckSignature(file_path, &signature_info_);
509 bool is_signed = (signature_info_.certificate_chain_size() > 0);
510 if (is_signed) {
511 DVLOG(2) << "Downloaded a signed binary: " << file_path.value();
512 } else {
513 DVLOG(2) << "Downloaded an unsigned binary: "
514 << file_path.value();
516 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed);
517 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime",
518 base::TimeTicks::Now() - start_time);
520 start_time = base::TimeTicks::Now();
521 image_headers_.reset(new ClientDownloadRequest_ImageHeaders());
522 if (!binary_feature_extractor_->ExtractImageFeatures(
523 file_path,
524 BinaryFeatureExtractor::kDefaultOptions,
525 image_headers_.get(),
526 nullptr /* signed_data */)) {
527 image_headers_.reset();
529 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractImageHeadersTime",
530 base::TimeTicks::Now() - start_time);
532 OnFileFeatureExtractionDone();
535 void StartExtractZipFeatures() {
536 DCHECK_CURRENTLY_ON(BrowserThread::UI);
537 DCHECK(item_); // Called directly from Start(), item should still exist.
538 zip_analysis_start_time_ = base::TimeTicks::Now();
539 // We give the zip analyzer a weak pointer to this object. Since the
540 // analyzer is refcounted, it might outlive the request.
541 analyzer_ = new SandboxedZipAnalyzer(
542 item_->GetFullPath(),
543 base::Bind(&CheckClientDownloadRequest::OnZipAnalysisFinished,
544 weakptr_factory_.GetWeakPtr()));
545 analyzer_->Start();
548 void OnZipAnalysisFinished(const zip_analyzer::Results& results) {
549 DCHECK_CURRENTLY_ON(BrowserThread::UI);
550 DCHECK_EQ(ClientDownloadRequest::ZIPPED_EXECUTABLE, type_);
551 if (!service_)
552 return;
553 if (results.success) {
554 zipped_executable_ = results.has_executable;
555 archived_binary_.CopyFrom(results.archived_binary);
556 DVLOG(1) << "Zip analysis finished for " << item_->GetFullPath().value()
557 << ", has_executable=" << results.has_executable
558 << " has_archive=" << results.has_archive;
559 } else {
560 DVLOG(1) << "Zip analysis failed for " << item_->GetFullPath().value();
562 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
563 zipped_executable_);
564 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable",
565 results.has_archive && !zipped_executable_);
566 UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime",
567 base::TimeTicks::Now() - zip_analysis_start_time_);
568 for (const auto& file_extension : results.archived_archive_filetypes)
569 RecordArchivedArchiveFileExtensionType(file_extension);
571 if (!zipped_executable_ && !results.has_archive) {
572 PostFinishTask(UNKNOWN, REASON_ARCHIVE_WITHOUT_BINARIES);
573 return;
576 if (!zipped_executable_ && results.has_archive)
577 type_ = ClientDownloadRequest::ZIPPED_ARCHIVE;
578 OnFileFeatureExtractionDone();
581 static void RecordCountOfSignedOrWhitelistedDownload() {
582 UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
585 void CheckWhitelists() {
586 DCHECK_CURRENTLY_ON(BrowserThread::IO);
588 if (!database_manager_.get()) {
589 PostFinishTask(UNKNOWN, REASON_SB_DISABLED);
590 return;
593 const GURL& url = url_chain_.back();
594 // TODO(asanka): This may acquire a lock on the SB DB on the IO thread.
595 if (url.is_valid() && database_manager_->MatchDownloadWhitelistUrl(url)) {
596 DVLOG(2) << url << " is on the download whitelist.";
597 RecordCountOfSignedOrWhitelistedDownload();
598 // TODO(grt): Continue processing without uploading so that
599 // ClientDownloadRequest callbacks can be run even for this type of safe
600 // download.
601 PostFinishTask(SAFE, REASON_WHITELISTED_URL);
602 return;
605 if (signature_info_.trusted()) {
606 RecordCountOfSignedOrWhitelistedDownload();
607 for (int i = 0; i < signature_info_.certificate_chain_size(); ++i) {
608 if (CertificateChainIsWhitelisted(
609 signature_info_.certificate_chain(i))) {
610 // TODO(grt): Continue processing without uploading so that
611 // ClientDownloadRequest callbacks can be run even for this type of
612 // safe download.
613 PostFinishTask(SAFE, REASON_TRUSTED_EXECUTABLE);
614 return;
619 if (!pingback_enabled_) {
620 PostFinishTask(UNKNOWN, REASON_PING_DISABLED);
621 return;
624 // Currently, the UI is only enabled on Windows and OSX so we don't even
625 // bother with pinging the server if we're not on one of those platforms.
626 // TODO(noelutz): change this code once the UI is done for Linux.
627 #if defined(OS_WIN) || defined(OS_MACOSX)
628 // The URLFetcher is owned by the UI thread, so post a message to
629 // start the pingback.
630 BrowserThread::PostTask(
631 BrowserThread::UI,
632 FROM_HERE,
633 base::Bind(&CheckClientDownloadRequest::GetTabRedirects, this));
634 #else
635 PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED);
636 #endif
639 void GetTabRedirects() {
640 DCHECK_CURRENTLY_ON(BrowserThread::UI);
641 if (!service_)
642 return;
644 if (!tab_url_.is_valid()) {
645 SendRequest();
646 return;
649 Profile* profile = Profile::FromBrowserContext(item_->GetBrowserContext());
650 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
651 profile, ServiceAccessType::EXPLICIT_ACCESS);
652 if (!history) {
653 SendRequest();
654 return;
657 history->QueryRedirectsTo(
658 tab_url_,
659 base::Bind(&CheckClientDownloadRequest::OnGotTabRedirects,
660 base::Unretained(this),
661 tab_url_),
662 &request_tracker_);
665 void OnGotTabRedirects(const GURL& url,
666 const history::RedirectList* redirect_list) {
667 DCHECK_CURRENTLY_ON(BrowserThread::UI);
668 DCHECK_EQ(url, tab_url_);
669 if (!service_)
670 return;
672 if (!redirect_list->empty()) {
673 tab_redirects_.insert(
674 tab_redirects_.end(), redirect_list->rbegin(), redirect_list->rend());
677 SendRequest();
680 void SendRequest() {
681 DCHECK_CURRENTLY_ON(BrowserThread::UI);
683 // This is our last chance to check whether the request has been canceled
684 // before sending it.
685 if (!service_)
686 return;
687 bool is_extended_reporting = false;
688 if (item_->GetBrowserContext()) {
689 Profile* profile =
690 Profile::FromBrowserContext(item_->GetBrowserContext());
691 is_extended_reporting = profile &&
692 profile->GetPrefs()->GetBoolean(
693 prefs::kSafeBrowsingExtendedReportingEnabled);
696 ClientDownloadRequest request;
697 if (is_extended_reporting) {
698 request.mutable_population()->set_user_population(
699 ChromeUserPopulation::EXTENDED_REPORTING);
700 } else {
701 request.mutable_population()->set_user_population(
702 ChromeUserPopulation::SAFE_BROWSING);
704 request.set_url(SanitizeUrl(item_->GetUrlChain().back()));
705 request.mutable_digests()->set_sha256(item_->GetHash());
706 request.set_length(item_->GetReceivedBytes());
707 for (size_t i = 0; i < item_->GetUrlChain().size(); ++i) {
708 ClientDownloadRequest::Resource* resource = request.add_resources();
709 resource->set_url(SanitizeUrl(item_->GetUrlChain()[i]));
710 if (i == item_->GetUrlChain().size() - 1) {
711 // The last URL in the chain is the download URL.
712 resource->set_type(ClientDownloadRequest::DOWNLOAD_URL);
713 resource->set_referrer(SanitizeUrl(item_->GetReferrerUrl()));
714 DVLOG(2) << "dl url " << resource->url();
715 if (!item_->GetRemoteAddress().empty()) {
716 resource->set_remote_ip(item_->GetRemoteAddress());
717 DVLOG(2) << " dl url remote addr: " << resource->remote_ip();
719 DVLOG(2) << "dl referrer " << resource->referrer();
720 } else {
721 DVLOG(2) << "dl redirect " << i << " " << resource->url();
722 resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT);
724 // TODO(noelutz): fill out the remote IP addresses.
726 // TODO(mattm): fill out the remote IP addresses for tab resources.
727 for (size_t i = 0; i < tab_redirects_.size(); ++i) {
728 ClientDownloadRequest::Resource* resource = request.add_resources();
729 DVLOG(2) << "tab redirect " << i << " " << tab_redirects_[i].spec();
730 resource->set_url(SanitizeUrl(tab_redirects_[i]));
731 resource->set_type(ClientDownloadRequest::TAB_REDIRECT);
733 if (tab_url_.is_valid()) {
734 ClientDownloadRequest::Resource* resource = request.add_resources();
735 resource->set_url(SanitizeUrl(tab_url_));
736 DVLOG(2) << "tab url " << resource->url();
737 resource->set_type(ClientDownloadRequest::TAB_URL);
738 if (tab_referrer_url_.is_valid()) {
739 resource->set_referrer(SanitizeUrl(tab_referrer_url_));
740 DVLOG(2) << "tab referrer " << resource->referrer();
744 request.set_user_initiated(item_->HasUserGesture());
745 request.set_file_basename(
746 item_->GetTargetFilePath().BaseName().AsUTF8Unsafe());
747 request.set_download_type(type_);
748 request.mutable_signature()->CopyFrom(signature_info_);
749 if (image_headers_)
750 request.set_allocated_image_headers(image_headers_.release());
751 if (zipped_executable_)
752 request.mutable_archived_binary()->Swap(&archived_binary_);
753 if (!request.SerializeToString(&client_download_request_data_)) {
754 FinishRequest(UNKNOWN, REASON_INVALID_REQUEST_PROTO);
755 return;
757 service_->client_download_request_callbacks_.Notify(item_, &request);
759 DVLOG(2) << "Sending a request for URL: "
760 << item_->GetUrlChain().back();
761 fetcher_ = net::URLFetcher::Create(0 /* ID used for testing */,
762 GetDownloadRequestUrl(),
763 net::URLFetcher::POST, this);
764 fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
765 fetcher_->SetAutomaticallyRetryOn5xx(false); // Don't retry on error.
766 fetcher_->SetRequestContext(service_->request_context_getter_.get());
767 fetcher_->SetUploadData("application/octet-stream",
768 client_download_request_data_);
769 request_start_time_ = base::TimeTicks::Now();
770 UMA_HISTOGRAM_COUNTS("SBClientDownload.DownloadRequestPayloadSize",
771 client_download_request_data_.size());
772 fetcher_->Start();
775 void PostFinishTask(DownloadCheckResult result,
776 DownloadCheckResultReason reason) {
777 BrowserThread::PostTask(
778 BrowserThread::UI,
779 FROM_HERE,
780 base::Bind(&CheckClientDownloadRequest::FinishRequest, this, result,
781 reason));
784 void FinishRequest(DownloadCheckResult result,
785 DownloadCheckResultReason reason) {
786 DCHECK_CURRENTLY_ON(BrowserThread::UI);
787 if (finished_) {
788 return;
790 finished_ = true;
791 // Ensure the timeout task is cancelled while we still have a non-zero
792 // refcount. (crbug.com/240449)
793 weakptr_factory_.InvalidateWeakPtrs();
794 if (!request_start_time_.is_null()) {
795 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestNetworkStats",
796 reason,
797 REASON_MAX);
799 if (!timeout_start_time_.is_null()) {
800 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestTimeoutStats",
801 reason,
802 REASON_MAX);
803 if (reason != REASON_REQUEST_CANCELED) {
804 UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestTimeoutDuration",
805 base::TimeTicks::Now() - timeout_start_time_);
808 if (service_) {
809 DVLOG(2) << "SafeBrowsing download verdict for: "
810 << item_->DebugString(true) << " verdict:" << reason
811 << " result:" << result;
812 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
813 reason,
814 REASON_MAX);
815 callback_.Run(result);
816 item_->RemoveObserver(this);
817 item_ = NULL;
818 DownloadProtectionService* service = service_;
819 service_ = NULL;
820 service->RequestFinished(this);
821 // DownloadProtectionService::RequestFinished will decrement our refcount,
822 // so we may be deleted now.
823 } else {
824 callback_.Run(UNKNOWN);
828 bool CertificateChainIsWhitelisted(
829 const ClientDownloadRequest_CertificateChain& chain) {
830 DCHECK_CURRENTLY_ON(BrowserThread::IO);
831 if (chain.element_size() < 2) {
832 // We need to have both a signing certificate and its issuer certificate
833 // present to construct a whitelist entry.
834 return false;
836 scoped_refptr<net::X509Certificate> cert =
837 net::X509Certificate::CreateFromBytes(
838 chain.element(0).certificate().data(),
839 chain.element(0).certificate().size());
840 if (!cert.get()) {
841 return false;
844 for (int i = 1; i < chain.element_size(); ++i) {
845 scoped_refptr<net::X509Certificate> issuer =
846 net::X509Certificate::CreateFromBytes(
847 chain.element(i).certificate().data(),
848 chain.element(i).certificate().size());
849 if (!issuer.get()) {
850 return false;
852 std::vector<std::string> whitelist_strings;
853 DownloadProtectionService::GetCertificateWhitelistStrings(
854 *cert.get(), *issuer.get(), &whitelist_strings);
855 for (size_t j = 0; j < whitelist_strings.size(); ++j) {
856 if (database_manager_->MatchDownloadWhitelistString(
857 whitelist_strings[j])) {
858 DVLOG(2) << "Certificate matched whitelist, cert="
859 << cert->subject().GetDisplayName()
860 << " issuer=" << issuer->subject().GetDisplayName();
861 return true;
864 cert = issuer;
866 return false;
869 // The DownloadItem we are checking. Will be NULL if the request has been
870 // canceled. Must be accessed only on UI thread.
871 content::DownloadItem* item_;
872 // Copies of data from |item_| for access on other threads.
873 std::vector<GURL> url_chain_;
874 GURL referrer_url_;
875 // URL chain of redirects leading to (but not including) |tab_url|.
876 std::vector<GURL> tab_redirects_;
877 // URL and referrer of the window the download was started from.
878 GURL tab_url_;
879 GURL tab_referrer_url_;
881 bool zipped_executable_;
882 ClientDownloadRequest_SignatureInfo signature_info_;
883 scoped_ptr<ClientDownloadRequest_ImageHeaders> image_headers_;
884 google::protobuf::RepeatedPtrField<ClientDownloadRequest_ArchivedBinary>
885 archived_binary_;
886 CheckDownloadCallback callback_;
887 // Will be NULL if the request has been canceled.
888 DownloadProtectionService* service_;
889 scoped_refptr<BinaryFeatureExtractor> binary_feature_extractor_;
890 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
891 const bool pingback_enabled_;
892 scoped_ptr<net::URLFetcher> fetcher_;
893 scoped_refptr<SandboxedZipAnalyzer> analyzer_;
894 base::TimeTicks zip_analysis_start_time_;
895 bool finished_;
896 ClientDownloadRequest::DownloadType type_;
897 std::string client_download_request_data_;
898 base::CancelableTaskTracker request_tracker_; // For HistoryService lookup.
899 base::TimeTicks start_time_; // Used for stats.
900 base::TimeTicks timeout_start_time_;
901 base::TimeTicks request_start_time_;
902 base::WeakPtrFactory<CheckClientDownloadRequest> weakptr_factory_;
904 DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest);
907 DownloadProtectionService::DownloadProtectionService(
908 SafeBrowsingService* sb_service,
909 net::URLRequestContextGetter* request_context_getter)
910 : request_context_getter_(request_context_getter),
911 enabled_(false),
912 binary_feature_extractor_(new BinaryFeatureExtractor()),
913 download_request_timeout_ms_(kDownloadRequestTimeoutMs),
914 feedback_service_(new DownloadFeedbackService(
915 request_context_getter, BrowserThread::GetBlockingPool())) {
917 if (sb_service) {
918 ui_manager_ = sb_service->ui_manager();
919 database_manager_ = sb_service->database_manager();
923 DownloadProtectionService::~DownloadProtectionService() {
924 DCHECK_CURRENTLY_ON(BrowserThread::UI);
925 CancelPendingRequests();
928 void DownloadProtectionService::SetEnabled(bool enabled) {
929 DCHECK_CURRENTLY_ON(BrowserThread::UI);
930 if (enabled == enabled_) {
931 return;
933 enabled_ = enabled;
934 if (!enabled_) {
935 CancelPendingRequests();
939 void DownloadProtectionService::CheckClientDownload(
940 content::DownloadItem* item,
941 const CheckDownloadCallback& callback) {
942 scoped_refptr<CheckClientDownloadRequest> request(
943 new CheckClientDownloadRequest(item, callback, this,
944 database_manager_,
945 binary_feature_extractor_.get()));
946 download_requests_.insert(request);
947 request->Start();
950 void DownloadProtectionService::CheckDownloadUrl(
951 const content::DownloadItem& item,
952 const CheckDownloadCallback& callback) {
953 DCHECK(!item.GetUrlChain().empty());
954 scoped_refptr<DownloadUrlSBClient> client(
955 new DownloadUrlSBClient(item, callback, ui_manager_, database_manager_));
956 // The client will release itself once it is done.
957 BrowserThread::PostTask(
958 BrowserThread::IO,
959 FROM_HERE,
960 base::Bind(&DownloadUrlSBClient::StartCheck, client));
963 bool DownloadProtectionService::IsSupportedDownload(
964 const content::DownloadItem& item,
965 const base::FilePath& target_path) const {
966 // Currently, the UI is only enabled on Windows and OSX. On Linux we still
967 // want to show the dangerous file type warning if the file is possibly
968 // dangerous which means we have to always return false here.
969 #if defined(OS_WIN) || defined(OS_MACOSX)
970 DownloadCheckResultReason reason = REASON_MAX;
971 ClientDownloadRequest::DownloadType type =
972 ClientDownloadRequest::WIN_EXECUTABLE;
973 return (CheckClientDownloadRequest::IsSupportedDownload(
974 item, target_path, &reason, &type) &&
975 (ClientDownloadRequest::CHROME_EXTENSION != type));
976 #else
977 return false;
978 #endif
981 DownloadProtectionService::ClientDownloadRequestSubscription
982 DownloadProtectionService::RegisterClientDownloadRequestCallback(
983 const ClientDownloadRequestCallback& callback) {
984 DCHECK_CURRENTLY_ON(BrowserThread::UI);
985 return client_download_request_callbacks_.Add(callback);
988 void DownloadProtectionService::CancelPendingRequests() {
989 DCHECK_CURRENTLY_ON(BrowserThread::UI);
990 for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
991 download_requests_.begin();
992 it != download_requests_.end();) {
993 // We need to advance the iterator before we cancel because canceling
994 // the request will invalidate it when RequestFinished is called below.
995 scoped_refptr<CheckClientDownloadRequest> tmp = *it++;
996 tmp->Cancel();
998 DCHECK(download_requests_.empty());
1001 void DownloadProtectionService::RequestFinished(
1002 CheckClientDownloadRequest* request) {
1003 DCHECK_CURRENTLY_ON(BrowserThread::UI);
1004 std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
1005 download_requests_.find(request);
1006 DCHECK(it != download_requests_.end());
1007 download_requests_.erase(*it);
1010 void DownloadProtectionService::ShowDetailsForDownload(
1011 const content::DownloadItem& item,
1012 content::PageNavigator* navigator) {
1013 GURL learn_more_url(chrome::kDownloadScanningLearnMoreURL);
1014 learn_more_url = google_util::AppendGoogleLocaleParam(
1015 learn_more_url, g_browser_process->GetApplicationLocale());
1016 learn_more_url = net::AppendQueryParameter(
1017 learn_more_url, "ctx",
1018 base::IntToString(static_cast<int>(item.GetDangerType())));
1019 navigator->OpenURL(
1020 content::OpenURLParams(learn_more_url,
1021 content::Referrer(),
1022 NEW_FOREGROUND_TAB,
1023 ui::PAGE_TRANSITION_LINK,
1024 false));
1027 namespace {
1028 // Escapes a certificate attribute so that it can be used in a whitelist
1029 // entry. Currently, we only escape slashes, since they are used as a
1030 // separator between attributes.
1031 std::string EscapeCertAttribute(const std::string& attribute) {
1032 std::string escaped;
1033 for (size_t i = 0; i < attribute.size(); ++i) {
1034 if (attribute[i] == '%') {
1035 escaped.append("%25");
1036 } else if (attribute[i] == '/') {
1037 escaped.append("%2F");
1038 } else {
1039 escaped.push_back(attribute[i]);
1042 return escaped;
1044 } // namespace
1046 // static
1047 void DownloadProtectionService::GetCertificateWhitelistStrings(
1048 const net::X509Certificate& certificate,
1049 const net::X509Certificate& issuer,
1050 std::vector<std::string>* whitelist_strings) {
1051 // The whitelist paths are in the format:
1052 // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit]
1054 // Any of CN, O, or OU may be omitted from the whitelist entry, in which
1055 // case they match anything. However, the attributes that do appear will
1056 // always be in the order shown above. At least one attribute will always
1057 // be present.
1059 const net::CertPrincipal& subject = certificate.subject();
1060 std::vector<std::string> ou_tokens;
1061 for (size_t i = 0; i < subject.organization_unit_names.size(); ++i) {
1062 ou_tokens.push_back(
1063 "/OU=" + EscapeCertAttribute(subject.organization_unit_names[i]));
1066 std::vector<std::string> o_tokens;
1067 for (size_t i = 0; i < subject.organization_names.size(); ++i) {
1068 o_tokens.push_back(
1069 "/O=" + EscapeCertAttribute(subject.organization_names[i]));
1072 std::string cn_token;
1073 if (!subject.common_name.empty()) {
1074 cn_token = "/CN=" + EscapeCertAttribute(subject.common_name);
1077 std::set<std::string> paths_to_check;
1078 if (!cn_token.empty()) {
1079 paths_to_check.insert(cn_token);
1081 for (size_t i = 0; i < o_tokens.size(); ++i) {
1082 paths_to_check.insert(cn_token + o_tokens[i]);
1083 paths_to_check.insert(o_tokens[i]);
1084 for (size_t j = 0; j < ou_tokens.size(); ++j) {
1085 paths_to_check.insert(cn_token + o_tokens[i] + ou_tokens[j]);
1086 paths_to_check.insert(o_tokens[i] + ou_tokens[j]);
1089 for (size_t i = 0; i < ou_tokens.size(); ++i) {
1090 paths_to_check.insert(cn_token + ou_tokens[i]);
1091 paths_to_check.insert(ou_tokens[i]);
1094 std::string issuer_fp = base::HexEncode(issuer.fingerprint().data,
1095 sizeof(issuer.fingerprint().data));
1096 for (std::set<std::string>::iterator it = paths_to_check.begin();
1097 it != paths_to_check.end(); ++it) {
1098 whitelist_strings->push_back("cert/" + issuer_fp + *it);
1102 // static
1103 GURL DownloadProtectionService::GetDownloadRequestUrl() {
1104 GURL url(kDownloadRequestUrl);
1105 std::string api_key = google_apis::GetAPIKey();
1106 if (!api_key.empty())
1107 url = url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
1109 return url;
1112 } // namespace safe_browsing