| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/safe_browsing_db/database_manager.h" | 5 #include "components/safe_browsing_db/database_manager.h" |
| 6 | 6 |
| 7 #include "components/safe_browsing_db/v4_get_hash_protocol_manager.h" | 7 #include "components/safe_browsing_db/v4_get_hash_protocol_manager.h" |
| 8 #include "content/public/browser/browser_thread.h" | 8 #include "content/public/browser/browser_thread.h" |
| 9 #include "net/url_request/url_request_context_getter.h" | 9 #include "net/url_request/url_request_context_getter.h" |
| 10 #include "url/gurl.h" | 10 #include "url/gurl.h" |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 87 | 87 |
| 88 // There can only be one in-progress check for the same client at a time. | 88 // There can only be one in-progress check for the same client at a time. |
| 89 DCHECK(FindClientApiCheck(client) == api_checks_.end()); | 89 DCHECK(FindClientApiCheck(client) == api_checks_.end()); |
| 90 | 90 |
| 91 // Compute a list of hashes for this url. | 91 // Compute a list of hashes for this url. |
| 92 std::vector<SBFullHash> full_hashes; | 92 std::vector<SBFullHash> full_hashes; |
| 93 UrlToFullHashes(url, false, &full_hashes); | 93 UrlToFullHashes(url, false, &full_hashes); |
| 94 if (full_hashes.empty()) | 94 if (full_hashes.empty()) |
| 95 return true; | 95 return true; |
| 96 | 96 |
| 97 // Copy to prefixes. | 97 // First check the cache. |
| 98 std::vector<SBPrefix> prefixes; | 98 |
| 99 for (const SBFullHash& full_hash : full_hashes) { | 99 // Used to determine cache expiration. |
| 100 prefixes.push_back(full_hash.prefix); | 100 base::Time now = base::Time::Now(); |
| 101 } | 101 |
| 102 // Multiple full hashes could share a prefix, remove duplicates. | 102 std::vector<SBFullHashResult> cached_results; |
| 103 std::sort(prefixes.begin(), prefixes.end()); | 103 std::vector<SBPrefix> prefixes_needing_reqs; |
| 104 prefixes.erase(std::unique(prefixes.begin(), prefixes.end()), prefixes.end()); | 104 GetFullHashCachedResults(SB_THREAT_TYPE_API_ABUSE, |
| 105 DCHECK(!prefixes.empty()); | 105 full_hashes, now, &prefixes_needing_reqs, &cached_results); |
| 106 |
| 107 if (prefixes_needing_reqs.empty() && cached_results.empty()) |
| 108 return true; |
| 106 | 109 |
| 107 SafeBrowsingApiCheck* check = | 110 SafeBrowsingApiCheck* check = |
| 108 new SafeBrowsingApiCheck(url, prefixes, full_hashes, client); | 111 new SafeBrowsingApiCheck(url, prefixes_needing_reqs, full_hashes, |
| 112 cached_results, client); |
| 109 api_checks_.insert(check); | 113 api_checks_.insert(check); |
| 110 | 114 |
| 111 // TODO(kcarattini): Implement cache compliance. | 115 if (prefixes_needing_reqs.empty()) { |
| 112 v4_get_hash_protocol_manager_->GetFullHashesWithApis(prefixes, | 116 // We can call the callback immediately if no prefixes require a request. |
| 117 // The |full_hash_results| representing the results fromt eh SB server will |
| 118 // be empty. |
| 119 std::vector<SBFullHashResult> full_hash_results; |
| 120 HandleGetHashesWithApisResults(check, full_hash_results, base::Time()); |
| 121 return false; |
| 122 } |
| 123 |
| 124 v4_get_hash_protocol_manager_->GetFullHashesWithApis(prefixes_needing_reqs, |
| 113 base::Bind(&SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults, | 125 base::Bind(&SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults, |
| 114 base::Unretained(this), check)); | 126 base::Unretained(this), check)); |
| 115 | 127 |
| 116 return false; | 128 return false; |
| 117 } | 129 } |
| 118 | 130 |
| 131 void SafeBrowsingDatabaseManager::GetFullHashCachedResults( |
| 132 const SBThreatType& threat_type, |
| 133 const std::vector<SBFullHash>& full_hashes, |
| 134 base::Time now, |
| 135 std::vector<SBPrefix>* prefixes_needing_reqs, |
| 136 std::vector<SBFullHashResult>* cached_results) { |
| 137 DCHECK(prefixes_needing_reqs); |
| 138 prefixes_needing_reqs->clear(); |
| 139 DCHECK(cached_results); |
| 140 cached_results->clear(); |
| 141 |
| 142 // Caching behavior is documented here: |
| 143 // https://developers.google.com/safe-browsing/v4/caching#about-caching |
| 144 // |
| 145 // The cache operates as follows: |
| 146 // Lookup: |
| 147 // Case 1: The prefix is in the cache. |
| 148 // Case a: The full hash is in the cache. |
| 149 // Case i : The positive full hash result has not expired. |
| 150 // The result is unsafe and we do not need to send a new |
| 151 // request. |
| 152 // Case ii: The positive full hash result has expired. |
| 153 // We need to send a request for full hashes. |
| 154 // Case b: The full hash is not in the cache. |
| 155 // Case i : The negative cache entry has not expired. |
| 156 // The result is still safe and we do not need to send a |
| 157 // new request. |
| 158 // Case ii: The negative cache entry has expired. |
| 159 // We need to send a request for full hashes. |
| 160 // Case 2: The prefix is not in the cache. |
| 161 // We need to send a request for full hashes. |
| 162 // |
| 163 // Eviction: |
| 164 // SBCachedFullHashResult entries can be removed from the cache only when |
| 165 // the negative cache expire time and the cache expire time of all full |
| 166 // hash results for that prefix have expired. |
| 167 // Individual full hash results can be removed from the prefix's |
| 168 // cache entry if they expire AND their expire time is after the negative |
| 169 // cache expire time. |
| 170 // |
| 171 // TODO(kcarattini): Implement cache eviction. |
| 172 for (const SBFullHash& full_hash : full_hashes) { |
| 173 auto entry = v4_full_hash_cache_[threat_type].find(full_hash.prefix); |
| 174 if (entry != v4_full_hash_cache_[threat_type].end()) { |
| 175 // Case 1. |
| 176 const SBCachedFullHashResult& cache_result = entry->second; |
| 177 |
| 178 const SBFullHashResult* found_full_hash = nullptr; |
| 179 for (const SBFullHashResult& hash_result : cache_result.full_hashes) { |
| 180 if (SBFullHashEqual(full_hash, hash_result.hash)) { |
| 181 found_full_hash = &hash_result; |
| 182 break; |
| 183 } |
| 184 } |
| 185 |
| 186 if (found_full_hash) { |
| 187 // Case a. |
| 188 if (found_full_hash->cache_expire_after > now) { |
| 189 // Case i. |
| 190 cached_results->push_back(*found_full_hash); |
| 191 } else { |
| 192 // Case ii. |
| 193 prefixes_needing_reqs->push_back(full_hash.prefix); |
| 194 } |
| 195 } else { |
| 196 // Case b. |
| 197 if (cache_result.expire_after > now) { |
| 198 // Case i. |
| 199 } else { |
| 200 // Case ii. |
| 201 prefixes_needing_reqs->push_back(full_hash.prefix); |
| 202 } |
| 203 } |
| 204 } else { |
| 205 // Case 2. |
| 206 prefixes_needing_reqs->push_back(full_hash.prefix); |
| 207 } |
| 208 } |
| 209 |
| 210 // Multiple full hashes could share a prefix, remove duplicates. |
| 211 std::sort(prefixes_needing_reqs->begin(), prefixes_needing_reqs->end()); |
| 212 prefixes_needing_reqs->erase(std::unique(prefixes_needing_reqs->begin(), |
| 213 prefixes_needing_reqs->end()), prefixes_needing_reqs->end()); |
| 214 } |
| 215 |
| 119 void SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults( | 216 void SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults( |
| 120 SafeBrowsingApiCheck* check, | 217 SafeBrowsingApiCheck* check, |
| 121 const std::vector<SBFullHashResult>& full_hash_results, | 218 const std::vector<SBFullHashResult>& full_hash_results, |
| 122 const base::Time& negative_cache_expire) { | 219 const base::Time& negative_cache_expire) { |
| 123 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 220 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 124 DCHECK(check); | 221 DCHECK(check); |
| 125 | 222 |
| 126 // If the time is uninitialized, don't cache the results. | 223 // If the time is uninitialized, don't cache the results. |
| 127 if (!negative_cache_expire.is_null()) { | 224 if (!negative_cache_expire.is_null()) { |
| 128 // Cache the results. | 225 // Cache the results. |
| 129 // Create or reset all cached results for this prefix. | 226 // Create or reset all cached results for this prefix. |
| 130 for (const SBPrefix& prefix : check->prefixes()) { | 227 for (const SBPrefix& prefix : check->prefixes()) { |
| 131 api_cache_[prefix] = SBCachedFullHashResult(negative_cache_expire); | 228 v4_full_hash_cache_[SB_THREAT_TYPE_API_ABUSE][prefix] = |
| 229 SBCachedFullHashResult(negative_cache_expire); |
| 132 } | 230 } |
| 133 // Insert any full hash hits. Note that there may be one, multiple, or no | 231 // Insert any full hash hits. Note that there may be one, multiple, or no |
| 134 // full hashes for any given prefix. | 232 // full hashes for any given prefix. |
| 135 for (const SBFullHashResult& result : full_hash_results) { | 233 for (const SBFullHashResult& result : full_hash_results) { |
| 136 api_cache_[result.hash.prefix].full_hashes.push_back(result); | 234 v4_full_hash_cache_[SB_THREAT_TYPE_API_ABUSE][result.hash.prefix]. |
| 235 full_hashes.push_back(result); |
| 137 } | 236 } |
| 138 } | 237 } |
| 139 | 238 |
| 140 // If the check is not in |api_checks_| then the request was cancelled by the | 239 // If the check is not in |api_checks_| then the request was cancelled by the |
| 141 // client. | 240 // client. |
| 142 ApiCheckSet::iterator it = api_checks_.find(check); | 241 ApiCheckSet::iterator it = api_checks_.find(check); |
| 143 if (it == api_checks_.end()) | 242 if (it == api_checks_.end()) |
| 144 return; | 243 return; |
| 145 | 244 |
| 146 ThreatMetadata md; | 245 ThreatMetadata md; |
| 147 // Merge the metadata from all matching results. | 246 // Merge the metadata from all matching results. |
| 148 // TODO(kcarattini): This is O(N^2). Look at improving performance by | 247 // Note: A full hash may have a result in both the cached results (from |
| 149 // using a map, sorting or doing binary search etc.. | 248 // its own cache lookup) and in the server results (if another full hash |
| 150 for (const SBFullHashResult& result : full_hash_results) { | 249 // with the same prefix needed to request results from the server). In this |
| 151 for (const SBFullHash& full_hash : check->full_hashes()) { | 250 // unlikely case, the two results' metadata will be merged. |
| 152 if (SBFullHashEqual(full_hash, result.hash)) { | 251 PopulateApiMetadataResult(full_hash_results, check->full_hashes(), &md); |
| 153 md.api_permissions.insert(md.api_permissions.end(), | 252 PopulateApiMetadataResult(check->cached_results(), check->full_hashes(), &md); |
| 154 result.metadata.api_permissions.begin(), | |
| 155 result.metadata.api_permissions.end()); | |
| 156 break; | |
| 157 } | |
| 158 } | |
| 159 } | |
| 160 | 253 |
| 161 check->client()->OnCheckApiBlacklistUrlResult(check->url(), md); | 254 check->client()->OnCheckApiBlacklistUrlResult(check->url(), md); |
| 162 api_checks_.erase(it); | 255 api_checks_.erase(it); |
| 163 delete check; | 256 delete check; |
| 164 } | 257 } |
| 165 | 258 |
| 259 // TODO(kcarattini): This is O(N^2). Look at improving performance by |
| 260 // using a map, sorting or doing binary search etc.. |
| 261 void SafeBrowsingDatabaseManager::PopulateApiMetadataResult( |
| 262 const std::vector<SBFullHashResult>& results, |
| 263 const std::vector<SBFullHash>& full_hashes, |
| 264 ThreatMetadata* md) { |
| 265 DCHECK(md); |
| 266 for (const SBFullHashResult& result : results) { |
| 267 for (const SBFullHash& full_hash : full_hashes) { |
| 268 if (SBFullHashEqual(full_hash, result.hash)) { |
| 269 md->api_permissions.insert(md->api_permissions.end(), |
| 270 result.metadata.api_permissions.begin(), |
| 271 result.metadata.api_permissions.end()); |
| 272 break; |
| 273 } |
| 274 } |
| 275 } |
| 276 } |
| 277 |
| 166 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::SafeBrowsingApiCheck( | 278 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::SafeBrowsingApiCheck( |
| 167 const GURL& url, const std::vector<SBPrefix>& prefixes, | 279 const GURL& url, |
| 168 const std::vector<SBFullHash>& full_hashes, Client* client) | 280 const std::vector<SBPrefix>& prefixes, |
| 281 const std::vector<SBFullHash>& full_hashes, |
| 282 const std::vector<SBFullHashResult>& cached_results, |
| 283 Client* client) |
| 169 : url_(url), prefixes_(prefixes), full_hashes_(full_hashes), | 284 : url_(url), prefixes_(prefixes), full_hashes_(full_hashes), |
| 170 client_(client) { | 285 cached_results_(cached_results), client_(client) { |
| 171 } | 286 } |
| 172 | 287 |
| 173 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::~SafeBrowsingApiCheck() { | 288 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::~SafeBrowsingApiCheck() { |
| 174 } | 289 } |
| 175 | 290 |
| 176 } // namespace safe_browsing | 291 } // namespace safe_browsing |
| OLD | NEW |