| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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 "components/safe_browsing_db/remote_database_manager.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "base/metrics/histogram_macros.h" | |
| 10 #include "base/strings/string_number_conversions.h" | |
| 11 #include "base/strings/string_split.h" | |
| 12 #include "base/timer/elapsed_timer.h" | |
| 13 #include "components/safe_browsing_db/safe_browsing_api_handler.h" | |
| 14 #include "components/variations/variations_associated_data.h" | |
| 15 #include "content/public/browser/browser_thread.h" | |
| 16 | |
| 17 using content::BrowserThread; | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 // Android field trial for controlling types_to_check. | |
| 22 const char kAndroidFieldExperiment[] = "SafeBrowsingAndroid"; | |
| 23 const char kAndroidTypesToCheckParam[] = "types_to_check"; | |
| 24 | |
| 25 } // namespace | |
| 26 | |
| 27 namespace safe_browsing { | |
| 28 | |
| 29 // | |
| 30 // RemoteSafeBrowsingDatabaseManager::ClientRequest methods | |
| 31 // | |
| 32 class RemoteSafeBrowsingDatabaseManager::ClientRequest { | |
| 33 public: | |
| 34 ClientRequest(Client* client, | |
| 35 RemoteSafeBrowsingDatabaseManager* db_manager, | |
| 36 const GURL& url); | |
| 37 | |
| 38 static void OnRequestDoneWeak(const base::WeakPtr<ClientRequest>& req, | |
| 39 SBThreatType matched_threat_type, | |
| 40 const std::string& metadata); | |
| 41 void OnRequestDone(SBThreatType matched_threat_type, | |
| 42 const std::string& metadata); | |
| 43 | |
| 44 // Accessors | |
| 45 Client* client() const { return client_; } | |
| 46 const GURL& url() const { return url_; } | |
| 47 base::WeakPtr<ClientRequest> GetWeakPtr() { | |
| 48 return weak_factory_.GetWeakPtr(); | |
| 49 } | |
| 50 | |
| 51 private: | |
| 52 Client* client_; | |
| 53 RemoteSafeBrowsingDatabaseManager* db_manager_; | |
| 54 GURL url_; | |
| 55 base::ElapsedTimer timer_; | |
| 56 base::WeakPtrFactory<ClientRequest> weak_factory_; | |
| 57 }; | |
| 58 | |
| 59 RemoteSafeBrowsingDatabaseManager::ClientRequest::ClientRequest( | |
| 60 Client* client, | |
| 61 RemoteSafeBrowsingDatabaseManager* db_manager, | |
| 62 const GURL& url) | |
| 63 : client_(client), | |
| 64 db_manager_(db_manager), | |
| 65 url_(url), | |
| 66 weak_factory_(this) {} | |
| 67 | |
| 68 // Static | |
| 69 void RemoteSafeBrowsingDatabaseManager::ClientRequest::OnRequestDoneWeak( | |
| 70 const base::WeakPtr<ClientRequest>& req, | |
| 71 SBThreatType matched_threat_type, | |
| 72 const std::string& metadata) { | |
| 73 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 74 if (!req) | |
| 75 return; // Previously canceled | |
| 76 req->OnRequestDone(matched_threat_type, metadata); | |
| 77 } | |
| 78 | |
| 79 void RemoteSafeBrowsingDatabaseManager::ClientRequest::OnRequestDone( | |
| 80 SBThreatType matched_threat_type, | |
| 81 const std::string& metadata) { | |
| 82 DVLOG(1) << "OnRequestDone took " << timer_.Elapsed().InMilliseconds() | |
| 83 << " ms for client " << client_ << " and URL " << url_; | |
| 84 client_->OnCheckBrowseUrlResult(url_, matched_threat_type, metadata); | |
| 85 UMA_HISTOGRAM_TIMES("SB2.RemoteCall.Elapsed", timer_.Elapsed()); | |
| 86 // CancelCheck() will delete *this. | |
| 87 db_manager_->CancelCheck(client_); | |
| 88 } | |
| 89 | |
| 90 // | |
| 91 // RemoteSafeBrowsingDatabaseManager methods | |
| 92 // | |
| 93 | |
| 94 // TODO(nparker): Add more tests for this class | |
| 95 RemoteSafeBrowsingDatabaseManager::RemoteSafeBrowsingDatabaseManager() | |
| 96 : enabled_(false) { | |
| 97 // Decide which resource types to check. These two are the minimum. | |
| 98 resource_types_to_check_.insert(content::RESOURCE_TYPE_MAIN_FRAME); | |
| 99 resource_types_to_check_.insert(content::RESOURCE_TYPE_SUB_FRAME); | |
| 100 | |
| 101 // The param is expected to be a comma-separated list of ints | |
| 102 // corresponding to the enum types. We're keeping this finch | |
| 103 // control around so we can add back types if they later become dangerous. | |
| 104 const std::string ints_str = variations::GetVariationParamValue( | |
| 105 kAndroidFieldExperiment, kAndroidTypesToCheckParam); | |
| 106 if (ints_str.empty()) { | |
| 107 // By default, we check all types except a few. | |
| 108 static_assert(content::RESOURCE_TYPE_LAST_TYPE == | |
| 109 content::RESOURCE_TYPE_PLUGIN_RESOURCE + 1, | |
| 110 "Decide if new resource type should be skipped on mobile."); | |
| 111 for (int t_int = 0; t_int < content::RESOURCE_TYPE_LAST_TYPE; t_int++) { | |
| 112 content::ResourceType t = static_cast<content::ResourceType>(t_int); | |
| 113 switch (t) { | |
| 114 case content::RESOURCE_TYPE_STYLESHEET: | |
| 115 case content::RESOURCE_TYPE_IMAGE: | |
| 116 case content::RESOURCE_TYPE_FONT_RESOURCE: | |
| 117 case content::RESOURCE_TYPE_FAVICON: | |
| 118 break; | |
| 119 default: | |
| 120 resource_types_to_check_.insert(t); | |
| 121 } | |
| 122 } | |
| 123 } else { | |
| 124 // Use the finch param. | |
| 125 for (const std::string& val_str : base::SplitString( | |
| 126 ints_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { | |
| 127 int i; | |
| 128 if (base::StringToInt(val_str, &i) && i >= 0 && | |
| 129 i < content::RESOURCE_TYPE_LAST_TYPE) { | |
| 130 resource_types_to_check_.insert(static_cast<content::ResourceType>(i)); | |
| 131 } | |
| 132 } | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 RemoteSafeBrowsingDatabaseManager::~RemoteSafeBrowsingDatabaseManager() { | |
| 137 DCHECK(!enabled_); | |
| 138 } | |
| 139 | |
| 140 bool RemoteSafeBrowsingDatabaseManager::IsSupported() const { | |
| 141 return SafeBrowsingApiHandler::GetInstance() != nullptr; | |
| 142 } | |
| 143 | |
| 144 safe_browsing::ThreatSource RemoteSafeBrowsingDatabaseManager::GetThreatSource() | |
| 145 const { | |
| 146 return safe_browsing::ThreatSource::REMOTE; | |
| 147 } | |
| 148 | |
| 149 bool RemoteSafeBrowsingDatabaseManager::ChecksAreAlwaysAsync() const { | |
| 150 return true; | |
| 151 } | |
| 152 | |
| 153 bool RemoteSafeBrowsingDatabaseManager::CanCheckResourceType( | |
| 154 content::ResourceType resource_type) const { | |
| 155 return resource_types_to_check_.count(resource_type) > 0; | |
| 156 } | |
| 157 | |
| 158 bool RemoteSafeBrowsingDatabaseManager::CanCheckUrl(const GURL& url) const { | |
| 159 return url.SchemeIs(url::kHttpsScheme) || url.SchemeIs(url::kHttpScheme) || | |
| 160 url.SchemeIs(url::kFtpScheme); | |
| 161 } | |
| 162 | |
| 163 bool RemoteSafeBrowsingDatabaseManager::download_protection_enabled() const { | |
| 164 return false; | |
| 165 } | |
| 166 | |
| 167 bool RemoteSafeBrowsingDatabaseManager::CheckDownloadUrl( | |
| 168 const std::vector<GURL>& url_chain, | |
| 169 Client* client) { | |
| 170 NOTREACHED(); | |
| 171 return true; | |
| 172 } | |
| 173 | |
| 174 bool RemoteSafeBrowsingDatabaseManager::CheckExtensionIDs( | |
| 175 const std::set<std::string>& extension_ids, | |
| 176 Client* client) { | |
| 177 NOTREACHED(); | |
| 178 return true; | |
| 179 } | |
| 180 | |
| 181 bool RemoteSafeBrowsingDatabaseManager::MatchMalwareIP( | |
| 182 const std::string& ip_address) { | |
| 183 NOTREACHED(); | |
| 184 return false; | |
| 185 } | |
| 186 | |
| 187 bool RemoteSafeBrowsingDatabaseManager::MatchCsdWhitelistUrl(const GURL& url) { | |
| 188 NOTREACHED(); | |
| 189 return true; | |
| 190 } | |
| 191 | |
| 192 bool RemoteSafeBrowsingDatabaseManager::MatchDownloadWhitelistUrl( | |
| 193 const GURL& url) { | |
| 194 NOTREACHED(); | |
| 195 return true; | |
| 196 } | |
| 197 | |
| 198 bool RemoteSafeBrowsingDatabaseManager::MatchDownloadWhitelistString( | |
| 199 const std::string& str) { | |
| 200 NOTREACHED(); | |
| 201 return true; | |
| 202 } | |
| 203 | |
| 204 bool RemoteSafeBrowsingDatabaseManager::MatchInclusionWhitelistUrl( | |
| 205 const GURL& url) { | |
| 206 NOTREACHED(); | |
| 207 return true; | |
| 208 } | |
| 209 | |
| 210 bool RemoteSafeBrowsingDatabaseManager::IsMalwareKillSwitchOn() { | |
| 211 NOTREACHED(); | |
| 212 return true; | |
| 213 } | |
| 214 | |
| 215 bool RemoteSafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn() { | |
| 216 NOTREACHED(); | |
| 217 return true; | |
| 218 } | |
| 219 | |
| 220 bool RemoteSafeBrowsingDatabaseManager::CheckBrowseUrl(const GURL& url, | |
| 221 Client* client) { | |
| 222 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 223 if (!enabled_) | |
| 224 return true; | |
| 225 | |
| 226 bool can_check_url = CanCheckUrl(url); | |
| 227 UMA_HISTOGRAM_BOOLEAN("SB2.RemoteCall.CanCheckUrl", can_check_url); | |
| 228 if (!can_check_url) | |
| 229 return true; // Safe, continue right away. | |
| 230 | |
| 231 scoped_ptr<ClientRequest> req(new ClientRequest(client, this, url)); | |
| 232 std::vector<SBThreatType> threat_types; // Not currently used. | |
| 233 | |
| 234 DVLOG(1) << "Checking for client " << client << " and URL " << url; | |
| 235 SafeBrowsingApiHandler* api_handler = SafeBrowsingApiHandler::GetInstance(); | |
| 236 // This shouldn't happen since SafeBrowsingResourceThrottle checks | |
| 237 // IsSupported() ealier. | |
| 238 DCHECK(api_handler) << "SafeBrowsingApiHandler was never constructed"; | |
| 239 api_handler->StartURLCheck( | |
| 240 base::Bind(&ClientRequest::OnRequestDoneWeak, req->GetWeakPtr()), url, | |
| 241 threat_types); | |
| 242 | |
| 243 UMA_HISTOGRAM_COUNTS_10000("SB2.RemoteCall.ChecksPending", | |
| 244 current_requests_.size()); | |
| 245 current_requests_.push_back(req.release()); | |
| 246 | |
| 247 // Defer the resource load. | |
| 248 return false; | |
| 249 } | |
| 250 | |
| 251 void RemoteSafeBrowsingDatabaseManager::CancelCheck(Client* client) { | |
| 252 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 253 DCHECK(enabled_); | |
| 254 for (auto itr = current_requests_.begin(); itr != current_requests_.end(); | |
| 255 ++itr) { | |
| 256 if ((*itr)->client() == client) { | |
| 257 DVLOG(2) << "Canceling check for URL " << (*itr)->url(); | |
| 258 delete *itr; | |
| 259 current_requests_.erase(itr); | |
| 260 return; | |
| 261 } | |
| 262 } | |
| 263 NOTREACHED(); | |
| 264 } | |
| 265 | |
| 266 void RemoteSafeBrowsingDatabaseManager::StartOnIOThread() { | |
| 267 VLOG(1) << "RemoteSafeBrowsingDatabaseManager starting"; | |
| 268 enabled_ = true; | |
| 269 } | |
| 270 | |
| 271 void RemoteSafeBrowsingDatabaseManager::StopOnIOThread(bool shutdown) { | |
| 272 // |shutdown| is not used. | |
| 273 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 274 DVLOG(1) << "RemoteSafeBrowsingDatabaseManager stopping"; | |
| 275 | |
| 276 // Call back and delete any remaining clients. OnRequestDone() modifies | |
| 277 // |current_requests_|, so we make a copy first. | |
| 278 std::vector<ClientRequest*> to_callback(current_requests_); | |
| 279 for (auto req : to_callback) { | |
| 280 DVLOG(1) << "Stopping: Invoking unfinished req for URL " << req->url(); | |
| 281 req->OnRequestDone(SB_THREAT_TYPE_SAFE, std::string()); | |
| 282 } | |
| 283 enabled_ = false; | |
| 284 } | |
| 285 | |
| 286 } // namespace safe_browsing | |
| OLD | NEW |