Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(56)

Side by Side Diff: net/base/sdch_manager.cc

Issue 574283006: Fix dictionary domain check problem. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Incorporated comments and forbid dictionary domains with "."s. Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/base/sdch_manager.h ('k') | net/base/sdch_manager_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "crypto/sha2.h" 12 #include "crypto/sha2.h"
13 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 13 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
14 #include "net/url_request/url_request_http_job.h" 14 #include "net/url_request/url_request_http_job.h"
15 15
16 namespace {
17
18 // Spec collision warning: URLs may have domain names which have a
19 // trailing "." (e.g. "http://this.is.a.domain.com./file.txt"). The
20 // SDCH spec was written to parallel RFC 2965 (an old version of the
21 // cookie spec, which didn't precisely address the issue of trailing "."s.
22 // The newest version of the cookie spec, RFC 6265, specifically prohibits
23 // trailing "."s from cookie domains. So we prohibit trailing "."s in
24 // dictionary domains, and strip any trailing "." from URL domains.
25 bool StripTrailingDot(std::string* host) {
26 if (host->size() == 0)
Ryan Sleevi 2014/09/22 20:30:37 host->empty() (a nit I hold on to from the days o
Randy Smith (Not in Mondays) 2014/09/23 19:52:58 Done.
27 return false;
28
29 if ((*host)[host->size() - 1] != '.')
Ryan Sleevi 2014/09/22 20:30:36 if (*host->rbegin() != '.') return false; Lazin
Randy Smith (Not in Mondays) 2014/09/23 19:52:58 Agreed :-}. Done.
30 return false;
31
32 host->resize(host->size() - 1);
33 return true;
34 }
35
36 void StripTrailingDotURL(GURL* gurl) {
Ryan Sleevi 2014/09/22 20:30:36 naming wise, this felt a little weird, although I
Randy Smith (Not in Mondays) 2014/09/23 19:52:58 I don't want "NormalizeToDictionary" because to me
37 std::string host(gurl->host());
38
39 if (!StripTrailingDot(&host))
40 return;
Ryan Sleevi 2014/09/22 20:30:36 Just inline this? You never use "StripTrailingDot"
Randy Smith (Not in Mondays) 2014/09/23 19:52:58 Done.
41
42 GURL::Replacements replacements;
43 replacements.SetHostStr(host);
44 *gurl = gurl->ReplaceComponents(replacements);
45 return;
46 }
47
48 } // namespace
49
16 namespace net { 50 namespace net {
17 51
18 //------------------------------------------------------------------------------ 52 //------------------------------------------------------------------------------
19 // static 53 // static
20 54
21 // Adjust SDCH limits downwards for mobile. 55 // Adjust SDCH limits downwards for mobile.
22 #if defined(OS_ANDROID) || defined(OS_IOS) 56 #if defined(OS_ANDROID) || defined(OS_IOS)
23 // static 57 // static
24 const size_t SdchManager::kMaxDictionaryCount = 1; 58 const size_t SdchManager::kMaxDictionaryCount = 1;
25 const size_t SdchManager::kMaxDictionarySize = 500 * 1000; 59 const size_t SdchManager::kMaxDictionarySize = 500 * 1000;
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) { 156 registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) {
123 SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN); 157 SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN);
124 return false; // domain was a TLD. 158 return false; // domain was a TLD.
125 } 159 }
126 if (!Dictionary::DomainMatch(dictionary_url, domain)) { 160 if (!Dictionary::DomainMatch(dictionary_url, domain)) {
127 SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL); 161 SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL);
128 return false; 162 return false;
129 } 163 }
130 164
131 std::string referrer_url_host = dictionary_url.host(); 165 std::string referrer_url_host = dictionary_url.host();
132 size_t postfix_domain_index = referrer_url_host.rfind(domain); 166 size_t postfix_domain_index = referrer_url_host.rfind(domain);
Ryan Sleevi 2014/09/22 22:49:42 BUG: Check your npos results! Bad things happen he
Randy Smith (Not in Mondays) 2014/09/23 19:52:58 Ignoring all comments on untouched code for this C
133 // See if it is indeed a postfix, or just an internal string. 167 // See if it is indeed a postfix, or just an internal string.
134 if (referrer_url_host.size() == postfix_domain_index + domain.size()) { 168 if (referrer_url_host.size() == postfix_domain_index + domain.size()) {
Ryan Sleevi 2014/09/22 22:49:42 I find this logic really, really weird. Why not j
135 // It is a postfix... so check to see if there's a dot in the prefix. 169 // It is a postfix... so check to see if there's a dot in the prefix.
136 size_t end_of_host_index = referrer_url_host.find_first_of('.'); 170 size_t end_of_host_index = referrer_url_host.find_first_of('.');
137 if (referrer_url_host.npos != end_of_host_index && 171 if (referrer_url_host.npos != end_of_host_index &&
138 end_of_host_index < postfix_domain_index) { 172 end_of_host_index < postfix_domain_index) {
139 SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX); 173 SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX);
140 return false; 174 return false;
141 } 175 }
142 } 176 }
143 177
144 if (!ports.empty() 178 if (!ports.empty()
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 } 432 }
399 433
400 return true; 434 return true;
401 } 435 }
402 436
403 void SdchManager::GetVcdiffDictionary( 437 void SdchManager::GetVcdiffDictionary(
404 const std::string& server_hash, 438 const std::string& server_hash,
405 const GURL& referring_url, 439 const GURL& referring_url,
406 scoped_refptr<Dictionary>* dictionary) { 440 scoped_refptr<Dictionary>* dictionary) {
407 DCHECK(CalledOnValidThread()); 441 DCHECK(CalledOnValidThread());
442
443 GURL referring_url_hdn(referring_url);
444 StripTrailingDotURL(&referring_url_hdn);
445
408 *dictionary = NULL; 446 *dictionary = NULL;
409 DictionaryMap::iterator it = dictionaries_.find(server_hash); 447 DictionaryMap::iterator it = dictionaries_.find(server_hash);
410 if (it == dictionaries_.end()) { 448 if (it == dictionaries_.end()) {
411 return; 449 return;
412 } 450 }
413 scoped_refptr<Dictionary> matching_dictionary = it->second; 451 scoped_refptr<Dictionary> matching_dictionary = it->second;
414 if (!IsInSupportedDomain(referring_url)) 452 if (!IsInSupportedDomain(referring_url_hdn))
415 return; 453 return;
416 if (!matching_dictionary->CanUse(referring_url)) 454 if (!matching_dictionary->CanUse(referring_url_hdn))
Ryan Sleevi 2014/09/22 20:30:37 OK, I'm fairly confused now why it's necessary to
Randy Smith (Not in Mondays) 2014/09/22 20:43:51 This isn't necessary; it's the checks in AddSdchDi
417 return; 455 return;
418 *dictionary = matching_dictionary; 456 *dictionary = matching_dictionary;
419 } 457 }
420 458
421 // TODO(jar): If we have evictions from the dictionaries_, then we need to 459 // TODO(jar): If we have evictions from the dictionaries_, then we need to
422 // change this interface to return a list of reference counted Dictionary 460 // change this interface to return a list of reference counted Dictionary
423 // instances that can be used if/when a server specifies one. 461 // instances that can be used if/when a server specifies one.
424 void SdchManager::GetAvailDictionaryList(const GURL& target_url, 462 void SdchManager::GetAvailDictionaryList(const GURL& target_url,
425 std::string* list) { 463 std::string* list) {
426 DCHECK(CalledOnValidThread()); 464 DCHECK(CalledOnValidThread());
465
466 GURL target_url_hdn(target_url);
467 StripTrailingDotURL(&target_url_hdn);
468
427 int count = 0; 469 int count = 0;
428 for (DictionaryMap::iterator it = dictionaries_.begin(); 470 for (DictionaryMap::iterator it = dictionaries_.begin();
429 it != dictionaries_.end(); ++it) { 471 it != dictionaries_.end(); ++it) {
430 if (!IsInSupportedDomain(target_url)) 472 if (!IsInSupportedDomain(target_url_hdn))
431 continue; 473 continue;
432 if (!it->second->CanAdvertise(target_url)) 474 if (!it->second->CanAdvertise(target_url_hdn))
433 continue; 475 continue;
434 ++count; 476 ++count;
435 if (!list->empty()) 477 if (!list->empty())
436 list->append(","); 478 list->append(",");
437 list->append(it->second->client_hash()); 479 list->append(it->second->client_hash());
438 } 480 }
439 // Watch to see if we have corrupt or numerous dictionaries. 481 // Watch to see if we have corrupt or numerous dictionaries.
440 if (count > 0) 482 if (count > 0)
441 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); 483 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count);
442 } 484 }
(...skipping 28 matching lines...) Expand all
471 allow_latency_experiment_.insert(url.host()); 513 allow_latency_experiment_.insert(url.host());
472 return; 514 return;
473 } 515 }
474 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); 516 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host());
475 if (allow_latency_experiment_.end() == it) 517 if (allow_latency_experiment_.end() == it)
476 return; // It was already erased, or never allowed. 518 return; // It was already erased, or never allowed.
477 SdchErrorRecovery(LATENCY_TEST_DISALLOWED); 519 SdchErrorRecovery(LATENCY_TEST_DISALLOWED);
478 allow_latency_experiment_.erase(it); 520 allow_latency_experiment_.erase(it);
479 } 521 }
480 522
481 void SdchManager::AddSdchDictionary(const std::string& dictionary_text, 523 void SdchManager::AddSdchDictionary(const std::string& dictionary_text,
Ryan Sleevi 2014/09/22 20:55:26 style nits: Perhaps for a future CL, but this shou
482 const GURL& dictionary_url) { 524 const GURL& dictionary_url_const) {
Ryan Sleevi 2014/09/22 20:55:26 pedantry naming: dictionary_url here, and normaliz
Randy Smith (Not in Mondays) 2014/09/23 19:52:58 Done.
483 DCHECK(CalledOnValidThread()); 525 DCHECK(CalledOnValidThread());
484 std::string client_hash; 526 std::string client_hash;
485 std::string server_hash; 527 std::string server_hash;
486 GenerateHash(dictionary_text, &client_hash, &server_hash); 528 GenerateHash(dictionary_text, &client_hash, &server_hash);
487 if (dictionaries_.find(server_hash) != dictionaries_.end()) { 529 if (dictionaries_.find(server_hash) != dictionaries_.end()) {
488 SdchErrorRecovery(DICTIONARY_ALREADY_LOADED); 530 SdchErrorRecovery(DICTIONARY_ALREADY_LOADED);
489 return; // Already loaded. 531 return; // Already loaded.
490 } 532 }
491 533
492 std::string domain, path; 534 std::string domain, path;
493 std::set<int> ports; 535 std::set<int> ports;
494 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); 536 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30));
495 537
496 if (dictionary_text.empty()) { 538 if (dictionary_text.empty()) {
497 SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT); 539 SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT);
498 return; // Missing header. 540 return; // Missing header.
499 } 541 }
500 542
501 size_t header_end = dictionary_text.find("\n\n"); 543 size_t header_end = dictionary_text.find("\n\n");
502 if (std::string::npos == header_end) { 544 if (std::string::npos == header_end) {
503 SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER); 545 SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER);
504 return; // Missing header. 546 return; // Missing header.
505 } 547 }
506 size_t line_start = 0; // Start of line being parsed. 548 size_t line_start = 0; // Start of line being parsed.
507 while (1) { 549 while (1) {
508 size_t line_end = dictionary_text.find('\n', line_start); 550 size_t line_end = dictionary_text.find('\n', line_start);
509 DCHECK(std::string::npos != line_end); 551 DCHECK(std::string::npos != line_end);
510 DCHECK_LE(line_end, header_end); 552 DCHECK_LE(line_end, header_end);
Ryan Sleevi 2014/09/22 20:55:26 This is really unnerving. Shouldn't these be non-C
511 553
512 size_t colon_index = dictionary_text.find(':', line_start); 554 size_t colon_index = dictionary_text.find(':', line_start);
513 if (std::string::npos == colon_index) { 555 if (std::string::npos == colon_index) {
514 SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON); 556 SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON);
515 return; // Illegal line missing a colon. 557 return; // Illegal line missing a colon.
516 } 558 }
517 559
518 if (colon_index > line_end) 560 if (colon_index > line_end)
Ryan Sleevi 2014/09/22 20:55:26 if line_end == npos, this will always be false.
519 break; 561 break;
520 562
521 size_t value_start = dictionary_text.find_first_not_of(" \t", 563 size_t value_start = dictionary_text.find_first_not_of(" \t",
522 colon_index + 1); 564 colon_index + 1);
523 if (std::string::npos != value_start) { 565 if (std::string::npos != value_start) {
524 if (value_start >= line_end) 566 if (value_start >= line_end)
525 break; 567 break;
526 std::string name(dictionary_text, line_start, colon_index - line_start); 568 std::string name(dictionary_text, line_start, colon_index - line_start);
527 std::string value(dictionary_text, value_start, line_end - value_start); 569 std::string value(dictionary_text, value_start, line_end - value_start);
528 name = base::StringToLowerASCII(name); 570 name = base::StringToLowerASCII(name);
Ryan Sleevi 2014/09/22 20:55:26 Such wasteful copying :'( name == could be LowerC
529 if (name == "domain") { 571 if (name == "domain") {
530 domain = value; 572 domain = value;
531 } else if (name == "path") { 573 } else if (name == "path") {
532 path = value; 574 path = value;
533 } else if (name == "format-version") { 575 } else if (name == "format-version") {
534 if (value != "1.0") 576 if (value != "1.0")
535 return; 577 return;
536 } else if (name == "max-age") { 578 } else if (name == "max-age") {
537 int64 seconds; 579 int64 seconds;
538 base::StringToInt64(value, &seconds); 580 base::StringToInt64(value, &seconds);
Ryan Sleevi 2014/09/22 20:55:26 Unchecked return value? Killing me smalls, KILLING
539 expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds); 581 expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds);
540 } else if (name == "port") { 582 } else if (name == "port") {
541 int port; 583 int port;
542 base::StringToInt(value, &port); 584 base::StringToInt(value, &port);
Ryan Sleevi 2014/09/22 20:55:26 This too!
543 if (port >= 0) 585 if (port >= 0)
544 ports.insert(port); 586 ports.insert(port);
545 } 587 }
546 } 588 }
547 589
548 if (line_end >= header_end) 590 if (line_end >= header_end)
549 break; 591 break;
550 line_start = line_end + 1; 592 line_start = line_end + 1;
Ryan Sleevi 2014/09/22 20:55:27 if line_end == npos, this will wrap around to 0 (s
551 } 593 }
552 594
595 // Forbid dictionary with trailing dots; the SDCH spec is explicitly
596 // patterned after an old cookie spec (RFC 2695) which is silent on
597 // the issue of trailing dots, and the newer cookie spec (RFC 6265)
598 // explicitly forbids them.
599 if (domain.size() != 0 && domain[domain.size() - 1] == '.') {
600 SdchErrorRecovery(DICTIONARY_DOMAIN_HAS_TRAILING_PERIOD);
601 return;
Ryan Sleevi 2014/09/22 20:55:27 From your reply, it _seems_ that this is the ONLY
Randy Smith (Not in Mondays) 2014/09/22 21:09:56 I don't believe so; I think the stripping of the d
Ryan Sleevi 2014/09/22 22:49:42 I tried to work through why this is. That is, I'm
602 }
603
604 // Strip trailing dot from the dictionary url before comparison.
605 GURL dictionary_url(dictionary_url_const);
606 StripTrailingDotURL(&dictionary_url);
Randy Smith (Not in Mondays) 2014/09/23 17:36:43 >> I believe this is the only change required to s
607
553 if (!IsInSupportedDomain(dictionary_url)) 608 if (!IsInSupportedDomain(dictionary_url))
554 return; 609 return;
555 610
556 if (!Dictionary::CanSet(domain, path, ports, dictionary_url)) 611 if (!Dictionary::CanSet(domain, path, ports, dictionary_url))
557 return; 612 return;
558 613
559 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of 614 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of
560 // useless dictionaries. We should probably have a cache eviction plan, 615 // useless dictionaries. We should probably have a cache eviction plan,
561 // instead of just blocking additions. For now, with the spec in flux, it 616 // instead of just blocking additions. For now, with the spec in flux, it
562 // is probably not worth doing eviction handling. 617 // is probably not worth doing eviction handling.
(...skipping 20 matching lines...) Expand all
583 void SdchManager::UrlSafeBase64Encode(const std::string& input, 638 void SdchManager::UrlSafeBase64Encode(const std::string& input,
584 std::string* output) { 639 std::string* output) {
585 // Since this is only done during a dictionary load, and hashes are only 8 640 // Since this is only done during a dictionary load, and hashes are only 8
586 // characters, we just do the simple fixup, rather than rewriting the encoder. 641 // characters, we just do the simple fixup, rather than rewriting the encoder.
587 base::Base64Encode(input, output); 642 base::Base64Encode(input, output);
588 std::replace(output->begin(), output->end(), '+', '-'); 643 std::replace(output->begin(), output->end(), '+', '-');
589 std::replace(output->begin(), output->end(), '/', '_'); 644 std::replace(output->begin(), output->end(), '/', '_');
590 } 645 }
591 646
592 } // namespace net 647 } // namespace net
OLDNEW
« no previous file with comments | « net/base/sdch_manager.h ('k') | net/base/sdch_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698