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/field_trial.h"
     13 #include "base/metrics/histogram.h"
     14 #include "base/metrics/sparse_histogram.h"
     15 #include "base/sequenced_task_runner_helpers.h"
     16 #include "base/stl_util.h"
     17 #include "base/strings/string_number_conversions.h"
     18 #include "base/strings/string_util.h"
     19 #include "base/strings/stringprintf.h"
     20 #include "base/task/cancelable_task_tracker.h"
     21 #include "base/threading/sequenced_worker_pool.h"
     22 #include "base/time/time.h"
     23 #include "chrome/browser/browser_process.h"
     24 #include "chrome/browser/history/history_service.h"
     25 #include "chrome/browser/history/history_service_factory.h"
     26 #include "chrome/browser/safe_browsing/binary_feature_extractor.h"
     27 #include "chrome/browser/safe_browsing/download_feedback_service.h"
     28 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
     29 #include "chrome/browser/safe_browsing/sandboxed_zip_analyzer.h"
     30 #include "chrome/browser/ui/browser.h"
     31 #include "chrome/browser/ui/browser_list.h"
     32 #include "chrome/common/safe_browsing/csd.pb.h"
     33 #include "chrome/common/safe_browsing/download_protection_util.h"
     34 #include "chrome/common/safe_browsing/zip_analyzer.h"
     35 #include "chrome/common/url_constants.h"
     36 #include "components/google/core/browser/google_util.h"
     37 #include "content/public/browser/browser_thread.h"
     38 #include "content/public/browser/download_item.h"
     39 #include "content/public/browser/page_navigator.h"
     40 #include "google_apis/google_api_keys.h"
     41 #include "net/base/escape.h"
     42 #include "net/base/load_flags.h"
     43 #include "net/cert/x509_cert_types.h"
     44 #include "net/cert/x509_certificate.h"
     45 #include "net/http/http_status_code.h"
     46 #include "net/url_request/url_fetcher.h"
     47 #include "net/url_request/url_fetcher_delegate.h"
     48 #include "net/url_request/url_request_context_getter.h"
     49 #include "net/url_request/url_request_status.h"
     50 
     51 using content::BrowserThread;
     52 
     53 namespace {
     54 static const int64 kDownloadRequestTimeoutMs = 7000;
     55 }  // namespace
     56 
     57 namespace safe_browsing {
     58 
     59 const char DownloadProtectionService::kDownloadRequestUrl[] =
     60     "https://sb-ssl.google.com/safebrowsing/clientreport/download";
     61 
     62 namespace {
     63 // List of extensions for which we track some UMA stats.
     64 enum MaliciousExtensionType {
     65   EXTENSION_EXE,
     66   EXTENSION_MSI,
     67   EXTENSION_CAB,
     68   EXTENSION_SYS,
     69   EXTENSION_SCR,
     70   EXTENSION_DRV,
     71   EXTENSION_BAT,
     72   EXTENSION_ZIP,
     73   EXTENSION_RAR,
     74   EXTENSION_DLL,
     75   EXTENSION_PIF,
     76   EXTENSION_COM,
     77   EXTENSION_JAR,
     78   EXTENSION_CLASS,
     79   EXTENSION_PDF,
     80   EXTENSION_VB,
     81   EXTENSION_REG,
     82   EXTENSION_GRP,
     83   EXTENSION_OTHER,  // Groups all other extensions into one bucket.
     84   EXTENSION_CRX,
     85   EXTENSION_APK,
     86   EXTENSION_DMG,
     87   EXTENSION_PKG,
     88   EXTENSION_TORRENT,
     89   EXTENSION_MAX,
     90 };
     91 
     92 MaliciousExtensionType GetExtensionType(const base::FilePath& f) {
     93   if (f.MatchesExtension(FILE_PATH_LITERAL(".exe"))) return EXTENSION_EXE;
     94   if (f.MatchesExtension(FILE_PATH_LITERAL(".msi"))) return EXTENSION_MSI;
     95   if (f.MatchesExtension(FILE_PATH_LITERAL(".cab"))) return EXTENSION_CAB;
     96   if (f.MatchesExtension(FILE_PATH_LITERAL(".sys"))) return EXTENSION_SYS;
     97   if (f.MatchesExtension(FILE_PATH_LITERAL(".scr"))) return EXTENSION_SCR;
     98   if (f.MatchesExtension(FILE_PATH_LITERAL(".drv"))) return EXTENSION_DRV;
     99   if (f.MatchesExtension(FILE_PATH_LITERAL(".bat"))) return EXTENSION_BAT;
    100   if (f.MatchesExtension(FILE_PATH_LITERAL(".zip"))) return EXTENSION_ZIP;
    101   if (f.MatchesExtension(FILE_PATH_LITERAL(".rar"))) return EXTENSION_RAR;
    102   if (f.MatchesExtension(FILE_PATH_LITERAL(".dll"))) return EXTENSION_DLL;
    103   if (f.MatchesExtension(FILE_PATH_LITERAL(".pif"))) return EXTENSION_PIF;
    104   if (f.MatchesExtension(FILE_PATH_LITERAL(".com"))) return EXTENSION_COM;
    105   if (f.MatchesExtension(FILE_PATH_LITERAL(".jar"))) return EXTENSION_JAR;
    106   if (f.MatchesExtension(FILE_PATH_LITERAL(".class"))) return EXTENSION_CLASS;
    107   if (f.MatchesExtension(FILE_PATH_LITERAL(".pdf"))) return EXTENSION_PDF;
    108   if (f.MatchesExtension(FILE_PATH_LITERAL(".vb"))) return EXTENSION_VB;
    109   if (f.MatchesExtension(FILE_PATH_LITERAL(".reg"))) return EXTENSION_REG;
    110   if (f.MatchesExtension(FILE_PATH_LITERAL(".grp"))) return EXTENSION_GRP;
    111   if (f.MatchesExtension(FILE_PATH_LITERAL(".crx"))) return EXTENSION_CRX;
    112   if (f.MatchesExtension(FILE_PATH_LITERAL(".apk"))) return EXTENSION_APK;
    113   if (f.MatchesExtension(FILE_PATH_LITERAL(".dmg"))) return EXTENSION_DMG;
    114   if (f.MatchesExtension(FILE_PATH_LITERAL(".pkg"))) return EXTENSION_PKG;
    115   if (f.MatchesExtension(FILE_PATH_LITERAL(".torrent")))
    116     return EXTENSION_TORRENT;
    117   return EXTENSION_OTHER;
    118 }
    119 
    120 void RecordFileExtensionType(const base::FilePath& file) {
    121   UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadExtensions",
    122                             GetExtensionType(file),
    123                             EXTENSION_MAX);
    124 }
    125 
    126 // Enumerate for histogramming purposes.
    127 // DO NOT CHANGE THE ORDERING OF THESE VALUES (different histogram data will
    128 // be mixed together based on their values).
    129 enum SBStatsType {
    130   DOWNLOAD_URL_CHECKS_TOTAL,
    131   DOWNLOAD_URL_CHECKS_CANCELED,
    132   DOWNLOAD_URL_CHECKS_MALWARE,
    133 
    134   DOWNLOAD_HASH_CHECKS_TOTAL,
    135   DOWNLOAD_HASH_CHECKS_MALWARE,
    136 
    137   // Memory space for histograms is determined by the max.
    138   // ALWAYS ADD NEW VALUES BEFORE THIS ONE.
    139   DOWNLOAD_CHECKS_MAX
    140 };
    141 }  // namespace
    142 
    143 // Parent SafeBrowsing::Client class used to lookup the bad binary
    144 // URL and digest list.  There are two sub-classes (one for each list).
    145 class DownloadSBClient
    146     : public SafeBrowsingDatabaseManager::Client,
    147       public base::RefCountedThreadSafe<DownloadSBClient> {
    148  public:
    149   DownloadSBClient(
    150       const content::DownloadItem& item,
    151       const DownloadProtectionService::CheckDownloadCallback& callback,
    152       const scoped_refptr<SafeBrowsingUIManager>& ui_manager,
    153       SBStatsType total_type,
    154       SBStatsType dangerous_type)
    155       : sha256_hash_(item.GetHash()),
    156         url_chain_(item.GetUrlChain()),
    157         referrer_url_(item.GetReferrerUrl()),
    158         callback_(callback),
    159         ui_manager_(ui_manager),
    160         start_time_(base::TimeTicks::Now()),
    161         total_type_(total_type),
    162         dangerous_type_(dangerous_type) {}
    163 
    164   virtual void StartCheck() = 0;
    165   virtual bool IsDangerous(SBThreatType threat_type) const = 0;
    166 
    167  protected:
    168   friend class base::RefCountedThreadSafe<DownloadSBClient>;
    169   virtual ~DownloadSBClient() {}
    170 
    171   void CheckDone(SBThreatType threat_type) {
    172     DownloadProtectionService::DownloadCheckResult result =
    173         IsDangerous(threat_type) ?
    174         DownloadProtectionService::DANGEROUS :
    175         DownloadProtectionService::SAFE;
    176     BrowserThread::PostTask(BrowserThread::UI,
    177                             FROM_HERE,
    178                             base::Bind(callback_, result));
    179     UpdateDownloadCheckStats(total_type_);
    180     if (threat_type != SB_THREAT_TYPE_SAFE) {
    181       UpdateDownloadCheckStats(dangerous_type_);
    182       BrowserThread::PostTask(
    183           BrowserThread::UI,
    184           FROM_HERE,
    185           base::Bind(&DownloadSBClient::ReportMalware,
    186                      this, threat_type));
    187     }
    188   }
    189 
    190   void ReportMalware(SBThreatType threat_type) {
    191     std::string post_data;
    192     if (!sha256_hash_.empty())
    193       post_data += base::HexEncode(sha256_hash_.data(),
    194                                    sha256_hash_.size()) + "\n";
    195     for (size_t i = 0; i < url_chain_.size(); ++i) {
    196       post_data += url_chain_[i].spec() + "\n";
    197     }
    198     ui_manager_->ReportSafeBrowsingHit(
    199         url_chain_.back(),  // malicious_url
    200         url_chain_.front(), // page_url
    201         referrer_url_,
    202         true,  // is_subresource
    203         threat_type,
    204         post_data);
    205   }
    206 
    207   void UpdateDownloadCheckStats(SBStatsType stat_type) {
    208     UMA_HISTOGRAM_ENUMERATION("SB2.DownloadChecks",
    209                               stat_type,
    210                               DOWNLOAD_CHECKS_MAX);
    211   }
    212 
    213   std::string sha256_hash_;
    214   std::vector<GURL> url_chain_;
    215   GURL referrer_url_;
    216   DownloadProtectionService::CheckDownloadCallback callback_;
    217   scoped_refptr<SafeBrowsingUIManager> ui_manager_;
    218   base::TimeTicks start_time_;
    219 
    220  private:
    221   const SBStatsType total_type_;
    222   const SBStatsType dangerous_type_;
    223 
    224   DISALLOW_COPY_AND_ASSIGN(DownloadSBClient);
    225 };
    226 
    227 class DownloadUrlSBClient : public DownloadSBClient {
    228  public:
    229   DownloadUrlSBClient(
    230       const content::DownloadItem& item,
    231       const DownloadProtectionService::CheckDownloadCallback& callback,
    232       const scoped_refptr<SafeBrowsingUIManager>& ui_manager,
    233       const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager)
    234       : DownloadSBClient(item, callback, ui_manager,
    235                          DOWNLOAD_URL_CHECKS_TOTAL,
    236                          DOWNLOAD_URL_CHECKS_MALWARE),
    237         database_manager_(database_manager) { }
    238 
    239   virtual void StartCheck() OVERRIDE {
    240     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    241     if (!database_manager_.get() ||
    242         database_manager_->CheckDownloadUrl(url_chain_, this)) {
    243       CheckDone(SB_THREAT_TYPE_SAFE);
    244     } else {
    245       AddRef();  // SafeBrowsingService takes a pointer not a scoped_refptr.
    246     }
    247   }
    248 
    249   virtual bool IsDangerous(SBThreatType threat_type) const OVERRIDE {
    250     return threat_type == SB_THREAT_TYPE_BINARY_MALWARE_URL;
    251   }
    252 
    253   virtual void OnCheckDownloadUrlResult(const std::vector<GURL>& url_chain,
    254                                         SBThreatType threat_type) OVERRIDE {
    255     CheckDone(threat_type);
    256     UMA_HISTOGRAM_TIMES("SB2.DownloadUrlCheckDuration",
    257                         base::TimeTicks::Now() - start_time_);
    258     Release();
    259   }
    260 
    261  protected:
    262   virtual ~DownloadUrlSBClient() {}
    263 
    264  private:
    265   scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
    266 
    267   DISALLOW_COPY_AND_ASSIGN(DownloadUrlSBClient);
    268 };
    269 
    270 class DownloadProtectionService::CheckClientDownloadRequest
    271     : public base::RefCountedThreadSafe<
    272           DownloadProtectionService::CheckClientDownloadRequest,
    273           BrowserThread::DeleteOnUIThread>,
    274       public net::URLFetcherDelegate,
    275       public content::DownloadItem::Observer {
    276  public:
    277   CheckClientDownloadRequest(
    278       content::DownloadItem* item,
    279       const CheckDownloadCallback& callback,
    280       DownloadProtectionService* service,
    281       const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
    282       BinaryFeatureExtractor* binary_feature_extractor)
    283       : item_(item),
    284         url_chain_(item->GetUrlChain()),
    285         referrer_url_(item->GetReferrerUrl()),
    286         tab_url_(item->GetTabUrl()),
    287         tab_referrer_url_(item->GetTabReferrerUrl()),
    288         zipped_executable_(false),
    289         callback_(callback),
    290         service_(service),
    291         binary_feature_extractor_(binary_feature_extractor),
    292         database_manager_(database_manager),
    293         pingback_enabled_(service_->enabled()),
    294         finished_(false),
    295         type_(ClientDownloadRequest::WIN_EXECUTABLE),
    296         weakptr_factory_(this),
    297         start_time_(base::TimeTicks::Now()) {
    298     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    299     item_->AddObserver(this);
    300   }
    301 
    302   void Start() {
    303     VLOG(2) << "Starting SafeBrowsing download check for: "
    304             << item_->DebugString(true);
    305     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    306     // TODO(noelutz): implement some cache to make sure we don't issue the same
    307     // request over and over again if a user downloads the same binary multiple
    308     // times.
    309     DownloadCheckResultReason reason = REASON_MAX;
    310     if (!IsSupportedDownload(
    311         *item_, item_->GetTargetFilePath(), &reason, &type_)) {
    312       switch (reason) {
    313         case REASON_EMPTY_URL_CHAIN:
    314         case REASON_INVALID_URL:
    315           PostFinishTask(UNKNOWN, reason);
    316           return;
    317 
    318         case REASON_NOT_BINARY_FILE:
    319           RecordFileExtensionType(item_->GetTargetFilePath());
    320           PostFinishTask(UNKNOWN, reason);
    321           return;
    322 
    323         default:
    324           // We only expect the reasons explicitly handled above.
    325           NOTREACHED();
    326       }
    327     }
    328     RecordFileExtensionType(item_->GetTargetFilePath());
    329 
    330     // Compute features from the file contents. Note that we record histograms
    331     // based on the result, so this runs regardless of whether the pingbacks
    332     // are enabled.
    333     if (item_->GetTargetFilePath().MatchesExtension(
    334         FILE_PATH_LITERAL(".zip"))) {
    335       StartExtractZipFeatures();
    336     } else {
    337       DCHECK(!download_protection_util::IsArchiveFile(
    338           item_->GetTargetFilePath()));
    339       StartExtractFileFeatures();
    340     }
    341   }
    342 
    343   // Start a timeout to cancel the request if it takes too long.
    344   // This should only be called after we have finished accessing the file.
    345   void StartTimeout() {
    346     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    347     if (!service_) {
    348       // Request has already been cancelled.
    349       return;
    350     }
    351     timeout_start_time_ = base::TimeTicks::Now();
    352     BrowserThread::PostDelayedTask(
    353         BrowserThread::UI,
    354         FROM_HERE,
    355         base::Bind(&CheckClientDownloadRequest::Cancel,
    356                    weakptr_factory_.GetWeakPtr()),
    357         base::TimeDelta::FromMilliseconds(
    358             service_->download_request_timeout_ms()));
    359   }
    360 
    361   // Canceling a request will cause us to always report the result as UNKNOWN
    362   // unless a pending request is about to call FinishRequest.
    363   void Cancel() {
    364     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    365     if (fetcher_.get()) {
    366       // The DownloadProtectionService is going to release its reference, so we
    367       // might be destroyed before the URLFetcher completes.  Cancel the
    368       // fetcher so it does not try to invoke OnURLFetchComplete.
    369       fetcher_.reset();
    370     }
    371     // Note: If there is no fetcher, then some callback is still holding a
    372     // reference to this object.  We'll eventually wind up in some method on
    373     // the UI thread that will call FinishRequest() again.  If FinishRequest()
    374     // is called a second time, it will be a no-op.
    375     FinishRequest(UNKNOWN, REASON_REQUEST_CANCELED);
    376     // Calling FinishRequest might delete this object, we may be deleted by
    377     // this point.
    378   }
    379 
    380   // content::DownloadItem::Observer implementation.
    381   virtual void OnDownloadDestroyed(content::DownloadItem* download) OVERRIDE {
    382     Cancel();
    383     DCHECK(item_ == NULL);
    384   }
    385 
    386   // From the net::URLFetcherDelegate interface.
    387   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
    388     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    389     DCHECK_EQ(source, fetcher_.get());
    390     VLOG(2) << "Received a response for URL: "
    391             << item_->GetUrlChain().back() << ": success="
    392             << source->GetStatus().is_success() << " response_code="
    393             << source->GetResponseCode();
    394     if (source->GetStatus().is_success()) {
    395       UMA_HISTOGRAM_SPARSE_SLOWLY(
    396           "SBClientDownload.DownloadRequestResponseCode",
    397           source->GetResponseCode());
    398     }
    399     UMA_HISTOGRAM_SPARSE_SLOWLY(
    400         "SBClientDownload.DownloadRequestNetError",
    401         -source->GetStatus().error());
    402     DownloadCheckResultReason reason = REASON_SERVER_PING_FAILED;
    403     DownloadCheckResult result = UNKNOWN;
    404     if (source->GetStatus().is_success() &&
    405         net::HTTP_OK == source->GetResponseCode()) {
    406       ClientDownloadResponse response;
    407       std::string data;
    408       bool got_data = source->GetResponseAsString(&data);
    409       DCHECK(got_data);
    410       if (!response.ParseFromString(data)) {
    411         reason = REASON_INVALID_RESPONSE_PROTO;
    412         result = UNKNOWN;
    413       } else if (response.verdict() == ClientDownloadResponse::SAFE) {
    414         reason = REASON_DOWNLOAD_SAFE;
    415         result = SAFE;
    416       } else if (service_ && !service_->IsSupportedDownload(
    417           *item_, item_->GetTargetFilePath())) {
    418         // The client of the download protection service assumes that we don't
    419         // support this download so we cannot return any other verdict than
    420         // UNKNOWN even if the server says it's dangerous to download this file.
    421         // Note: if service_ is NULL we already cancelled the request and
    422         // returned UNKNOWN.
    423         reason = REASON_DOWNLOAD_NOT_SUPPORTED;
    424         result = UNKNOWN;
    425       } else if (response.verdict() == ClientDownloadResponse::DANGEROUS) {
    426         reason = REASON_DOWNLOAD_DANGEROUS;
    427         result = DANGEROUS;
    428       } else if (response.verdict() == ClientDownloadResponse::UNCOMMON) {
    429         reason = REASON_DOWNLOAD_UNCOMMON;
    430         result = UNCOMMON;
    431       } else if (response.verdict() == ClientDownloadResponse::DANGEROUS_HOST) {
    432         reason = REASON_DOWNLOAD_DANGEROUS_HOST;
    433         result = DANGEROUS_HOST;
    434       } else if (
    435           response.verdict() == ClientDownloadResponse::POTENTIALLY_UNWANTED) {
    436         reason = REASON_DOWNLOAD_POTENTIALLY_UNWANTED;
    437         result = POTENTIALLY_UNWANTED;
    438       } else {
    439         LOG(DFATAL) << "Unknown download response verdict: "
    440                     << response.verdict();
    441         reason = REASON_INVALID_RESPONSE_VERDICT;
    442         result = UNKNOWN;
    443       }
    444       DownloadFeedbackService::MaybeStorePingsForDownload(
    445           result, item_, client_download_request_data_, data);
    446     }
    447     // We don't need the fetcher anymore.
    448     fetcher_.reset();
    449     UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestDuration",
    450                         base::TimeTicks::Now() - start_time_);
    451     UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestNetworkDuration",
    452                         base::TimeTicks::Now() - request_start_time_);
    453     FinishRequest(result, reason);
    454   }
    455 
    456   static bool IsSupportedDownload(const content::DownloadItem& item,
    457                                   const base::FilePath& target_path,
    458                                   DownloadCheckResultReason* reason,
    459                                   ClientDownloadRequest::DownloadType* type) {
    460     if (item.GetUrlChain().empty()) {
    461       *reason = REASON_EMPTY_URL_CHAIN;
    462       return false;
    463     }
    464     const GURL& final_url = item.GetUrlChain().back();
    465     if (!final_url.is_valid() || final_url.is_empty() ||
    466         !final_url.IsStandard() || final_url.SchemeIsFile()) {
    467       *reason = REASON_INVALID_URL;
    468       return false;
    469     }
    470     if (!download_protection_util::IsBinaryFile(target_path)) {
    471       *reason = REASON_NOT_BINARY_FILE;
    472       return false;
    473     }
    474     *type = download_protection_util::GetDownloadType(target_path);
    475     return true;
    476   }
    477 
    478  private:
    479   friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
    480   friend class base::DeleteHelper<CheckClientDownloadRequest>;
    481 
    482   virtual ~CheckClientDownloadRequest() {
    483     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    484     DCHECK(item_ == NULL);
    485   }
    486 
    487   void OnFileFeatureExtractionDone() {
    488     // This can run in any thread, since it just posts more messages.
    489 
    490     // TODO(noelutz): DownloadInfo should also contain the IP address of
    491     // every URL in the redirect chain.  We also should check whether the
    492     // download URL is hosted on the internal network.
    493     BrowserThread::PostTask(
    494         BrowserThread::IO,
    495         FROM_HERE,
    496         base::Bind(&CheckClientDownloadRequest::CheckWhitelists, this));
    497 
    498     // We wait until after the file checks finish to start the timeout, as
    499     // windows can cause permissions errors if the timeout fired while we were
    500     // checking the file signature and we tried to complete the download.
    501     BrowserThread::PostTask(
    502         BrowserThread::UI,
    503         FROM_HERE,
    504         base::Bind(&CheckClientDownloadRequest::StartTimeout, this));
    505   }
    506 
    507   void StartExtractFileFeatures() {
    508     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    509     DCHECK(item_);  // Called directly from Start(), item should still exist.
    510     // Since we do blocking I/O, offload this to a worker thread.
    511     // The task does not need to block shutdown.
    512     BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
    513         FROM_HERE,
    514         base::Bind(&CheckClientDownloadRequest::ExtractFileFeatures,
    515                    this, item_->GetFullPath()),
    516         base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
    517   }
    518 
    519   void ExtractFileFeatures(const base::FilePath& file_path) {
    520     base::TimeTicks start_time = base::TimeTicks::Now();
    521     binary_feature_extractor_->CheckSignature(file_path, &signature_info_);
    522     bool is_signed = (signature_info_.certificate_chain_size() > 0);
    523     if (is_signed) {
    524       VLOG(2) << "Downloaded a signed binary: " << file_path.value();
    525     } else {
    526       VLOG(2) << "Downloaded an unsigned binary: "
    527               << file_path.value();
    528     }
    529     UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed);
    530     UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime",
    531                         base::TimeTicks::Now() - start_time);
    532 
    533     start_time = base::TimeTicks::Now();
    534     binary_feature_extractor_->ExtractImageHeaders(file_path, &image_headers_);
    535     UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractImageHeadersTime",
    536                         base::TimeTicks::Now() - start_time);
    537 
    538     OnFileFeatureExtractionDone();
    539   }
    540 
    541   void StartExtractZipFeatures() {
    542     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    543     DCHECK(item_);  // Called directly from Start(), item should still exist.
    544     zip_analysis_start_time_ = base::TimeTicks::Now();
    545     // We give the zip analyzer a weak pointer to this object.  Since the
    546     // analyzer is refcounted, it might outlive the request.
    547     analyzer_ = new SandboxedZipAnalyzer(
    548         item_->GetFullPath(),
    549         base::Bind(&CheckClientDownloadRequest::OnZipAnalysisFinished,
    550                    weakptr_factory_.GetWeakPtr()));
    551     analyzer_->Start();
    552   }
    553 
    554   void OnZipAnalysisFinished(const zip_analyzer::Results& results) {
    555     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    556     if (!service_)
    557       return;
    558     if (results.success) {
    559       zipped_executable_ = results.has_executable;
    560       VLOG(1) << "Zip analysis finished for " << item_->GetFullPath().value()
    561               << ", has_executable=" << results.has_executable
    562               << " has_archive=" << results.has_archive;
    563     } else {
    564       VLOG(1) << "Zip analysis failed for " << item_->GetFullPath().value();
    565     }
    566     UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasExecutable",
    567                           zipped_executable_);
    568     UMA_HISTOGRAM_BOOLEAN("SBClientDownload.ZipFileHasArchiveButNoExecutable",
    569                           results.has_archive && !zipped_executable_);
    570     UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractZipFeaturesTime",
    571                         base::TimeTicks::Now() - zip_analysis_start_time_);
    572 
    573     if (!zipped_executable_) {
    574       PostFinishTask(UNKNOWN, REASON_ARCHIVE_WITHOUT_BINARIES);
    575       return;
    576     }
    577     OnFileFeatureExtractionDone();
    578   }
    579 
    580   static void RecordCountOfSignedOrWhitelistedDownload() {
    581     UMA_HISTOGRAM_COUNTS("SBClientDownload.SignedOrWhitelistedDownload", 1);
    582   }
    583 
    584   void CheckWhitelists() {
    585     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    586 
    587     if (!database_manager_.get()) {
    588       PostFinishTask(UNKNOWN, REASON_SB_DISABLED);
    589       return;
    590     }
    591 
    592     const GURL& url = url_chain_.back();
    593     if (url.is_valid() && database_manager_->MatchDownloadWhitelistUrl(url)) {
    594       VLOG(2) << url << " is on the download whitelist.";
    595       RecordCountOfSignedOrWhitelistedDownload();
    596       PostFinishTask(SAFE, REASON_WHITELISTED_URL);
    597       return;
    598     }
    599 
    600     if (signature_info_.trusted()) {
    601       RecordCountOfSignedOrWhitelistedDownload();
    602       for (int i = 0; i < signature_info_.certificate_chain_size(); ++i) {
    603         if (CertificateChainIsWhitelisted(
    604                 signature_info_.certificate_chain(i))) {
    605           PostFinishTask(SAFE, REASON_TRUSTED_EXECUTABLE);
    606           return;
    607         }
    608       }
    609     }
    610 
    611     if (!pingback_enabled_) {
    612       PostFinishTask(UNKNOWN, REASON_PING_DISABLED);
    613       return;
    614     }
    615 
    616     // Currently, the UI only works on Windows so we don't even bother with
    617     // pinging the server if we're not on Windows.
    618     // TODO(noelutz): change this code once the UI is done for Linux and Mac.
    619 #if defined(OS_MACOSX)
    620   // TODO(mattm): remove this (see crbug.com/414834).
    621   if (base::FieldTrialList::FindFullName("SafeBrowsingOSXClientDownloadPings")
    622       != "Enabled") {
    623     PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED);
    624     return;
    625   }
    626 #endif
    627 #if defined(OS_WIN) || defined(OS_MACOSX)
    628     // The URLFetcher is owned by the UI thread, so post a message to
    629     // start the pingback.
    630     BrowserThread::PostTask(
    631         BrowserThread::UI,
    632         FROM_HERE,
    633         base::Bind(&CheckClientDownloadRequest::GetTabRedirects, this));
    634 #else
    635     PostFinishTask(UNKNOWN, REASON_OS_NOT_SUPPORTED);
    636 #endif
    637   }
    638 
    639   void GetTabRedirects() {
    640     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    641     if (!tab_url_.is_valid()) {
    642       SendRequest();
    643       return;
    644     }
    645 
    646     Profile* profile = Profile::FromBrowserContext(item_->GetBrowserContext());
    647     HistoryService* history =
    648         HistoryServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS);
    649     if (!history) {
    650       SendRequest();
    651       return;
    652     }
    653 
    654     history->QueryRedirectsTo(
    655         tab_url_,
    656         base::Bind(&CheckClientDownloadRequest::OnGotTabRedirects,
    657                    base::Unretained(this),
    658                    tab_url_),
    659         &request_tracker_);
    660   }
    661 
    662   void OnGotTabRedirects(const GURL& url,
    663                          const history::RedirectList* redirect_list) {
    664     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    665     DCHECK_EQ(url, tab_url_);
    666 
    667     if (!redirect_list->empty()) {
    668       tab_redirects_.insert(
    669           tab_redirects_.end(), redirect_list->rbegin(), redirect_list->rend());
    670     }
    671 
    672     SendRequest();
    673   }
    674 
    675   void SendRequest() {
    676     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    677 
    678     // This is our last chance to check whether the request has been canceled
    679     // before sending it.
    680     if (!service_)
    681       return;
    682 
    683     ClientDownloadRequest request;
    684     request.set_url(item_->GetUrlChain().back().spec());
    685     request.mutable_digests()->set_sha256(item_->GetHash());
    686     request.set_length(item_->GetReceivedBytes());
    687     for (size_t i = 0; i < item_->GetUrlChain().size(); ++i) {
    688       ClientDownloadRequest::Resource* resource = request.add_resources();
    689       resource->set_url(item_->GetUrlChain()[i].spec());
    690       if (i == item_->GetUrlChain().size() - 1) {
    691         // The last URL in the chain is the download URL.
    692         resource->set_type(ClientDownloadRequest::DOWNLOAD_URL);
    693         resource->set_referrer(item_->GetReferrerUrl().spec());
    694         DVLOG(2) << "dl url " << resource->url();
    695         if (!item_->GetRemoteAddress().empty()) {
    696           resource->set_remote_ip(item_->GetRemoteAddress());
    697           DVLOG(2) << "  dl url remote addr: " << resource->remote_ip();
    698         }
    699         DVLOG(2) << "dl referrer " << resource->referrer();
    700       } else {
    701         DVLOG(2) << "dl redirect " << i << " " << resource->url();
    702         resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT);
    703       }
    704       // TODO(noelutz): fill out the remote IP addresses.
    705     }
    706     // TODO(mattm): fill out the remote IP addresses for tab resources.
    707     for (size_t i = 0; i < tab_redirects_.size(); ++i) {
    708       ClientDownloadRequest::Resource* resource = request.add_resources();
    709       DVLOG(2) << "tab redirect " << i << " " << tab_redirects_[i].spec();
    710       resource->set_url(tab_redirects_[i].spec());
    711       resource->set_type(ClientDownloadRequest::TAB_REDIRECT);
    712     }
    713     if (tab_url_.is_valid()) {
    714       ClientDownloadRequest::Resource* resource = request.add_resources();
    715       resource->set_url(tab_url_.spec());
    716       DVLOG(2) << "tab url " << resource->url();
    717       resource->set_type(ClientDownloadRequest::TAB_URL);
    718       if (tab_referrer_url_.is_valid()) {
    719         resource->set_referrer(tab_referrer_url_.spec());
    720         DVLOG(2) << "tab referrer " << resource->referrer();
    721       }
    722     }
    723 
    724     request.set_user_initiated(item_->HasUserGesture());
    725     request.set_file_basename(
    726         item_->GetTargetFilePath().BaseName().AsUTF8Unsafe());
    727     request.set_download_type(type_);
    728     request.mutable_signature()->CopyFrom(signature_info_);
    729     request.mutable_image_headers()->CopyFrom(image_headers_);
    730     if (!request.SerializeToString(&client_download_request_data_)) {
    731       FinishRequest(UNKNOWN, REASON_INVALID_REQUEST_PROTO);
    732       return;
    733     }
    734 
    735     VLOG(2) << "Sending a request for URL: "
    736             << item_->GetUrlChain().back();
    737     fetcher_.reset(net::URLFetcher::Create(0 /* ID used for testing */,
    738                                            GetDownloadRequestUrl(),
    739                                            net::URLFetcher::POST,
    740                                            this));
    741     fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
    742     fetcher_->SetAutomaticallyRetryOn5xx(false);  // Don't retry on error.
    743     fetcher_->SetRequestContext(service_->request_context_getter_.get());
    744     fetcher_->SetUploadData("application/octet-stream",
    745                             client_download_request_data_);
    746     request_start_time_ = base::TimeTicks::Now();
    747     UMA_HISTOGRAM_COUNTS("SBClientDownload.DownloadRequestPayloadSize",
    748                          client_download_request_data_.size());
    749     fetcher_->Start();
    750   }
    751 
    752   void PostFinishTask(DownloadCheckResult result,
    753                       DownloadCheckResultReason reason) {
    754     BrowserThread::PostTask(
    755         BrowserThread::UI,
    756         FROM_HERE,
    757         base::Bind(&CheckClientDownloadRequest::FinishRequest, this, result,
    758                    reason));
    759   }
    760 
    761   void FinishRequest(DownloadCheckResult result,
    762                      DownloadCheckResultReason reason) {
    763     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    764     if (finished_) {
    765       return;
    766     }
    767     finished_ = true;
    768     // Ensure the timeout task is cancelled while we still have a non-zero
    769     // refcount. (crbug.com/240449)
    770     weakptr_factory_.InvalidateWeakPtrs();
    771     if (!request_start_time_.is_null()) {
    772       UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestNetworkStats",
    773                                 reason,
    774                                 REASON_MAX);
    775     }
    776     if (!timeout_start_time_.is_null()) {
    777       UMA_HISTOGRAM_ENUMERATION("SBClientDownload.DownloadRequestTimeoutStats",
    778                                 reason,
    779                                 REASON_MAX);
    780       if (reason != REASON_REQUEST_CANCELED) {
    781         UMA_HISTOGRAM_TIMES("SBClientDownload.DownloadRequestTimeoutDuration",
    782                             base::TimeTicks::Now() - timeout_start_time_);
    783       }
    784     }
    785     if (service_) {
    786       VLOG(2) << "SafeBrowsing download verdict for: "
    787               << item_->DebugString(true) << " verdict:" << reason
    788               << " result:" << result;
    789       UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
    790                                 reason,
    791                                 REASON_MAX);
    792 #if defined(OS_MACOSX)
    793       // OSX is currently sending pings only for evaluation purposes, ignore
    794       // the result for now.
    795       // TODO(mattm): remove this and update the ifdef in
    796       // DownloadItemImpl::IsDangerous (see crbug.com/413968).
    797       result = UNKNOWN;
    798 #endif
    799       callback_.Run(result);
    800       item_->RemoveObserver(this);
    801       item_ = NULL;
    802       DownloadProtectionService* service = service_;
    803       service_ = NULL;
    804       service->RequestFinished(this);
    805       // DownloadProtectionService::RequestFinished will decrement our refcount,
    806       // so we may be deleted now.
    807     } else {
    808       callback_.Run(UNKNOWN);
    809     }
    810   }
    811 
    812   bool CertificateChainIsWhitelisted(
    813       const ClientDownloadRequest_CertificateChain& chain) {
    814     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    815     if (chain.element_size() < 2) {
    816       // We need to have both a signing certificate and its issuer certificate
    817       // present to construct a whitelist entry.
    818       return false;
    819     }
    820     scoped_refptr<net::X509Certificate> cert =
    821         net::X509Certificate::CreateFromBytes(
    822             chain.element(0).certificate().data(),
    823             chain.element(0).certificate().size());
    824     if (!cert.get()) {
    825       return false;
    826     }
    827 
    828     for (int i = 1; i < chain.element_size(); ++i) {
    829       scoped_refptr<net::X509Certificate> issuer =
    830           net::X509Certificate::CreateFromBytes(
    831               chain.element(i).certificate().data(),
    832               chain.element(i).certificate().size());
    833       if (!issuer.get()) {
    834         return false;
    835       }
    836       std::vector<std::string> whitelist_strings;
    837       DownloadProtectionService::GetCertificateWhitelistStrings(
    838           *cert.get(), *issuer.get(), &whitelist_strings);
    839       for (size_t j = 0; j < whitelist_strings.size(); ++j) {
    840         if (database_manager_->MatchDownloadWhitelistString(
    841                 whitelist_strings[j])) {
    842           VLOG(2) << "Certificate matched whitelist, cert="
    843                   << cert->subject().GetDisplayName()
    844                   << " issuer=" << issuer->subject().GetDisplayName();
    845           return true;
    846         }
    847       }
    848       cert = issuer;
    849     }
    850     return false;
    851   }
    852 
    853   // The DownloadItem we are checking. Will be NULL if the request has been
    854   // canceled. Must be accessed only on UI thread.
    855   content::DownloadItem* item_;
    856   // Copies of data from |item_| for access on other threads.
    857   std::vector<GURL> url_chain_;
    858   GURL referrer_url_;
    859   // URL chain of redirects leading to (but not including) |tab_url|.
    860   std::vector<GURL> tab_redirects_;
    861   // URL and referrer of the window the download was started from.
    862   GURL tab_url_;
    863   GURL tab_referrer_url_;
    864 
    865   bool zipped_executable_;
    866   ClientDownloadRequest_SignatureInfo signature_info_;
    867   ClientDownloadRequest_ImageHeaders image_headers_;
    868   CheckDownloadCallback callback_;
    869   // Will be NULL if the request has been canceled.
    870   DownloadProtectionService* service_;
    871   scoped_refptr<BinaryFeatureExtractor> binary_feature_extractor_;
    872   scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
    873   const bool pingback_enabled_;
    874   scoped_ptr<net::URLFetcher> fetcher_;
    875   scoped_refptr<SandboxedZipAnalyzer> analyzer_;
    876   base::TimeTicks zip_analysis_start_time_;
    877   bool finished_;
    878   ClientDownloadRequest::DownloadType type_;
    879   std::string client_download_request_data_;
    880   base::CancelableTaskTracker request_tracker_;  // For HistoryService lookup.
    881   base::WeakPtrFactory<CheckClientDownloadRequest> weakptr_factory_;
    882   base::TimeTicks start_time_;  // Used for stats.
    883   base::TimeTicks timeout_start_time_;
    884   base::TimeTicks request_start_time_;
    885 
    886   DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest);
    887 };
    888 
    889 DownloadProtectionService::DownloadProtectionService(
    890     SafeBrowsingService* sb_service,
    891     net::URLRequestContextGetter* request_context_getter)
    892     : request_context_getter_(request_context_getter),
    893       enabled_(false),
    894       binary_feature_extractor_(new BinaryFeatureExtractor()),
    895       download_request_timeout_ms_(kDownloadRequestTimeoutMs),
    896       feedback_service_(new DownloadFeedbackService(
    897           request_context_getter, BrowserThread::GetBlockingPool())) {
    898 
    899   if (sb_service) {
    900     ui_manager_ = sb_service->ui_manager();
    901     database_manager_ = sb_service->database_manager();
    902   }
    903 }
    904 
    905 DownloadProtectionService::~DownloadProtectionService() {
    906   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    907   CancelPendingRequests();
    908 }
    909 
    910 void DownloadProtectionService::SetEnabled(bool enabled) {
    911   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    912   if (enabled == enabled_) {
    913     return;
    914   }
    915   enabled_ = enabled;
    916   if (!enabled_) {
    917     CancelPendingRequests();
    918   }
    919 }
    920 
    921 void DownloadProtectionService::CheckClientDownload(
    922     content::DownloadItem* item,
    923     const CheckDownloadCallback& callback) {
    924   scoped_refptr<CheckClientDownloadRequest> request(
    925       new CheckClientDownloadRequest(item, callback, this,
    926                                      database_manager_,
    927                                      binary_feature_extractor_.get()));
    928   download_requests_.insert(request);
    929   request->Start();
    930 }
    931 
    932 void DownloadProtectionService::CheckDownloadUrl(
    933     const content::DownloadItem& item,
    934     const CheckDownloadCallback& callback) {
    935   DCHECK(!item.GetUrlChain().empty());
    936   scoped_refptr<DownloadUrlSBClient> client(
    937       new DownloadUrlSBClient(item, callback, ui_manager_, database_manager_));
    938   // The client will release itself once it is done.
    939   BrowserThread::PostTask(
    940         BrowserThread::IO,
    941         FROM_HERE,
    942         base::Bind(&DownloadUrlSBClient::StartCheck, client));
    943 }
    944 
    945 bool DownloadProtectionService::IsSupportedDownload(
    946     const content::DownloadItem& item,
    947     const base::FilePath& target_path) const {
    948   // Currently, the UI is only enabled on Windows.  On Mac we send the ping but
    949   // ignore the result (see ifdef in FinishRequest).  On Linux we still
    950   // want to show the dangerous file type warning if the file is possibly
    951   // dangerous which means we have to always return false here.
    952 #if defined(OS_WIN)
    953   DownloadCheckResultReason reason = REASON_MAX;
    954   ClientDownloadRequest::DownloadType type =
    955       ClientDownloadRequest::WIN_EXECUTABLE;
    956   return (CheckClientDownloadRequest::IsSupportedDownload(
    957               item, target_path, &reason, &type) &&
    958           (ClientDownloadRequest::CHROME_EXTENSION != type));
    959 #else
    960   return false;
    961 #endif
    962 }
    963 
    964 void DownloadProtectionService::CancelPendingRequests() {
    965   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    966   for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
    967            download_requests_.begin();
    968        it != download_requests_.end();) {
    969     // We need to advance the iterator before we cancel because canceling
    970     // the request will invalidate it when RequestFinished is called below.
    971     scoped_refptr<CheckClientDownloadRequest> tmp = *it++;
    972     tmp->Cancel();
    973   }
    974   DCHECK(download_requests_.empty());
    975 }
    976 
    977 void DownloadProtectionService::RequestFinished(
    978     CheckClientDownloadRequest* request) {
    979   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    980   std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it =
    981       download_requests_.find(request);
    982   DCHECK(it != download_requests_.end());
    983   download_requests_.erase(*it);
    984 }
    985 
    986 void DownloadProtectionService::ShowDetailsForDownload(
    987     const content::DownloadItem& item,
    988     content::PageNavigator* navigator) {
    989   GURL learn_more_url(chrome::kDownloadScanningLearnMoreURL);
    990   learn_more_url = google_util::AppendGoogleLocaleParam(
    991       learn_more_url, g_browser_process->GetApplicationLocale());
    992   navigator->OpenURL(
    993       content::OpenURLParams(learn_more_url,
    994                              content::Referrer(),
    995                              NEW_FOREGROUND_TAB,
    996                              ui::PAGE_TRANSITION_LINK,
    997                              false));
    998 }
    999 
   1000 namespace {
   1001 // Escapes a certificate attribute so that it can be used in a whitelist
   1002 // entry.  Currently, we only escape slashes, since they are used as a
   1003 // separator between attributes.
   1004 std::string EscapeCertAttribute(const std::string& attribute) {
   1005   std::string escaped;
   1006   for (size_t i = 0; i < attribute.size(); ++i) {
   1007     if (attribute[i] == '%') {
   1008       escaped.append("%25");
   1009     } else if (attribute[i] == '/') {
   1010       escaped.append("%2F");
   1011     } else {
   1012       escaped.push_back(attribute[i]);
   1013     }
   1014   }
   1015   return escaped;
   1016 }
   1017 }  // namespace
   1018 
   1019 // static
   1020 void DownloadProtectionService::GetCertificateWhitelistStrings(
   1021     const net::X509Certificate& certificate,
   1022     const net::X509Certificate& issuer,
   1023     std::vector<std::string>* whitelist_strings) {
   1024   // The whitelist paths are in the format:
   1025   // cert/<ascii issuer fingerprint>[/CN=common_name][/O=org][/OU=unit]
   1026   //
   1027   // Any of CN, O, or OU may be omitted from the whitelist entry, in which
   1028   // case they match anything.  However, the attributes that do appear will
   1029   // always be in the order shown above.  At least one attribute will always
   1030   // be present.
   1031 
   1032   const net::CertPrincipal& subject = certificate.subject();
   1033   std::vector<std::string> ou_tokens;
   1034   for (size_t i = 0; i < subject.organization_unit_names.size(); ++i) {
   1035     ou_tokens.push_back(
   1036         "/OU=" + EscapeCertAttribute(subject.organization_unit_names[i]));
   1037   }
   1038 
   1039   std::vector<std::string> o_tokens;
   1040   for (size_t i = 0; i < subject.organization_names.size(); ++i) {
   1041     o_tokens.push_back(
   1042         "/O=" + EscapeCertAttribute(subject.organization_names[i]));
   1043   }
   1044 
   1045   std::string cn_token;
   1046   if (!subject.common_name.empty()) {
   1047     cn_token = "/CN=" + EscapeCertAttribute(subject.common_name);
   1048   }
   1049 
   1050   std::set<std::string> paths_to_check;
   1051   if (!cn_token.empty()) {
   1052     paths_to_check.insert(cn_token);
   1053   }
   1054   for (size_t i = 0; i < o_tokens.size(); ++i) {
   1055     paths_to_check.insert(cn_token + o_tokens[i]);
   1056     paths_to_check.insert(o_tokens[i]);
   1057     for (size_t j = 0; j < ou_tokens.size(); ++j) {
   1058       paths_to_check.insert(cn_token + o_tokens[i] + ou_tokens[j]);
   1059       paths_to_check.insert(o_tokens[i] + ou_tokens[j]);
   1060     }
   1061   }
   1062   for (size_t i = 0; i < ou_tokens.size(); ++i) {
   1063     paths_to_check.insert(cn_token + ou_tokens[i]);
   1064     paths_to_check.insert(ou_tokens[i]);
   1065   }
   1066 
   1067   std::string issuer_fp = base::HexEncode(issuer.fingerprint().data,
   1068                                           sizeof(issuer.fingerprint().data));
   1069   for (std::set<std::string>::iterator it = paths_to_check.begin();
   1070        it != paths_to_check.end(); ++it) {
   1071     whitelist_strings->push_back("cert/" + issuer_fp + *it);
   1072   }
   1073 }
   1074 
   1075 // static
   1076 GURL DownloadProtectionService::GetDownloadRequestUrl() {
   1077   GURL url(kDownloadRequestUrl);
   1078   std::string api_key = google_apis::GetAPIKey();
   1079   if (!api_key.empty())
   1080     url = url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
   1081 
   1082   return url;
   1083 }
   1084 
   1085 }  // namespace safe_browsing
   1086