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 32 using content::BrowserThread; 33 using content::NavigationEntry; 34 using content::WebContents; 35 36 struct SafeBrowsingUIManager::WhiteListedEntry { 37 int render_process_host_id; 38 int render_view_id; 39 std::string domain; 40 SBThreatType threat_type; 41 }; 42 43 SafeBrowsingUIManager::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 50 SafeBrowsingUIManager::UnsafeResource::~UnsafeResource() { } 51 52 SafeBrowsingUIManager::SafeBrowsingUIManager( 53 const scoped_refptr<SafeBrowsingService>& service) 54 : sb_service_(service) { 55 } 56 57 SafeBrowsingUIManager::~SafeBrowsingUIManager() { } 58 59 void SafeBrowsingUIManager::StopOnIOThread(bool shutdown) { 60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 61 62 if (shutdown) 63 sb_service_ = NULL; 64 } 65 66 void 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. 72 bool SafeBrowsingUIManager::CanReportStats() const { 73 const metrics::MetricsService* metrics = g_browser_process->metrics_service(); 74 return metrics && metrics->reporting_active(); 75 } 76 77 void 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 96 void 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. 183 void 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 201 void SafeBrowsingUIManager::AddObserver(Observer* observer) { 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 203 observer_list_.AddObserver(observer); 204 } 205 206 void SafeBrowsingUIManager::RemoveObserver(Observer* observer) { 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 208 observer_list_.RemoveObserver(observer); 209 } 210 211 void 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. 236 void 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 251 void 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 264 bool 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