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