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