safe_browsing_resource_throttle.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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. 22static const int kCheckUrlTimeoutMs = 5000; 23 24// TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more 25// unit test coverage. 26 27SafeBrowsingResourceThrottle::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 40SafeBrowsingResourceThrottle::~SafeBrowsingResourceThrottle() { 41 if (state_ == STATE_CHECKING_URL) 42 database_manager_->CancelCheck(this); 43} 44 45void 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 56void 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 76const 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. 82void 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#if defined(OS_ANDROID) 90 // Temporarily disable SB interstitial during Finch experiment. 91 // The database check is still exercised, but the interstitial never shown. 92 threat_type = SB_THREAT_TYPE_SAFE; 93#endif 94 95 timer_.Stop(); // Cancel the timeout timer. 96 threat_type_ = threat_type; 97 state_ = STATE_NONE; 98 99 if (threat_type == SB_THREAT_TYPE_SAFE) { 100 // Log how much time the safe browsing check cost us. 101 ui_manager_->LogPauseDelay(base::TimeTicks::Now() - url_check_start_time_); 102 103 // Continue the request. 104 ResumeRequest(); 105 return; 106 } 107 108 if (request_->load_flags() & net::LOAD_PREFETCH) { 109 // Don't prefetch resources that fail safe browsing, disallow 110 // them. 111 controller()->Cancel(); 112 return; 113 } 114 115 const content::ResourceRequestInfo* info = 116 content::ResourceRequestInfo::ForRequest(request_); 117 118 SafeBrowsingUIManager::UnsafeResource resource; 119 resource.url = url; 120 resource.original_url = request_->original_url(); 121 resource.redirect_urls = redirect_urls_; 122 resource.is_subresource = is_subresource_; 123 resource.threat_type = threat_type; 124 resource.callback = base::Bind( 125 &SafeBrowsingResourceThrottle::OnBlockingPageComplete, AsWeakPtr()); 126 resource.render_process_host_id = info->GetChildID(); 127 resource.render_view_id = info->GetRouteID(); 128 129 state_ = STATE_DISPLAYING_BLOCKING_PAGE; 130 131 content::BrowserThread::PostTask( 132 content::BrowserThread::UI, 133 FROM_HERE, 134 base::Bind(&SafeBrowsingResourceThrottle::StartDisplayingBlockingPage, 135 AsWeakPtr(), ui_manager_, resource)); 136} 137 138void SafeBrowsingResourceThrottle::StartDisplayingBlockingPage( 139 const base::WeakPtr<SafeBrowsingResourceThrottle>& throttle, 140 scoped_refptr<SafeBrowsingUIManager> ui_manager, 141 const SafeBrowsingUIManager::UnsafeResource& resource) { 142 bool should_show_blocking_page = true; 143 144 content::RenderViewHost* rvh = content::RenderViewHost::FromID( 145 resource.render_process_host_id, resource.render_view_id); 146 if (rvh) { 147 content::WebContents* web_contents = 148 content::WebContents::FromRenderViewHost(rvh); 149 prerender::PrerenderContents* prerender_contents = 150 prerender::PrerenderContents::FromWebContents(web_contents); 151 if (prerender_contents) { 152 prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING); 153 should_show_blocking_page = false; 154 } 155 156 if (should_show_blocking_page) { 157 ui_manager->DisplayBlockingPage(resource); 158 return; 159 } 160 } 161 162 // Tab is gone or it's being prerendered. 163 content::BrowserThread::PostTask( 164 content::BrowserThread::IO, 165 FROM_HERE, 166 base::Bind(&SafeBrowsingResourceThrottle::Cancel, throttle)); 167} 168 169void SafeBrowsingResourceThrottle::Cancel() { 170 controller()->Cancel(); 171} 172 173// SafeBrowsingService::UrlCheckCallback implementation, called on the IO 174// thread when the user has decided to proceed with the current request, or 175// go back. 176void SafeBrowsingResourceThrottle::OnBlockingPageComplete(bool proceed) { 177 CHECK(state_ == STATE_DISPLAYING_BLOCKING_PAGE); 178 state_ = STATE_NONE; 179 180 if (proceed) { 181 threat_type_ = SB_THREAT_TYPE_SAFE; 182 ResumeRequest(); 183 } else { 184 controller()->Cancel(); 185 } 186} 187 188bool SafeBrowsingResourceThrottle::CheckUrl(const GURL& url) { 189 CHECK(state_ == STATE_NONE); 190 bool succeeded_synchronously = database_manager_->CheckBrowseUrl(url, this); 191 if (succeeded_synchronously) { 192 threat_type_ = SB_THREAT_TYPE_SAFE; 193 ui_manager_->LogPauseDelay(base::TimeDelta()); // No delay. 194 return true; 195 } 196 197 state_ = STATE_CHECKING_URL; 198 url_being_checked_ = url; 199 200 // Record the start time of the check. 201 url_check_start_time_ = base::TimeTicks::Now(); 202 203 // Start a timer to abort the check if it takes too long. 204 timer_.Start(FROM_HERE, 205 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), 206 this, &SafeBrowsingResourceThrottle::OnCheckUrlTimeout); 207 208 return false; 209} 210 211void SafeBrowsingResourceThrottle::OnCheckUrlTimeout() { 212 CHECK(state_ == STATE_CHECKING_URL); 213 CHECK(defer_state_ != DEFERRED_NONE); 214 215 database_manager_->CancelCheck(this); 216 OnCheckBrowseUrlResult(url_being_checked_, SB_THREAT_TYPE_SAFE); 217} 218 219void SafeBrowsingResourceThrottle::ResumeRequest() { 220 CHECK(state_ == STATE_NONE); 221 CHECK(defer_state_ != DEFERRED_NONE); 222 223 defer_state_ = DEFERRED_NONE; 224 controller()->Resume(); 225} 226