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/safe_browsing/ui_manager.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/callback.h"
10#include "base/debug/leak_tracker.h"
11#include "base/stl_util.h"
12#include "base/strings/string_util.h"
13#include "base/threading/thread.h"
14#include "base/threading/thread_restrictions.h"
15#include "chrome/browser/browser_process.h"
16#include "chrome/browser/safe_browsing/malware_details.h"
17#include "chrome/browser/safe_browsing/metadata.pb.h"
18#include "chrome/browser/safe_browsing/ping_manager.h"
19#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
20#include "chrome/browser/safe_browsing/safe_browsing_service.h"
21#include "chrome/browser/tab_contents/tab_util.h"
22#include "chrome/common/url_constants.h"
23#include "components/metrics/metrics_service.h"
24#include "content/public/browser/browser_thread.h"
25#include "content/public/browser/navigation_entry.h"
26#include "content/public/browser/notification_service.h"
27#include "content/public/browser/web_contents.h"
28#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
29#include "net/url_request/url_request_context.h"
30#include "net/url_request/url_request_context_getter.h"
31
32using content::BrowserThread;
33using content::NavigationEntry;
34using content::WebContents;
35
36struct SafeBrowsingUIManager::WhiteListedEntry {
37  int render_process_host_id;
38  int render_view_id;
39  std::string domain;
40  SBThreatType threat_type;
41};
42
43SafeBrowsingUIManager::UnsafeResource::UnsafeResource()
44    : is_subresource(false),
45      threat_type(SB_THREAT_TYPE_SAFE),
46      render_process_host_id(-1),
47      render_view_id(-1) {
48}
49
50SafeBrowsingUIManager::UnsafeResource::~UnsafeResource() { }
51
52SafeBrowsingUIManager::SafeBrowsingUIManager(
53    const scoped_refptr<SafeBrowsingService>& service)
54    : sb_service_(service) {
55}
56
57SafeBrowsingUIManager::~SafeBrowsingUIManager() { }
58
59void SafeBrowsingUIManager::StopOnIOThread(bool shutdown) {
60  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
61
62  if (shutdown)
63    sb_service_ = NULL;
64}
65
66void SafeBrowsingUIManager::LogPauseDelay(base::TimeDelta time) {
67  UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
68}
69
70// Only report SafeBrowsing related stats when UMA is enabled. User must also
71// ensure that safe browsing is enabled from the calling profile.
72bool SafeBrowsingUIManager::CanReportStats() const {
73  const metrics::MetricsService* metrics = g_browser_process->metrics_service();
74  return metrics && metrics->reporting_active();
75}
76
77void SafeBrowsingUIManager::OnBlockingPageDone(
78    const std::vector<UnsafeResource>& resources,
79    bool proceed) {
80  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
81  for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
82       iter != resources.end(); ++iter) {
83    const UnsafeResource& resource = *iter;
84    if (!resource.callback.is_null())
85      resource.callback.Run(proceed);
86
87    if (proceed) {
88      BrowserThread::PostTask(
89          BrowserThread::UI,
90          FROM_HERE,
91          base::Bind(&SafeBrowsingUIManager::UpdateWhitelist, this, resource));
92    }
93  }
94}
95
96void SafeBrowsingUIManager::DisplayBlockingPage(
97    const UnsafeResource& resource) {
98  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
99
100  if (!resource.threat_metadata.empty() &&
101      resource.threat_type == SB_THREAT_TYPE_URL_MALWARE) {
102    safe_browsing::MalwarePatternType proto;
103    // Malware sites tagged as "landing site" should only show a warning for a
104    // main-frame or sub-frame resource. (See "Types of Malware sites" under
105    // https://developers.google.com/safe-browsing/developers_guide_v3#UserWarnings)
106    if (proto.ParseFromString(resource.threat_metadata) &&
107        proto.pattern_type() == safe_browsing::MalwarePatternType::LANDING &&
108        resource.is_subresource && !resource.is_subframe) {
109      if (!resource.callback.is_null()) {
110        BrowserThread::PostTask(
111            BrowserThread::IO, FROM_HERE, base::Bind(resource.callback, true));
112      }
113      return;
114    }
115  }
116
117  // Indicate to interested observers that the resource in question matched the
118  // SB filters. If the resource is already whitelisted, OnSafeBrowsingHit
119  // won't be called.
120  if (resource.threat_type != SB_THREAT_TYPE_SAFE) {
121    FOR_EACH_OBSERVER(Observer, observer_list_, OnSafeBrowsingMatch(resource));
122  }
123
124  // Check if the user has already ignored our warning for this render_view
125  // and domain.
126  if (IsWhitelisted(resource)) {
127    if (!resource.callback.is_null()) {
128      BrowserThread::PostTask(
129          BrowserThread::IO, FROM_HERE, base::Bind(resource.callback, true));
130    }
131    return;
132  }
133
134  // The tab might have been closed.
135  WebContents* web_contents =
136      tab_util::GetWebContentsByID(resource.render_process_host_id,
137                                   resource.render_view_id);
138
139  if (!web_contents) {
140    // The tab is gone and we did not have a chance at showing the interstitial.
141    // Just act as if "Don't Proceed" were chosen.
142    std::vector<UnsafeResource> resources;
143    resources.push_back(resource);
144    BrowserThread::PostTask(
145      BrowserThread::IO, FROM_HERE,
146      base::Bind(&SafeBrowsingUIManager::OnBlockingPageDone,
147                 this, resources, false));
148    return;
149  }
150
151  if (resource.threat_type != SB_THREAT_TYPE_SAFE &&
152      CanReportStats()) {
153    GURL page_url = web_contents->GetURL();
154    GURL referrer_url;
155    NavigationEntry* entry = web_contents->GetController().GetActiveEntry();
156    if (entry)
157      referrer_url = entry->GetReferrer().url;
158
159    // When the malicious url is on the main frame, and resource.original_url
160    // is not the same as the resource.url, that means we have a redirect from
161    // resource.original_url to resource.url.
162    // Also, at this point, page_url points to the _previous_ page that we
163    // were on. We replace page_url with resource.original_url and referrer
164    // with page_url.
165    if (!resource.is_subresource &&
166        !resource.original_url.is_empty() &&
167        resource.original_url != resource.url) {
168      referrer_url = page_url;
169      page_url = resource.original_url;
170    }
171    ReportSafeBrowsingHit(resource.url, page_url, referrer_url,
172                          resource.is_subresource, resource.threat_type,
173                          std::string() /* post_data */);
174  }
175  if (resource.threat_type != SB_THREAT_TYPE_SAFE) {
176    FOR_EACH_OBSERVER(Observer, observer_list_, OnSafeBrowsingHit(resource));
177  }
178  SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
179}
180
181// A safebrowsing hit is sent after a blocking page for malware/phishing
182// or after the warning dialog for download urls, only for UMA users.
183void SafeBrowsingUIManager::ReportSafeBrowsingHit(
184    const GURL& malicious_url,
185    const GURL& page_url,
186    const GURL& referrer_url,
187    bool is_subresource,
188    SBThreatType threat_type,
189    const std::string& post_data) {
190  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
191  if (!CanReportStats())
192    return;
193
194  BrowserThread::PostTask(
195      BrowserThread::IO, FROM_HERE,
196      base::Bind(&SafeBrowsingUIManager::ReportSafeBrowsingHitOnIOThread, this,
197                 malicious_url, page_url, referrer_url, is_subresource,
198                 threat_type, post_data));
199}
200
201void SafeBrowsingUIManager::AddObserver(Observer* observer) {
202  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
203  observer_list_.AddObserver(observer);
204}
205
206void SafeBrowsingUIManager::RemoveObserver(Observer* observer) {
207  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
208  observer_list_.RemoveObserver(observer);
209}
210
211void SafeBrowsingUIManager::ReportSafeBrowsingHitOnIOThread(
212    const GURL& malicious_url,
213    const GURL& page_url,
214    const GURL& referrer_url,
215    bool is_subresource,
216    SBThreatType threat_type,
217    const std::string& post_data) {
218  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
219
220  // The service may delete the ping manager (i.e. when user disabling service,
221  // etc). This happens on the IO thread.
222  if (sb_service_.get() == NULL || sb_service_->ping_manager() == NULL)
223    return;
224
225  DVLOG(1) << "ReportSafeBrowsingHit: " << malicious_url << " " << page_url
226           << " " << referrer_url << " " << is_subresource << " "
227           << threat_type;
228  sb_service_->ping_manager()->ReportSafeBrowsingHit(
229      malicious_url, page_url,
230      referrer_url, is_subresource,
231      threat_type, post_data);
232}
233
234// If the user had opted-in to send MalwareDetails, this gets called
235// when the report is ready.
236void SafeBrowsingUIManager::SendSerializedMalwareDetails(
237    const std::string& serialized) {
238  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
239
240  // The service may delete the ping manager (i.e. when user disabling service,
241  // etc). This happens on the IO thread.
242  if (sb_service_.get() == NULL || sb_service_->ping_manager() == NULL)
243    return;
244
245  if (!serialized.empty()) {
246    DVLOG(1) << "Sending serialized malware details.";
247    sb_service_->ping_manager()->ReportMalwareDetails(serialized);
248  }
249}
250
251void SafeBrowsingUIManager::UpdateWhitelist(const UnsafeResource& resource) {
252  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
253  // Whitelist this domain and warning type for the given tab.
254  WhiteListedEntry entry;
255  entry.render_process_host_id = resource.render_process_host_id;
256  entry.render_view_id = resource.render_view_id;
257  entry.domain = net::registry_controlled_domains::GetDomainAndRegistry(
258      resource.url,
259      net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
260  entry.threat_type = resource.threat_type;
261  white_listed_entries_.push_back(entry);
262}
263
264bool SafeBrowsingUIManager::IsWhitelisted(const UnsafeResource& resource) {
265  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
266  // Check if the user has already ignored our warning for this render_view
267  // and domain.
268  for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
269    const WhiteListedEntry& entry = white_listed_entries_[i];
270    if (entry.render_process_host_id == resource.render_process_host_id &&
271        entry.render_view_id == resource.render_view_id &&
272        // Threat type must be the same or they can either be client-side
273        // phishing/malware URL or a SafeBrowsing phishing/malware URL.
274        // If we show one type of phishing/malware warning we don't want to show
275        // a second phishing/malware warning.
276        (entry.threat_type == resource.threat_type ||
277         (entry.threat_type == SB_THREAT_TYPE_URL_PHISHING &&
278          resource.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL) ||
279         (entry.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL &&
280          resource.threat_type == SB_THREAT_TYPE_URL_PHISHING) ||
281         (entry.threat_type == SB_THREAT_TYPE_URL_MALWARE &&
282          resource.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) ||
283         (entry.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL &&
284          resource.threat_type == SB_THREAT_TYPE_URL_MALWARE))) {
285      return entry.domain ==
286          net::registry_controlled_domains::GetDomainAndRegistry(
287              resource.url,
288              net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
289    }
290  }
291  return false;
292}
293
294