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()) | |
Elly Fong-Jones
2015/04/08 16:25:33
Is this an unrelated bugfix?
Randy Smith (Not in Mondays)
2015/04/08 21:06:05
Not really. CanUse() used to use the above test,
| |
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 |