| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "net/base/sdch_manager.h" | 5 #include "net/base/sdch_manager.h" |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
| 10 #include "base/strings/string_number_conversions.h" | 10 #include "base/strings/string_number_conversions.h" |
| 11 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
| 12 #include "base/time/default_clock.h" | 12 #include "base/time/default_clock.h" |
| 13 #include "base/values.h" | 13 #include "base/values.h" |
| 14 #include "crypto/sha2.h" | 14 #include "crypto/sha2.h" |
| 15 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | |
| 16 #include "net/base/sdch_observer.h" | 15 #include "net/base/sdch_observer.h" |
| 17 #include "net/url_request/url_request_http_job.h" | 16 #include "net/url_request/url_request_http_job.h" |
| 18 | 17 |
| 19 namespace { | 18 namespace { |
| 20 | 19 |
| 21 void StripTrailingDot(GURL* gurl) { | 20 void StripTrailingDot(GURL* gurl) { |
| 22 std::string host(gurl->host()); | 21 std::string host(gurl->host()); |
| 23 | 22 |
| 24 if (host.empty()) | 23 if (host.empty()) |
| 25 return; | 24 return; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 38 } // namespace | 37 } // namespace |
| 39 | 38 |
| 40 namespace net { | 39 namespace net { |
| 41 | 40 |
| 42 // static | 41 // static |
| 43 bool SdchManager::g_sdch_enabled_ = true; | 42 bool SdchManager::g_sdch_enabled_ = true; |
| 44 | 43 |
| 45 // static | 44 // static |
| 46 bool SdchManager::g_secure_scheme_supported_ = true; | 45 bool SdchManager::g_secure_scheme_supported_ = true; |
| 47 | 46 |
| 48 SdchManager::Dictionary::Dictionary(const std::string& dictionary_text, | |
| 49 size_t offset, | |
| 50 const std::string& client_hash, | |
| 51 const std::string& server_hash, | |
| 52 const GURL& gurl, | |
| 53 const std::string& domain, | |
| 54 const std::string& path, | |
| 55 const base::Time& expiration, | |
| 56 const std::set<int>& ports) | |
| 57 : text_(dictionary_text, offset), | |
| 58 client_hash_(client_hash), | |
| 59 server_hash_(server_hash), | |
| 60 url_(gurl), | |
| 61 domain_(domain), | |
| 62 path_(path), | |
| 63 expiration_(expiration), | |
| 64 ports_(ports), | |
| 65 clock_(new base::DefaultClock) { | |
| 66 } | |
| 67 | |
| 68 SdchManager::Dictionary::Dictionary(const SdchManager::Dictionary& rhs) | |
| 69 : text_(rhs.text_), | |
| 70 client_hash_(rhs.client_hash_), | |
| 71 server_hash_(rhs.server_hash_), | |
| 72 url_(rhs.url_), | |
| 73 domain_(rhs.domain_), | |
| 74 path_(rhs.path_), | |
| 75 expiration_(rhs.expiration_), | |
| 76 ports_(rhs.ports_), | |
| 77 clock_(new base::DefaultClock) { | |
| 78 } | |
| 79 | |
| 80 SdchManager::Dictionary::~Dictionary() {} | |
| 81 | |
| 82 // Security functions restricting loads and use of dictionaries. | |
| 83 | |
| 84 // static | |
| 85 SdchProblemCode SdchManager::Dictionary::CanSet(const std::string& domain, | |
| 86 const std::string& path, | |
| 87 const std::set<int>& ports, | |
| 88 const GURL& dictionary_url) { | |
| 89 /* | |
| 90 A dictionary is invalid and must not be stored if any of the following are | |
| 91 true: | |
| 92 1. The dictionary has no Domain attribute. | |
| 93 2. The effective host name that derives from the referer URL host name does | |
| 94 not domain-match the Domain attribute. | |
| 95 3. The Domain attribute is a top level domain. | |
| 96 4. The referer URL host is a host domain name (not IP address) and has the | |
| 97 form HD, where D is the value of the Domain attribute, and H is a string | |
| 98 that contains one or more dots. | |
| 99 5. If the dictionary has a Port attribute and the referer URL's port was not | |
| 100 in the list. | |
| 101 */ | |
| 102 | |
| 103 // TODO(jar): Redirects in dictionary fetches might plausibly be problematic, | |
| 104 // and hence the conservative approach is to not allow any redirects (if there | |
| 105 // were any... then don't allow the dictionary to be set). | |
| 106 | |
| 107 if (domain.empty()) | |
| 108 return SDCH_DICTIONARY_MISSING_DOMAIN_SPECIFIER; // Domain is required. | |
| 109 | |
| 110 if (registry_controlled_domains::GetDomainAndRegistry( | |
| 111 domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) | |
| 112 .empty()) { | |
| 113 return SDCH_DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN; // domain was a TLD. | |
| 114 } | |
| 115 | |
| 116 if (!Dictionary::DomainMatch(dictionary_url, domain)) | |
| 117 return SDCH_DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL; | |
| 118 | |
| 119 std::string referrer_url_host = dictionary_url.host(); | |
| 120 size_t postfix_domain_index = referrer_url_host.rfind(domain); | |
| 121 // See if it is indeed a postfix, or just an internal string. | |
| 122 if (referrer_url_host.size() == postfix_domain_index + domain.size()) { | |
| 123 // It is a postfix... so check to see if there's a dot in the prefix. | |
| 124 size_t end_of_host_index = referrer_url_host.find_first_of('.'); | |
| 125 if (referrer_url_host.npos != end_of_host_index && | |
| 126 end_of_host_index < postfix_domain_index) { | |
| 127 return SDCH_DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX; | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 if (!ports.empty() && 0 == ports.count(dictionary_url.EffectiveIntPort())) | |
| 132 return SDCH_DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL; | |
| 133 | |
| 134 return SDCH_OK; | |
| 135 } | |
| 136 | |
| 137 SdchProblemCode SdchManager::Dictionary::CanUse( | |
| 138 const GURL& target_url) const { | |
| 139 /* | |
| 140 1. The request URL's host name domain-matches the Domain attribute of the | |
| 141 dictionary. | |
| 142 2. If the dictionary has a Port attribute, the request port is one of the | |
| 143 ports listed in the Port attribute. | |
| 144 3. The request URL path-matches the path attribute of the dictionary. | |
| 145 4. The request is not an HTTPS request. | |
| 146 We can override (ignore) item (4) only when we have explicitly enabled | |
| 147 HTTPS support AND the dictionary acquisition scheme matches the target | |
| 148 url scheme. | |
| 149 */ | |
| 150 if (!DomainMatch(target_url, domain_)) | |
| 151 return SDCH_DICTIONARY_FOUND_HAS_WRONG_DOMAIN; | |
| 152 | |
| 153 if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) | |
| 154 return SDCH_DICTIONARY_FOUND_HAS_WRONG_PORT_LIST; | |
| 155 | |
| 156 if (path_.size() && !PathMatch(target_url.path(), path_)) | |
| 157 return SDCH_DICTIONARY_FOUND_HAS_WRONG_PATH; | |
| 158 | |
| 159 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) | |
| 160 return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; | |
| 161 | |
| 162 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) | |
| 163 return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; | |
| 164 | |
| 165 // TODO(jar): Remove overly restrictive failsafe test (added per security | |
| 166 // review) when we have a need to be more general. | |
| 167 if (!target_url.SchemeIsHTTPOrHTTPS()) | |
| 168 return SDCH_ATTEMPT_TO_DECODE_NON_HTTP_DATA; | |
| 169 | |
| 170 return SDCH_OK; | |
| 171 } | |
| 172 | |
| 173 // static | |
| 174 bool SdchManager::Dictionary::PathMatch(const std::string& path, | |
| 175 const std::string& restriction) { | |
| 176 /* Must be either: | |
| 177 1. P2 is equal to P1 | |
| 178 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the | |
| 179 character following P2 in P1 is "/". | |
| 180 */ | |
| 181 if (path == restriction) | |
| 182 return true; | |
| 183 size_t prefix_length = restriction.size(); | |
| 184 if (prefix_length > path.size()) | |
| 185 return false; // Can't be a prefix. | |
| 186 if (0 != path.compare(0, prefix_length, restriction)) | |
| 187 return false; | |
| 188 return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/'; | |
| 189 } | |
| 190 | |
| 191 // static | |
| 192 bool SdchManager::Dictionary::DomainMatch(const GURL& gurl, | |
| 193 const std::string& restriction) { | |
| 194 // TODO(jar): This is not precisely a domain match definition. | |
| 195 return gurl.DomainIs(restriction.data(), restriction.size()); | |
| 196 } | |
| 197 | |
| 198 bool SdchManager::Dictionary::Expired() const { | |
| 199 return clock_->Now() > expiration_; | |
| 200 } | |
| 201 | |
| 202 void SdchManager::Dictionary::SetClockForTesting( | |
| 203 scoped_ptr<base::Clock> clock) { | |
| 204 clock_ = clock.Pass(); | |
| 205 } | |
| 206 | |
| 207 SdchManager::DictionarySet::DictionarySet() {} | 47 SdchManager::DictionarySet::DictionarySet() {} |
| 208 | 48 |
| 209 SdchManager::DictionarySet::~DictionarySet() {} | 49 SdchManager::DictionarySet::~DictionarySet() {} |
| 210 | 50 |
| 211 std::string SdchManager::DictionarySet::GetDictionaryClientHashList() const { | 51 std::string SdchManager::DictionarySet::GetDictionaryClientHashList() const { |
| 212 std::string result; | 52 std::string result; |
| 213 bool first = true; | 53 bool first = true; |
| 214 for (const auto& entry: dictionaries_) { | 54 for (const auto& entry: dictionaries_) { |
| 215 if (!first) | 55 if (!first) |
| 216 result.append(","); | 56 result.append(","); |
| 217 | 57 |
| 218 result.append(entry.second->data.client_hash()); | 58 result.append(entry.second->data.client_hash()); |
| 219 first = false; | 59 first = false; |
| 220 } | 60 } |
| 221 return result; | 61 return result; |
| 222 } | 62 } |
| 223 | 63 |
| 224 const SdchManager::Dictionary* SdchManager::DictionarySet::GetDictionary( | 64 const SdchDictionary* SdchManager::DictionarySet::GetDictionary( |
| 225 const std::string& hash) const { | 65 const std::string& hash) const { |
| 226 auto it = dictionaries_.find(hash); | 66 auto it = dictionaries_.find(hash); |
| 227 if (it == dictionaries_.end()) | 67 if (it == dictionaries_.end()) |
| 228 return NULL; | 68 return NULL; |
| 229 | 69 |
| 230 return &it->second->data; | 70 return &it->second->data; |
| 231 } | 71 } |
| 232 | 72 |
| 233 bool SdchManager::DictionarySet::Empty() const { | 73 bool SdchManager::DictionarySet::Empty() const { |
| 234 return dictionaries_.empty(); | 74 return dictionaries_.empty(); |
| 235 } | 75 } |
| 236 | 76 |
| 237 void SdchManager::DictionarySet::AddDictionary( | 77 void SdchManager::DictionarySet::AddDictionary( |
| 238 const std::string& server_hash, | 78 const std::string& server_hash, |
| 239 const scoped_refptr<base::RefCountedData<SdchManager::Dictionary>>& | 79 const scoped_refptr<base::RefCountedData<SdchDictionary>>& dictionary) { |
| 240 dictionary) { | |
| 241 DCHECK(dictionaries_.end() == dictionaries_.find(server_hash)); | 80 DCHECK(dictionaries_.end() == dictionaries_.find(server_hash)); |
| 242 | 81 |
| 243 dictionaries_[server_hash] = dictionary; | 82 dictionaries_[server_hash] = dictionary; |
| 244 } | 83 } |
| 245 | 84 |
| 246 SdchManager::SdchManager() : factory_(this) { | 85 SdchManager::SdchManager() : factory_(this) { |
| 247 DCHECK(thread_checker_.CalledOnValidThread()); | 86 DCHECK(thread_checker_.CalledOnValidThread()); |
| 248 } | 87 } |
| 249 | 88 |
| 250 SdchManager::~SdchManager() { | 89 SdchManager::~SdchManager() { |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 423 } | 262 } |
| 424 | 263 |
| 425 scoped_ptr<SdchManager::DictionarySet> | 264 scoped_ptr<SdchManager::DictionarySet> |
| 426 SdchManager::GetDictionarySet(const GURL& target_url) { | 265 SdchManager::GetDictionarySet(const GURL& target_url) { |
| 427 if (IsInSupportedDomain(target_url) != SDCH_OK) | 266 if (IsInSupportedDomain(target_url) != SDCH_OK) |
| 428 return NULL; | 267 return NULL; |
| 429 | 268 |
| 430 int count = 0; | 269 int count = 0; |
| 431 scoped_ptr<SdchManager::DictionarySet> result(new DictionarySet); | 270 scoped_ptr<SdchManager::DictionarySet> result(new DictionarySet); |
| 432 for (const auto& entry: dictionaries_) { | 271 for (const auto& entry: dictionaries_) { |
| 272 if (!secure_scheme_supported() && target_url.SchemeIsSecure()) |
| 273 continue; |
| 433 if (entry.second->data.CanUse(target_url) != SDCH_OK) | 274 if (entry.second->data.CanUse(target_url) != SDCH_OK) |
| 434 continue; | 275 continue; |
| 435 if (entry.second->data.Expired()) | 276 if (entry.second->data.Expired()) |
| 436 continue; | 277 continue; |
| 437 ++count; | 278 ++count; |
| 438 result->AddDictionary(entry.first, entry.second); | 279 result->AddDictionary(entry.first, entry.second); |
| 439 } | 280 } |
| 440 | 281 |
| 441 if (count == 0) | 282 if (count == 0) |
| 442 return NULL; | 283 return NULL; |
| 443 | 284 |
| 444 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); | 285 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); |
| 445 | 286 |
| 446 return result.Pass(); | 287 return result.Pass(); |
| 447 } | 288 } |
| 448 | 289 |
| 449 scoped_ptr<SdchManager::DictionarySet> | 290 scoped_ptr<SdchManager::DictionarySet> |
| 450 SdchManager::GetDictionarySetByHash( | 291 SdchManager::GetDictionarySetByHash( |
| 451 const GURL& target_url, | 292 const GURL& target_url, |
| 452 const std::string& server_hash, | 293 const std::string& server_hash, |
| 453 SdchProblemCode* problem_code) { | 294 SdchProblemCode* problem_code) { |
| 454 scoped_ptr<SdchManager::DictionarySet> result; | 295 scoped_ptr<SdchManager::DictionarySet> result; |
| 455 | 296 |
| 456 *problem_code = SDCH_DICTIONARY_HASH_NOT_FOUND; | 297 *problem_code = SDCH_DICTIONARY_HASH_NOT_FOUND; |
| 457 const auto& it = dictionaries_.find(server_hash); | 298 const auto& it = dictionaries_.find(server_hash); |
| 458 if (it == dictionaries_.end()) | 299 if (it == dictionaries_.end()) |
| 459 return result.Pass(); | 300 return result.Pass(); |
| 460 | 301 |
| 302 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) { |
| 303 *problem_code = SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
| 304 return result.Pass(); |
| 305 } |
| 306 |
| 461 *problem_code = it->second->data.CanUse(target_url); | 307 *problem_code = it->second->data.CanUse(target_url); |
| 462 if (*problem_code != SDCH_OK) | 308 if (*problem_code != SDCH_OK) |
| 463 return result.Pass(); | 309 return result.Pass(); |
| 464 | 310 |
| 465 result.reset(new DictionarySet); | 311 result.reset(new DictionarySet); |
| 466 result->AddDictionary(it->first, it->second); | 312 result->AddDictionary(it->first, it->second); |
| 467 return result.Pass(); | 313 return result.Pass(); |
| 468 } | 314 } |
| 469 | 315 |
| 470 // static | 316 // static |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 580 } | 426 } |
| 581 | 427 |
| 582 // Narrow fix for http://crbug.com/389451. | 428 // Narrow fix for http://crbug.com/389451. |
| 583 GURL dictionary_url_normalized(dictionary_url); | 429 GURL dictionary_url_normalized(dictionary_url); |
| 584 StripTrailingDot(&dictionary_url_normalized); | 430 StripTrailingDot(&dictionary_url_normalized); |
| 585 | 431 |
| 586 SdchProblemCode rv = IsInSupportedDomain(dictionary_url_normalized); | 432 SdchProblemCode rv = IsInSupportedDomain(dictionary_url_normalized); |
| 587 if (rv != SDCH_OK) | 433 if (rv != SDCH_OK) |
| 588 return rv; | 434 return rv; |
| 589 | 435 |
| 590 rv = Dictionary::CanSet(domain, path, ports, dictionary_url_normalized); | 436 rv = SdchDictionary::CanSet(domain, path, ports, dictionary_url_normalized); |
| 591 if (rv != SDCH_OK) | 437 if (rv != SDCH_OK) |
| 592 return rv; | 438 return rv; |
| 593 | 439 |
| 594 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); | 440 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); |
| 595 DVLOG(1) << "Loaded dictionary with client hash " << client_hash | 441 DVLOG(1) << "Loaded dictionary with client hash " << client_hash |
| 596 << " and server hash " << server_hash; | 442 << " and server hash " << server_hash; |
| 597 Dictionary dictionary(dictionary_text, header_end + 2, client_hash, | 443 SdchDictionary dictionary(dictionary_text, header_end + 2, client_hash, |
| 598 server_hash, dictionary_url_normalized, domain, path, | 444 server_hash, dictionary_url_normalized, domain, |
| 599 expiration, ports); | 445 path, expiration, ports); |
| 600 dictionaries_[server_hash] = | 446 dictionaries_[server_hash] = |
| 601 new base::RefCountedData<Dictionary>(dictionary); | 447 new base::RefCountedData<SdchDictionary>(dictionary); |
| 602 if (server_hash_p) | 448 if (server_hash_p) |
| 603 *server_hash_p = server_hash; | 449 *server_hash_p = server_hash; |
| 604 | 450 |
| 605 return SDCH_OK; | 451 return SDCH_OK; |
| 606 } | 452 } |
| 607 | 453 |
| 608 SdchProblemCode SdchManager::RemoveSdchDictionary( | 454 SdchProblemCode SdchManager::RemoveSdchDictionary( |
| 609 const std::string& server_hash) { | 455 const std::string& server_hash) { |
| 610 if (dictionaries_.find(server_hash) == dictionaries_.end()) | 456 if (dictionaries_.find(server_hash) == dictionaries_.end()) |
| 611 return SDCH_DICTIONARY_HASH_NOT_FOUND; | 457 return SDCH_DICTIONARY_HASH_NOT_FOUND; |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 671 entry_dict->SetInteger("tries", it->second.count); | 517 entry_dict->SetInteger("tries", it->second.count); |
| 672 entry_dict->SetInteger("reason", it->second.reason); | 518 entry_dict->SetInteger("reason", it->second.reason); |
| 673 entry_list->Append(entry_dict); | 519 entry_list->Append(entry_dict); |
| 674 } | 520 } |
| 675 value->Set("blacklisted", entry_list); | 521 value->Set("blacklisted", entry_list); |
| 676 | 522 |
| 677 return value; | 523 return value; |
| 678 } | 524 } |
| 679 | 525 |
| 680 } // namespace net | 526 } // namespace net |
| OLD | NEW |