- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / safe_browsing / download_protection_service.cc
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.
4
5 #include "chrome/browser/safe_browsing/download_protection_service.h"
6
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"
44
45 using content::BrowserThread;
46
47 namespace {
48 static const int64 kDownloadRequestTimeoutMs = 3000;
49 }  // namespace
50
51 namespace safe_browsing {
52
53 const char DownloadProtectionService::kDownloadRequestUrl[] =
54     "https://sb-ssl.google.com/safebrowsing/clientreport/download";
55
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;
69 }
70
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,
95 };
96
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;
119 }
120
121 void RecordFileExtensionType(const base::FilePath& file) {
122   UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadExtensions",
123                             GetExtensionType(file),
124                             EXTENSION_MAX);
125 }
126
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,
134
135   DOWNLOAD_HASH_CHECKS_TOTAL,
136   DOWNLOAD_HASH_CHECKS_MALWARE,
137
138   // Memory space for histograms is determined by the max.
139   // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
140   DOWNLOAD_CHECKS_MAX
141 };
142 }  // namespace
143
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) {}
164
165   virtual void StartCheck() = 0;
166   virtual bool IsDangerous(SBThreatType threat_type) const = 0;
167
168  protected:
169   friend class base::RefCountedThreadSafe<DownloadSBClient>;
170   virtual ~DownloadSBClient() {}
171
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));
188     }
189   }
190
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";
198     }
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);
206   }
207
208   void UpdateDownloadCheckStats(SBStatsType stat_type) {
209     UMA_HISTOGRAM_ENUMERATION("SB2.DownloadChecks",
210                               stat_type,
211                               DOWNLOAD_CHECKS_MAX);
212   }
213
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_;
220
221  private:
222   const SBStatsType total_type_;
223   const SBStatsType dangerous_type_;
224
225   DISALLOW_COPY_AND_ASSIGN(DownloadSBClient);
226 };
227
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) { }
239
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.
247     }
248   }
249
250   virtual bool IsDangerous(SBThreatType threat_type) const OVERRIDE {
251     return threat_type == SB_THREAT_TYPE_BINARY_MALWARE_URL;
252   }
253
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();
260   }
261
262  protected:
263   virtual ~DownloadUrlSBClient() {}
264
265  private:
266   scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
267
268   DISALLOW_COPY_AND_ASSIGN(DownloadUrlSBClient);
269 };
270
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);
299   }
300
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;
316
317         case REASON_NOT_BINARY_FILE:
318           RecordFileExtensionType(item_->GetTargetFilePath());
319           PostFinishTask(SAFE, reason);
320           return;
321
322         default:
323           // We only expect the reasons explicitly handled above.
324           NOTREACHED();
325       }
326     }
327     RecordFileExtensionType(item_->GetTargetFilePath());
328
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();
339     }
340   }
341
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;
349     }
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()));
357   }
358
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();
368     }
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.
376   }
377
378   // content::DownloadItem::Observer implementation.
379   virtual void OnDownloadDestroyed(content::DownloadItem* download) OVERRIDE {
380     Cancel();
381     DCHECK(item_ == NULL);
382   }
383
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());
396     }
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;
437       }
438       DownloadFeedbackService::MaybeStorePingsForDownload(
439           result, item_, client_download_request_data_, data);
440     }
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);
446   }
447
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;
455     }
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;
461     }
462     if (!download_protection_util::IsBinaryFile(target_path)) {
463       *reason = REASON_NOT_BINARY_FILE;
464       return false;
465     }
466     *type = GetDownloadType(target_path);
467     return true;
468   }
469
470  private:
471   friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
472   friend class base::DeleteHelper<CheckClientDownloadRequest>;
473
474   virtual ~CheckClientDownloadRequest() {
475     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
476     DCHECK(item_ == NULL);
477   }
478
479   void OnFileFeatureExtractionDone() {
480     // This can run in any thread, since it just posts more messages.
481
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));
489
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));
497   }
498
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);
509   }
510
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();
520     }
521     UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed);
522     UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime",
523                         base::TimeTicks::Now() - start_time);
524
525     OnFileFeatureExtractionDone();
526   }
527
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();
539   }
540
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();
552     }
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_);
559
560     if (!zipped_executable_) {
561       PostFinishTask(SAFE, REASON_ARCHIVE_WITHOUT_BINARIES);
562       return;
563     }
564     OnFileFeatureExtractionDone();
565   }
566
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;
580         }
581       }
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;
588       }
589       if (reason != REASON_MAX || signature_info_.trusted()) {
590         UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
591       }
592     }
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;
599         }
600       }
601     }
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
620     }
621   }
622
623   void SendRequest() {
624     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
625
626     // This is our last chance to check whether the request has been canceled
627     // before sending it.
628     if (!service_)
629       return;
630
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());
644         }
645       } else {
646         resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT);
647       }
648       // TODO(noelutz): fill out the remote IP addresses.
649     }
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;
658     }
659
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();
674   }
675
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));
683   }
684
685   void FinishRequest(DownloadCheckResult result,
686                      DownloadCheckResultReason reason) {
687     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
688     if (finished_) {
689       return;
690     }
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);
711     }
712   }
713
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;
721     }
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;
728     }
729
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;
737       }
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;
748         }
749       }
750       cert = issuer;
751     }
752     return false;
753   }
754
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_;
761
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.
778
779   DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest);
780 };
781
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())) {
791
792   if (sb_service) {
793     ui_manager_ = sb_service->ui_manager();
794     database_manager_ = sb_service->database_manager();
795   }
796 }
797
798 DownloadProtectionService::~DownloadProtectionService() {
799   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
800   CancelPendingRequests();
801 }
802
803 void DownloadProtectionService::SetEnabled(bool enabled) {
804   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
805   if (enabled == enabled_) {
806     return;
807   }
808   enabled_ = enabled;
809   if (!enabled_) {
810     CancelPendingRequests();
811   }
812 }
813
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();
822 }
823
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));
835 }
836
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
855 }
856
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();
866   }
867   DCHECK(download_requests_.empty());
868 }
869
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);
877 }
878
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));
889 }
890
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]);
904     }
905   }
906   return escaped;
907 }
908 }  // namespace
909
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]
917   //
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.
922
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]));
928   }
929
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]));
934   }
935
936   std::string cn_token;
937   if (!subject.common_name.empty()) {
938     cn_token = "/CN=" + EscapeCertAttribute(subject.common_name);
939   }
940
941   std::set<std::string> paths_to_check;
942   if (!cn_token.empty()) {
943     paths_to_check.insert(cn_token);
944   }
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]);
951     }
952   }
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]);
956   }
957
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);
963   }
964 }
965
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));
972
973   return url;
974 }
975
976 }  // namespace safe_browsing