Home | History | Annotate | Download | only in safe_browsing
      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                                            GURL(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   if (item.GetDangerType() ==
    884       content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED)
    885     learn_more_url = GURL(chrome::kDownloadPotentiallyUnwantedLearnMoreURL);
    886   navigator->OpenURL(
    887       content::OpenURLParams(learn_more_url,
    888                              content::Referrer(),
    889                              NEW_FOREGROUND_TAB,
    890                              content::PAGE_TRANSITION_LINK,
    891                              false));
    892 }
    893 
    894 namespace {
    895 // Escapes a certificate attribute so that it can be used in a whitelist
    896 // entry.  Currently, we only escape slashes, since they are used as a
    897 // separator between attributes.
    898 std::string EscapeCertAttribute(const std::string& attribute) {
    899   std::string escaped;
    900   for (size_t i = 0; i < attribute.size(); ++i) {
    901     if (attribute[i] == '%') {
    902       escaped.append("%25");
    903     } else if (attribute[i] == '/') {
    904       escaped.append("%2F");
    905     } else {
    906       escaped.push_back(attribute[i]);
    907     }
    908   }
    909   return escaped;
    910 }
    911 }  // namespace
    912 
    913 // static
    914 void DownloadProtectionService::GetCertificateWhitelistStrings(
    915     const net::X509Certificate& certificate,
    916     const net::X509Certificate& issuer,
    917     std::vector<std::string>* whitelist_strings) {
    918   // The whitelist paths are in the format:
    919   // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit]
    920   //
    921   // Any of CN, O, or OU may be omitted from the whitelist entry, in which
    922   // case they match anything.  However, the attributes that do appear will
    923   // always be in the order shown above.  At least one attribute will always
    924   // be present.
    925 
    926   const net::CertPrincipal& subject = certificate.subject();
    927   std::vector<std::string> ou_tokens;
    928   for (size_t i = 0; i < subject.organization_unit_names.size(); ++i) {
    929     ou_tokens.push_back(
    930         "/OU=" + EscapeCertAttribute(subject.organization_unit_names[i]));
    931   }
    932 
    933   std::vector<std::string> o_tokens;
    934   for (size_t i = 0; i < subject.organization_names.size(); ++i) {
    935     o_tokens.push_back(
    936         "/O=" + EscapeCertAttribute(subject.organization_names[i]));
    937   }
    938 
    939   std::string cn_token;
    940   if (!subject.common_name.empty()) {
    941     cn_token = "/CN=" + EscapeCertAttribute(subject.common_name);
    942   }
    943 
    944   std::set<std::string> paths_to_check;
    945   if (!cn_token.empty()) {
    946     paths_to_check.insert(cn_token);
    947   }
    948   for (size_t i = 0; i < o_tokens.size(); ++i) {
    949     paths_to_check.insert(cn_token + o_tokens[i]);
    950     paths_to_check.insert(o_tokens[i]);
    951     for (size_t j = 0; j < ou_tokens.size(); ++j) {
    952       paths_to_check.insert(cn_token + o_tokens[i] + ou_tokens[j]);
    953       paths_to_check.insert(o_tokens[i] + ou_tokens[j]);
    954     }
    955   }
    956   for (size_t i = 0; i < ou_tokens.size(); ++i) {
    957     paths_to_check.insert(cn_token + ou_tokens[i]);
    958     paths_to_check.insert(ou_tokens[i]);
    959   }
    960 
    961   std::string issuer_fp = base::HexEncode(issuer.fingerprint().data,
    962                                           sizeof(issuer.fingerprint().data));
    963   for (std::set<std::string>::iterator it = paths_to_check.begin();
    964        it != paths_to_check.end(); ++it) {
    965     whitelist_strings->push_back("cert/" + issuer_fp + *it);
    966   }
    967 }
    968 
    969 // static
    970 std::string DownloadProtectionService::GetDownloadRequestUrl() {
    971   std::string url = kDownloadRequestUrl;
    972   std::string api_key = google_apis::GetAPIKey();
    973   if (!api_key.empty()) {
    974     base::StringAppendF(&url, "?key=%s",
    975                         net::EscapeQueryParamValue(api_key, true).c_str());
    976   }
    977   return url;
    978 }
    979 
    980 }  // namespace safe_browsing
    981