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