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