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