1 // Copyright (c) 2011 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_handler.h"
6
7 #include "base/logging.h"
8 #include "content/browser/renderer_host/global_request_id.h"
9 #include "content/browser/renderer_host/resource_dispatcher_host.h"
10 #include "content/browser/renderer_host/resource_message_filter.h"
11 #include "content/common/resource_response.h"
12 #include "net/base/io_buffer.h"
13 #include "net/base/load_flags.h"
14 #include "net/base/net_errors.h"
15 #include "net/url_request/url_request.h"
16
17 // Maximum time in milliseconds to wait for the safe browsing service to
18 // verify a URL. After this amount of time the outstanding check will be
19 // aborted, and the URL will be treated as if it were safe.
20 static const int kCheckUrlTimeoutMs = 5000;
21
22 // TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more
23 // unit test coverage.
24
SafeBrowsingResourceHandler(ResourceHandler * handler,int render_process_host_id,int render_view_id,ResourceType::Type resource_type,SafeBrowsingService * safe_browsing,ResourceDispatcherHost * resource_dispatcher_host)25 SafeBrowsingResourceHandler::SafeBrowsingResourceHandler(
26 ResourceHandler* handler,
27 int render_process_host_id,
28 int render_view_id,
29 ResourceType::Type resource_type,
30 SafeBrowsingService* safe_browsing,
31 ResourceDispatcherHost* resource_dispatcher_host)
32 : state_(STATE_NONE),
33 defer_state_(DEFERRED_NONE),
34 safe_browsing_result_(SafeBrowsingService::SAFE),
35 deferred_request_id_(-1),
36 next_handler_(handler),
37 render_process_host_id_(render_process_host_id),
38 render_view_id_(render_view_id),
39 safe_browsing_(safe_browsing),
40 rdh_(resource_dispatcher_host),
41 resource_type_(resource_type) {
42 }
43
~SafeBrowsingResourceHandler()44 SafeBrowsingResourceHandler::~SafeBrowsingResourceHandler() {
45 }
46
OnUploadProgress(int request_id,uint64 position,uint64 size)47 bool SafeBrowsingResourceHandler::OnUploadProgress(int request_id,
48 uint64 position,
49 uint64 size) {
50 return next_handler_->OnUploadProgress(request_id, position, size);
51 }
52
OnRequestRedirected(int request_id,const GURL & new_url,ResourceResponse * response,bool * defer)53 bool SafeBrowsingResourceHandler::OnRequestRedirected(
54 int request_id,
55 const GURL& new_url,
56 ResourceResponse* response,
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 next_handler_->OnRequestRedirected(
67 request_id, new_url, response, defer);
68 }
69
70 // If the URL couldn't be verified synchronously, defer following the
71 // redirect until the SafeBrowsing check is complete. Store the redirect
72 // context so we can pass it on to other handlers once we have completed
73 // our check.
74 defer_state_ = DEFERRED_REDIRECT;
75 deferred_request_id_ = request_id;
76 deferred_url_ = new_url;
77 deferred_redirect_response_ = response;
78 *defer = true;
79
80 return true;
81 }
82
OnResponseStarted(int request_id,ResourceResponse * response)83 bool SafeBrowsingResourceHandler::OnResponseStarted(
84 int request_id, ResourceResponse* response) {
85 CHECK(state_ == STATE_NONE);
86 CHECK(defer_state_ == DEFERRED_NONE);
87 return next_handler_->OnResponseStarted(request_id, response);
88 }
89
OnCheckUrlTimeout()90 void SafeBrowsingResourceHandler::OnCheckUrlTimeout() {
91 CHECK(state_ == STATE_CHECKING_URL);
92 CHECK(defer_state_ != DEFERRED_NONE);
93 safe_browsing_->CancelCheck(this);
94 OnBrowseUrlCheckResult(deferred_url_, SafeBrowsingService::SAFE);
95 }
96
OnWillStart(int request_id,const GURL & url,bool * defer)97 bool SafeBrowsingResourceHandler::OnWillStart(int request_id,
98 const GURL& url,
99 bool* defer) {
100 // We need to check the new URL before starting the request.
101 if (CheckUrl(url))
102 return next_handler_->OnWillStart(request_id, url, defer);
103
104 // If the URL couldn't be verified synchronously, defer starting the
105 // request until the check has completed.
106 defer_state_ = DEFERRED_START;
107 deferred_request_id_ = request_id;
108 deferred_url_ = url;
109 *defer = true;
110
111 return true;
112 }
113
OnWillRead(int request_id,net::IOBuffer ** buf,int * buf_size,int min_size)114 bool SafeBrowsingResourceHandler::OnWillRead(int request_id,
115 net::IOBuffer** buf, int* buf_size,
116 int min_size) {
117 CHECK(state_ == STATE_NONE);
118 CHECK(defer_state_ == DEFERRED_NONE);
119 return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
120 }
121
OnReadCompleted(int request_id,int * bytes_read)122 bool SafeBrowsingResourceHandler::OnReadCompleted(int request_id,
123 int* bytes_read) {
124 CHECK(state_ == STATE_NONE);
125 CHECK(defer_state_ == DEFERRED_NONE);
126 return next_handler_->OnReadCompleted(request_id, bytes_read);
127 }
128
OnResponseCompleted(int request_id,const net::URLRequestStatus & status,const std::string & security_info)129 bool SafeBrowsingResourceHandler::OnResponseCompleted(
130 int request_id, const net::URLRequestStatus& status,
131 const std::string& security_info) {
132 Shutdown();
133 return next_handler_->OnResponseCompleted(request_id, status, security_info);
134 }
135
OnRequestClosed()136 void SafeBrowsingResourceHandler::OnRequestClosed() {
137 Shutdown();
138 next_handler_->OnRequestClosed();
139 }
140
141 // SafeBrowsingService::Client implementation, called on the IO thread once
142 // the URL has been classified.
OnBrowseUrlCheckResult(const GURL & url,SafeBrowsingService::UrlCheckResult result)143 void SafeBrowsingResourceHandler::OnBrowseUrlCheckResult(
144 const GURL& url, SafeBrowsingService::UrlCheckResult result) {
145 CHECK(state_ == STATE_CHECKING_URL);
146 CHECK(defer_state_ != DEFERRED_NONE);
147 CHECK(url == deferred_url_) << "Was expecting: " << deferred_url_
148 << " but got: " << url;
149
150 timer_.Stop(); // Cancel the timeout timer.
151 safe_browsing_result_ = result;
152 state_ = STATE_NONE;
153
154 if (result == SafeBrowsingService::SAFE) {
155 // Log how much time the safe browsing check cost us.
156 base::TimeDelta pause_delta;
157 pause_delta = base::TimeTicks::Now() - url_check_start_time_;
158 safe_browsing_->LogPauseDelay(pause_delta);
159
160 // Continue the request.
161 ResumeRequest();
162 } else {
163 const net::URLRequest* request = rdh_->GetURLRequest(
164 GlobalRequestID(render_process_host_id_, deferred_request_id_));
165 if (request->load_flags() & net::LOAD_PREFETCH) {
166 // Don't prefetch resources that fail safe browsing, disallow
167 // them.
168 rdh_->CancelRequest(render_process_host_id_, deferred_request_id_, false);
169 } else {
170 StartDisplayingBlockingPage(url, result);
171 }
172 }
173
174 Release(); // Balances the AddRef() in CheckingUrl().
175 }
176
StartDisplayingBlockingPage(const GURL & url,SafeBrowsingService::UrlCheckResult result)177 void SafeBrowsingResourceHandler::StartDisplayingBlockingPage(
178 const GURL& url,
179 SafeBrowsingService::UrlCheckResult result) {
180 CHECK(state_ == STATE_NONE);
181 CHECK(defer_state_ != DEFERRED_NONE);
182 CHECK(deferred_request_id_ != -1);
183
184 state_ = STATE_DISPLAYING_BLOCKING_PAGE;
185 AddRef(); // Balanced in OnBlockingPageComplete().
186
187 // Grab the original url of this request as well.
188 GURL original_url;
189 net::URLRequest* request = rdh_->GetURLRequest(
190 GlobalRequestID(render_process_host_id_, deferred_request_id_));
191 if (request)
192 original_url = request->original_url();
193 else
194 original_url = url;
195
196 safe_browsing_->DisplayBlockingPage(
197 url, original_url, redirect_urls_, resource_type_,
198 result, this, render_process_host_id_, render_view_id_);
199 }
200
201 // SafeBrowsingService::Client implementation, called on the IO thread when
202 // the user has decided to proceed with the current request, or go back.
OnBlockingPageComplete(bool proceed)203 void SafeBrowsingResourceHandler::OnBlockingPageComplete(bool proceed) {
204 CHECK(state_ == STATE_DISPLAYING_BLOCKING_PAGE);
205 state_ = STATE_NONE;
206
207 if (proceed) {
208 safe_browsing_result_ = SafeBrowsingService::SAFE;
209 net::URLRequest* request = rdh_->GetURLRequest(
210 GlobalRequestID(render_process_host_id_, deferred_request_id_));
211
212 // The request could be canceled by renderer at this stage.
213 // As a result, click proceed will do nothing (crbug.com/76460).
214 if (request)
215 ResumeRequest();
216 } else {
217 rdh_->CancelRequest(render_process_host_id_, deferred_request_id_, false);
218 }
219
220 Release(); // Balances the AddRef() in StartDisplayingBlockingPage().
221 }
222
Shutdown()223 void SafeBrowsingResourceHandler::Shutdown() {
224 if (state_ == STATE_CHECKING_URL) {
225 timer_.Stop();
226 safe_browsing_->CancelCheck(this);
227 state_ = STATE_NONE;
228 // Balance the AddRef() from CheckUrl() which would ordinarily be
229 // balanced by OnUrlCheckResult().
230 Release();
231 }
232 }
233
CheckUrl(const GURL & url)234 bool SafeBrowsingResourceHandler::CheckUrl(const GURL& url) {
235 CHECK(state_ == STATE_NONE);
236 bool succeeded_synchronously = safe_browsing_->CheckBrowseUrl(url, this);
237 if (succeeded_synchronously) {
238 safe_browsing_result_ = SafeBrowsingService::SAFE;
239 safe_browsing_->LogPauseDelay(base::TimeDelta()); // No delay.
240 return true;
241 }
242
243 AddRef(); // Balanced in OnUrlCheckResult().
244 state_ = STATE_CHECKING_URL;
245
246 // Record the start time of the check.
247 url_check_start_time_ = base::TimeTicks::Now();
248
249 // Start a timer to abort the check if it takes too long.
250 timer_.Start(base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs),
251 this, &SafeBrowsingResourceHandler::OnCheckUrlTimeout);
252
253 return false;
254 }
255
ResumeRequest()256 void SafeBrowsingResourceHandler::ResumeRequest() {
257 CHECK(state_ == STATE_NONE);
258 CHECK(defer_state_ != DEFERRED_NONE);
259
260 // Resume whatever stage got paused by the safe browsing check.
261 switch (defer_state_) {
262 case DEFERRED_START:
263 ResumeStart();
264 break;
265 case DEFERRED_REDIRECT:
266 ResumeRedirect();
267 break;
268 case DEFERRED_NONE:
269 NOTREACHED();
270 break;
271 }
272 }
273
ResumeStart()274 void SafeBrowsingResourceHandler::ResumeStart() {
275 CHECK(defer_state_ == DEFERRED_START);
276 CHECK(deferred_request_id_ != -1);
277 defer_state_ = DEFERRED_NONE;
278
279 // Retrieve the details for the paused OnWillStart().
280 int request_id = deferred_request_id_;
281 GURL url = deferred_url_;
282
283 ClearDeferredRequestInfo();
284
285 // Give the other resource handlers a chance to defer starting.
286 bool defer = false;
287 // TODO(eroman): the return value is being lost here. Should
288 // use it to cancel the request.
289 next_handler_->OnWillStart(request_id, url, &defer);
290 if (!defer)
291 rdh_->StartDeferredRequest(render_process_host_id_, request_id);
292 }
293
ResumeRedirect()294 void SafeBrowsingResourceHandler::ResumeRedirect() {
295 CHECK(defer_state_ == DEFERRED_REDIRECT);
296 defer_state_ = DEFERRED_NONE;
297
298 // Retrieve the details for the paused OnReceivedRedirect().
299 int request_id = deferred_request_id_;
300 GURL redirect_url = deferred_url_;
301 scoped_refptr<ResourceResponse> redirect_response =
302 deferred_redirect_response_;
303
304 ClearDeferredRequestInfo();
305
306 // Give the other resource handlers a chance to handle the redirect.
307 bool defer = false;
308 // TODO(eroman): the return value is being lost here. Should
309 // use it to cancel the request.
310 next_handler_->OnRequestRedirected(request_id, redirect_url,
311 redirect_response, &defer);
312 if (!defer) {
313 rdh_->FollowDeferredRedirect(render_process_host_id_, request_id,
314 false, GURL());
315 }
316 }
317
ClearDeferredRequestInfo()318 void SafeBrowsingResourceHandler::ClearDeferredRequestInfo() {
319 deferred_request_id_ = -1;
320 deferred_url_ = GURL();
321 deferred_redirect_response_ = NULL;
322 }
323