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