Home | History | Annotate | Download | only in renderer_host
      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/renderer_host/safe_browsing_resource_throttle.h"
      6 
      7 #include "base/logging.h"
      8 #include "chrome/browser/browser_process.h"
      9 #include "chrome/browser/prerender/prerender_tracker.h"
     10 #include "chrome/browser/renderer_host/chrome_url_request_user_data.h"
     11 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
     12 #include "content/public/browser/resource_controller.h"
     13 #include "net/base/load_flags.h"
     14 #include "net/url_request/url_request.h"
     15 
     16 // Maximum time in milliseconds to wait for the safe browsing service to
     17 // verify a URL. After this amount of time the outstanding check will be
     18 // aborted, and the URL will be treated as if it were safe.
     19 static const int kCheckUrlTimeoutMs = 5000;
     20 
     21 // TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more
     22 //               unit test coverage.
     23 
     24 SafeBrowsingResourceThrottle::SafeBrowsingResourceThrottle(
     25     const net::URLRequest* request,
     26     int render_process_host_id,
     27     int render_view_id,
     28     bool is_subresource,
     29     SafeBrowsingService* safe_browsing)
     30     : state_(STATE_NONE),
     31       defer_state_(DEFERRED_NONE),
     32       threat_type_(SB_THREAT_TYPE_SAFE),
     33       render_process_host_id_(render_process_host_id),
     34       render_view_id_(render_view_id),
     35       database_manager_(safe_browsing->database_manager()),
     36       ui_manager_(safe_browsing->ui_manager()),
     37       request_(request),
     38       is_subresource_(is_subresource) {
     39 }
     40 
     41 SafeBrowsingResourceThrottle::~SafeBrowsingResourceThrottle() {
     42   if (state_ == STATE_CHECKING_URL)
     43     database_manager_->CancelCheck(this);
     44 }
     45 
     46 void SafeBrowsingResourceThrottle::WillStartRequest(bool* defer) {
     47   // We need to check the new URL before starting the request.
     48   if (CheckUrl(request_->url()))
     49     return;
     50 
     51   // If the URL couldn't be verified synchronously, defer starting the
     52   // request until the check has completed.
     53   defer_state_ = DEFERRED_START;
     54   *defer = true;
     55 }
     56 
     57 void SafeBrowsingResourceThrottle::WillRedirectRequest(const GURL& new_url,
     58                                                        bool* defer) {
     59   CHECK(state_ == STATE_NONE);
     60   CHECK(defer_state_ == DEFERRED_NONE);
     61 
     62   // Save the redirect urls for possible malware detail reporting later.
     63   redirect_urls_.push_back(new_url);
     64 
     65   // We need to check the new URL before following the redirect.
     66   if (CheckUrl(new_url))
     67     return;
     68 
     69   // If the URL couldn't be verified synchronously, defer following the
     70   // redirect until the SafeBrowsing check is complete. Store the redirect
     71   // context so we can pass it on to other handlers once we have completed
     72   // our check.
     73   defer_state_ = DEFERRED_REDIRECT;
     74   *defer = true;
     75 }
     76 
     77 // SafeBrowsingService::Client implementation, called on the IO thread once
     78 // the URL has been classified.
     79 void SafeBrowsingResourceThrottle::OnCheckBrowseUrlResult(
     80     const GURL& url, SBThreatType threat_type) {
     81   CHECK(state_ == STATE_CHECKING_URL);
     82   CHECK(defer_state_ != DEFERRED_NONE);
     83   CHECK(url == url_being_checked_) << "Was expecting: " << url_being_checked_
     84                                    << " but got: " << url;
     85 
     86   timer_.Stop();  // Cancel the timeout timer.
     87   threat_type_ = threat_type;
     88   state_ = STATE_NONE;
     89 
     90   if (threat_type == SB_THREAT_TYPE_SAFE) {
     91     // Log how much time the safe browsing check cost us.
     92     ui_manager_->LogPauseDelay(base::TimeTicks::Now() - url_check_start_time_);
     93 
     94     // Continue the request.
     95     ResumeRequest();
     96   } else {
     97     bool should_show_blocking_page = true;
     98     if (request_->load_flags() & net::LOAD_PREFETCH) {
     99       // Don't prefetch resources that fail safe browsing, disallow
    100       // them.
    101       controller()->Cancel();
    102       should_show_blocking_page = false;
    103     } else {
    104       ChromeURLRequestUserData* user_data =
    105           ChromeURLRequestUserData::Get(request_);
    106       if (user_data && user_data->is_prerender()) {
    107         prerender::PrerenderTracker* prerender_tracker = g_browser_process->
    108             prerender_tracker();
    109         if (prerender_tracker->TryCancelOnIOThread(
    110                 render_process_host_id_,
    111                 render_view_id_,
    112                 prerender::FINAL_STATUS_SAFE_BROWSING)) {
    113           controller()->Cancel();
    114           should_show_blocking_page = false;
    115         }
    116       }
    117     }
    118     if (should_show_blocking_page)
    119       StartDisplayingBlockingPage(url, threat_type);
    120   }
    121 }
    122 
    123 void SafeBrowsingResourceThrottle::StartDisplayingBlockingPage(
    124     const GURL& url, SBThreatType threat_type) {
    125   CHECK(state_ == STATE_NONE);
    126   CHECK(defer_state_ != DEFERRED_NONE);
    127 
    128   state_ = STATE_DISPLAYING_BLOCKING_PAGE;
    129 
    130   ui_manager_->DisplayBlockingPage(
    131       url,
    132       request_->original_url(),
    133       redirect_urls_,
    134       is_subresource_,
    135       threat_type,
    136       base::Bind(
    137           &SafeBrowsingResourceThrottle::OnBlockingPageComplete, AsWeakPtr()),
    138       render_process_host_id_,
    139       render_view_id_);
    140 }
    141 
    142 // SafeBrowsingService::UrlCheckCallback implementation, called on the IO
    143 // thread when the user has decided to proceed with the current request, or
    144 // go back.
    145 void SafeBrowsingResourceThrottle::OnBlockingPageComplete(bool proceed) {
    146   CHECK(state_ == STATE_DISPLAYING_BLOCKING_PAGE);
    147   state_ = STATE_NONE;
    148 
    149   if (proceed) {
    150     threat_type_ = SB_THREAT_TYPE_SAFE;
    151     ResumeRequest();
    152   } else {
    153     controller()->Cancel();
    154   }
    155 }
    156 
    157 bool SafeBrowsingResourceThrottle::CheckUrl(const GURL& url) {
    158   CHECK(state_ == STATE_NONE);
    159   bool succeeded_synchronously = database_manager_->CheckBrowseUrl(url, this);
    160   if (succeeded_synchronously) {
    161     threat_type_ = SB_THREAT_TYPE_SAFE;
    162     ui_manager_->LogPauseDelay(base::TimeDelta());  // No delay.
    163     return true;
    164   }
    165 
    166   state_ = STATE_CHECKING_URL;
    167   url_being_checked_ = url;
    168 
    169   // Record the start time of the check.
    170   url_check_start_time_ = base::TimeTicks::Now();
    171 
    172   // Start a timer to abort the check if it takes too long.
    173   timer_.Start(FROM_HERE,
    174                base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs),
    175                this, &SafeBrowsingResourceThrottle::OnCheckUrlTimeout);
    176 
    177   return false;
    178 }
    179 
    180 void SafeBrowsingResourceThrottle::OnCheckUrlTimeout() {
    181   CHECK(state_ == STATE_CHECKING_URL);
    182   CHECK(defer_state_ != DEFERRED_NONE);
    183 
    184   database_manager_->CancelCheck(this);
    185   OnCheckBrowseUrlResult(url_being_checked_, SB_THREAT_TYPE_SAFE);
    186 }
    187 
    188 void SafeBrowsingResourceThrottle::ResumeRequest() {
    189   CHECK(state_ == STATE_NONE);
    190   CHECK(defer_state_ != DEFERRED_NONE);
    191 
    192   defer_state_ = DEFERRED_NONE;
    193   controller()->Resume();
    194 }
    195