safe_browsing_resource_throttle.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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.
19static const int kCheckUrlTimeoutMs = 5000;
20
21// TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more
22//               unit test coverage.
23
24SafeBrowsingResourceThrottle::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
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
77// SafeBrowsingService::Client implementation, called on the IO thread once
78// the URL has been classified.
79void 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
123void 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.
145void 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
157bool 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
180void 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
188void SafeBrowsingResourceThrottle::ResumeRequest() {
189  CHECK(state_ == STATE_NONE);
190  CHECK(defer_state_ != DEFERRED_NONE);
191
192  defer_state_ = DEFERRED_NONE;
193  controller()->Resume();
194}
195