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

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

Issue 6895039: Merge 82409 - Add command-line control of the HSTS preload list. (Closed) Base URL: svn://svn.chromium.org/chrome/branches/742/src/
Patch Set: Created 9 years, 8 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 | Annotate | Revision Log
« no previous file with comments | « net/base/transport_security_state.h ('k') | net/base/transport_security_state_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) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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/transport_security_state.h" 5 #include "net/base/transport_security_state.h"
6 6
7 #include "base/base64.h" 7 #include "base/base64.h"
8 #include "base/command_line.h"
8 #include "base/json/json_reader.h" 9 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h" 10 #include "base/json/json_writer.h"
10 #include "base/logging.h" 11 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
12 #include "base/sha1.h" 13 #include "base/sha1.h"
13 #include "base/string_number_conversions.h" 14 #include "base/string_number_conversions.h"
15 #include "base/string_split.h"
14 #include "base/string_tokenizer.h" 16 #include "base/string_tokenizer.h"
15 #include "base/string_util.h" 17 #include "base/string_util.h"
16 #include "base/utf_string_conversions.h" 18 #include "base/utf_string_conversions.h"
17 #include "base/values.h" 19 #include "base/values.h"
18 #include "crypto/sha2.h" 20 #include "crypto/sha2.h"
19 #include "googleurl/src/gurl.h" 21 #include "googleurl/src/gurl.h"
20 #include "net/base/dns_util.h" 22 #include "net/base/dns_util.h"
23 #include "net/base/net_switches.h"
21 24
22 namespace net { 25 namespace net {
23 26
24 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year 27 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year
25 28
26 TransportSecurityState::TransportSecurityState() 29 TransportSecurityState::TransportSecurityState()
27 : delegate_(NULL) { 30 : delegate_(NULL) {
28 } 31 }
29 32
33 static std::string HashHost(const std::string& canonicalized_host) {
34 char hashed[crypto::SHA256_LENGTH];
35 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed));
36 return std::string(hashed, sizeof(hashed));
37 }
38
30 void TransportSecurityState::EnableHost(const std::string& host, 39 void TransportSecurityState::EnableHost(const std::string& host,
31 const DomainState& state) { 40 const DomainState& state) {
32 const std::string canonicalized_host = CanonicalizeHost(host); 41 const std::string canonicalized_host = CanonicalizeHost(host);
33 if (canonicalized_host.empty()) 42 if (canonicalized_host.empty())
34 return; 43 return;
35 44
36 // TODO(cevans) -- we likely want to permit a host to override a built-in, 45 // TODO(cevans) -- we likely want to permit a host to override a built-in,
37 // for at least the case where the override is stricter (i.e. includes 46 // for at least the case where the override is stricter (i.e. includes
38 // subdomains, or includes certificate pinning). 47 // subdomains, or includes certificate pinning).
39 bool temp; 48 DomainState temp;
40 if (IsPreloadedSTS(canonicalized_host, true, &temp)) 49 if (IsPreloadedSTS(canonicalized_host, true, &temp))
41 return; 50 return;
42 51
43 char hashed[crypto::SHA256_LENGTH];
44 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed));
45
46 // Use the original creation date if we already have this host. 52 // Use the original creation date if we already have this host.
47 DomainState state_copy(state); 53 DomainState state_copy(state);
48 DomainState existing_state; 54 DomainState existing_state;
49 if (IsEnabledForHost(&existing_state, host, true)) 55 if (IsEnabledForHost(&existing_state, host, true))
50 state_copy.created = existing_state.created; 56 state_copy.created = existing_state.created;
51 57
52 // We don't store these values. 58 // We don't store these values.
53 state_copy.preloaded = false; 59 state_copy.preloaded = false;
54 state_copy.domain.clear(); 60 state_copy.domain.clear();
55 61
56 enabled_hosts_[std::string(hashed, sizeof(hashed))] = state_copy; 62 enabled_hosts_[HashHost(canonicalized_host)] = state_copy;
57 DirtyNotify(); 63 DirtyNotify();
58 } 64 }
59 65
60 bool TransportSecurityState::DeleteHost(const std::string& host) { 66 bool TransportSecurityState::DeleteHost(const std::string& host) {
61 const std::string canonicalized_host = CanonicalizeHost(host); 67 const std::string canonicalized_host = CanonicalizeHost(host);
62 if (canonicalized_host.empty()) 68 if (canonicalized_host.empty())
63 return false; 69 return false;
64 70
65 char hashed[crypto::SHA256_LENGTH];
66 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed));
67
68 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find( 71 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find(
69 std::string(hashed, sizeof(hashed))); 72 HashHost(canonicalized_host));
70 if (i != enabled_hosts_.end()) { 73 if (i != enabled_hosts_.end()) {
71 enabled_hosts_.erase(i); 74 enabled_hosts_.erase(i);
72 DirtyNotify(); 75 DirtyNotify();
73 return true; 76 return true;
74 } 77 }
75 return false; 78 return false;
76 } 79 }
77 80
78 // IncludeNUL converts a char* to a std::string and includes the terminating 81 // IncludeNUL converts a char* to a std::string and includes the terminating
79 // NUL in the result. 82 // NUL in the result.
80 static std::string IncludeNUL(const char* in) { 83 static std::string IncludeNUL(const char* in) {
81 return std::string(in, strlen(in) + 1); 84 return std::string(in, strlen(in) + 1);
82 } 85 }
83 86
84 bool TransportSecurityState::IsEnabledForHost(DomainState* result, 87 bool TransportSecurityState::IsEnabledForHost(DomainState* result,
85 const std::string& host, 88 const std::string& host,
86 bool sni_available) { 89 bool sni_available) {
87 *result = DomainState();
88
89 const std::string canonicalized_host = CanonicalizeHost(host); 90 const std::string canonicalized_host = CanonicalizeHost(host);
90 if (canonicalized_host.empty()) 91 if (canonicalized_host.empty())
91 return false; 92 return false;
92 93
93 bool include_subdomains; 94 if (IsPreloadedSTS(canonicalized_host, sni_available, result))
94 if (IsPreloadedSTS(canonicalized_host, sni_available, &include_subdomains)) { 95 return result->mode != DomainState::MODE_NONE;
95 result->created = result->expiry = base::Time::FromTimeT(0);
96 result->mode = DomainState::MODE_STRICT;
97 result->include_subdomains = include_subdomains;
98 result->preloaded = true;
99 return true;
100 }
101 96
102 result->preloaded = false; 97 *result = DomainState();
98
103 base::Time current_time(base::Time::Now()); 99 base::Time current_time(base::Time::Now());
104 100
105 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { 101 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
106 char hashed_domain[crypto::SHA256_LENGTH]; 102 std::string hashed_domain(HashHost(IncludeNUL(&canonicalized_host[i])));
107 103
108 crypto::SHA256HashString(IncludeNUL(&canonicalized_host[i]), &hashed_domain,
109 sizeof(hashed_domain));
110 std::map<std::string, DomainState>::iterator j = 104 std::map<std::string, DomainState>::iterator j =
111 enabled_hosts_.find(std::string(hashed_domain, sizeof(hashed_domain))); 105 enabled_hosts_.find(hashed_domain);
112 if (j == enabled_hosts_.end()) 106 if (j == enabled_hosts_.end())
113 continue; 107 continue;
114 108
115 if (current_time > j->second.expiry) { 109 if (current_time > j->second.expiry) {
116 enabled_hosts_.erase(j); 110 enabled_hosts_.erase(j);
117 DirtyNotify(); 111 DirtyNotify();
118 continue; 112 continue;
119 } 113 }
120 114
121 *result = j->second; 115 *result = j->second;
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 } 323 }
330 state->Set("public_key_hashes", pins); 324 state->Set("public_key_hashes", pins);
331 325
332 toplevel.Set(HashedDomainToExternalString(i->first), state); 326 toplevel.Set(HashedDomainToExternalString(i->first), state);
333 } 327 }
334 328
335 base::JSONWriter::Write(&toplevel, true /* pretty print */, output); 329 base::JSONWriter::Write(&toplevel, true /* pretty print */, output);
336 return true; 330 return true;
337 } 331 }
338 332
339 bool TransportSecurityState::Deserialise(const std::string& input, 333 bool TransportSecurityState::LoadEntries(const std::string& input,
340 bool* dirty) { 334 bool* dirty) {
341 enabled_hosts_.clear(); 335 enabled_hosts_.clear();
336 return Deserialise(input, dirty, &enabled_hosts_);
337 }
342 338
339 // static
340 bool TransportSecurityState::Deserialise(
341 const std::string& input,
342 bool* dirty,
343 std::map<std::string, DomainState>* out) {
343 scoped_ptr<Value> value( 344 scoped_ptr<Value> value(
344 base::JSONReader::Read(input, false /* do not allow trailing commas */)); 345 base::JSONReader::Read(input, false /* do not allow trailing commas */));
345 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) 346 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY))
346 return false; 347 return false;
347 348
348 DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get()); 349 DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get());
349 const base::Time current_time(base::Time::Now()); 350 const base::Time current_time(base::Time::Now());
350 bool dirtied = false; 351 bool dirtied = false;
351 352
352 for (DictionaryValue::key_iterator i = dict_value->begin_keys(); 353 for (DictionaryValue::key_iterator i = dict_value->begin_keys();
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
386 } 387 }
387 } 388 }
388 389
389 DomainState::Mode mode; 390 DomainState::Mode mode;
390 if (mode_string == "strict") { 391 if (mode_string == "strict") {
391 mode = DomainState::MODE_STRICT; 392 mode = DomainState::MODE_STRICT;
392 } else if (mode_string == "opportunistic") { 393 } else if (mode_string == "opportunistic") {
393 mode = DomainState::MODE_OPPORTUNISTIC; 394 mode = DomainState::MODE_OPPORTUNISTIC;
394 } else if (mode_string == "spdy-only") { 395 } else if (mode_string == "spdy-only") {
395 mode = DomainState::MODE_SPDY_ONLY; 396 mode = DomainState::MODE_SPDY_ONLY;
397 } else if (mode_string == "none") {
398 mode = DomainState::MODE_NONE;
396 } else { 399 } else {
397 LOG(WARNING) << "Unknown TransportSecurityState mode string found: " 400 LOG(WARNING) << "Unknown TransportSecurityState mode string found: "
398 << mode_string; 401 << mode_string;
399 continue; 402 continue;
400 } 403 }
401 404
402 base::Time expiry_time = base::Time::FromDoubleT(expiry); 405 base::Time expiry_time = base::Time::FromDoubleT(expiry);
403 base::Time created_time; 406 base::Time created_time;
404 if (state->GetDouble("created", &created)) { 407 if (state->GetDouble("created", &created)) {
405 created_time = base::Time::FromDoubleT(created); 408 created_time = base::Time::FromDoubleT(created);
(...skipping 15 matching lines...) Expand all
421 dirtied = true; 424 dirtied = true;
422 continue; 425 continue;
423 } 426 }
424 427
425 DomainState new_state; 428 DomainState new_state;
426 new_state.mode = mode; 429 new_state.mode = mode;
427 new_state.created = created_time; 430 new_state.created = created_time;
428 new_state.expiry = expiry_time; 431 new_state.expiry = expiry_time;
429 new_state.include_subdomains = include_subdomains; 432 new_state.include_subdomains = include_subdomains;
430 new_state.public_key_hashes = public_key_hashes; 433 new_state.public_key_hashes = public_key_hashes;
431 enabled_hosts_[hashed] = new_state; 434 (*out)[hashed] = new_state;
432 } 435 }
433 436
434 *dirty = dirtied; 437 *dirty = dirtied;
435 return true; 438 return true;
436 } 439 }
437 440
438 TransportSecurityState::~TransportSecurityState() { 441 TransportSecurityState::~TransportSecurityState() {
439 } 442 }
440 443
441 void TransportSecurityState::DirtyNotify() { 444 void TransportSecurityState::DirtyNotify() {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
478 481
479 return new_host; 482 return new_host;
480 } 483 }
481 484
482 // IsPreloadedSTS returns true if the canonicalized hostname should always be 485 // IsPreloadedSTS returns true if the canonicalized hostname should always be
483 // considered to have STS enabled. 486 // considered to have STS enabled.
484 // static 487 // static
485 bool TransportSecurityState::IsPreloadedSTS( 488 bool TransportSecurityState::IsPreloadedSTS(
486 const std::string& canonicalized_host, 489 const std::string& canonicalized_host,
487 bool sni_available, 490 bool sni_available,
488 bool *include_subdomains) { 491 DomainState* out) {
492 out->preloaded = true;
493 out->mode = DomainState::MODE_STRICT;
494 out->created = base::Time::FromTimeT(0);
495 out->expiry = out->created;
496 out->include_subdomains = false;
497
498 std::map<std::string, DomainState> hosts;
499 std::string cmd_line_hsts =
500 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
501 switches::kHstsHosts);
502 if (!cmd_line_hsts.empty()) {
503 bool dirty;
504 Deserialise(cmd_line_hsts, &dirty, &hosts);
505 }
506
489 // In the medium term this list is likely to just be hardcoded here. This, 507 // In the medium term this list is likely to just be hardcoded here. This,
490 // slightly odd, form removes the need for additional relocations records. 508 // slightly odd, form removes the need for additional relocations records.
491 static const struct { 509 static const struct {
492 uint8 length; 510 uint8 length;
493 bool include_subdomains; 511 bool include_subdomains;
494 char dns_name[30]; 512 char dns_name[30];
495 } kPreloadedSTS[] = { 513 } kPreloadedSTS[] = {
496 {16, false, "\003www\006paypal\003com"}, 514 {16, false, "\003www\006paypal\003com"},
497 {16, false, "\003www\006elanex\003biz"}, 515 {16, false, "\003www\006elanex\003biz"},
498 {12, true, "\006jottit\003com"}, 516 {12, true, "\006jottit\003com"},
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
540 uint8 length; 558 uint8 length;
541 bool include_subdomains; 559 bool include_subdomains;
542 char dns_name[30]; 560 char dns_name[30];
543 } kPreloadedSNISTS[] = { 561 } kPreloadedSNISTS[] = {
544 {11, true, "\005gmail\003com"}, 562 {11, true, "\005gmail\003com"},
545 {16, true, "\012googlemail\003com"}, 563 {16, true, "\012googlemail\003com"},
546 }; 564 };
547 static const size_t kNumPreloadedSNISTS = ARRAYSIZE_UNSAFE(kPreloadedSNISTS); 565 static const size_t kNumPreloadedSNISTS = ARRAYSIZE_UNSAFE(kPreloadedSNISTS);
548 566
549 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { 567 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
568 std::string host_sub_chunk(&canonicalized_host[i],
569 canonicalized_host.size() - i);
570 out->domain = DNSDomainToString(host_sub_chunk);
571 std::string hashed_host(HashHost(host_sub_chunk));
572 if (hosts.find(hashed_host) != hosts.end()) {
573 *out = hosts[hashed_host];
574 out->domain = DNSDomainToString(host_sub_chunk);
575 out->preloaded = true;
576 return true;
577 }
550 for (size_t j = 0; j < kNumPreloadedSTS; j++) { 578 for (size_t j = 0; j < kNumPreloadedSTS; j++) {
551 if (kPreloadedSTS[j].length == canonicalized_host.size() - i && 579 if (kPreloadedSTS[j].length == canonicalized_host.size() - i &&
552 memcmp(kPreloadedSTS[j].dns_name, &canonicalized_host[i], 580 memcmp(kPreloadedSTS[j].dns_name, &canonicalized_host[i],
553 kPreloadedSTS[j].length) == 0) { 581 kPreloadedSTS[j].length) == 0) {
554 if (!kPreloadedSTS[j].include_subdomains && i != 0) 582 if (!kPreloadedSTS[j].include_subdomains && i != 0)
555 return false; 583 return false;
556 *include_subdomains = kPreloadedSTS[j].include_subdomains; 584 out->include_subdomains = kPreloadedSTS[j].include_subdomains;
557 return true; 585 return true;
558 } 586 }
559 } 587 }
560 if (sni_available) { 588 if (sni_available) {
561 for (size_t j = 0; j < kNumPreloadedSNISTS; j++) { 589 for (size_t j = 0; j < kNumPreloadedSNISTS; j++) {
562 if (kPreloadedSNISTS[j].length == canonicalized_host.size() - i && 590 if (kPreloadedSNISTS[j].length == canonicalized_host.size() - i &&
563 memcmp(kPreloadedSNISTS[j].dns_name, &canonicalized_host[i], 591 memcmp(kPreloadedSNISTS[j].dns_name, &canonicalized_host[i],
564 kPreloadedSNISTS[j].length) == 0) { 592 kPreloadedSNISTS[j].length) == 0) {
565 if (!kPreloadedSNISTS[j].include_subdomains && i != 0) 593 if (!kPreloadedSNISTS[j].include_subdomains && i != 0)
566 return false; 594 return false;
567 *include_subdomains = kPreloadedSNISTS[j].include_subdomains; 595 out->include_subdomains = kPreloadedSNISTS[j].include_subdomains;
568 return true; 596 return true;
569 } 597 }
570 } 598 }
571 } 599 }
572 } 600 }
573 601
574 return false; 602 return false;
575 } 603 }
576 604
577 static std::string HashesToBase64String( 605 static std::string HashesToBase64String(
(...skipping 28 matching lines...) Expand all
606 634
607 for (std::vector<net::SHA1Fingerprint>::const_iterator 635 for (std::vector<net::SHA1Fingerprint>::const_iterator
608 i = hashes.begin(); i != hashes.end(); ++i) { 636 i = hashes.begin(); i != hashes.end(); ++i) {
609 for (std::vector<net::SHA1Fingerprint>::const_iterator 637 for (std::vector<net::SHA1Fingerprint>::const_iterator
610 j = public_key_hashes.begin(); j != public_key_hashes.end(); ++j) { 638 j = public_key_hashes.begin(); j != public_key_hashes.end(); ++j) {
611 if (i->Equals(*j)) 639 if (i->Equals(*j))
612 return true; 640 return true;
613 } 641 }
614 } 642 }
615 643
616
617 LOG(ERROR) << "Rejecting public key chain for domain " << domain 644 LOG(ERROR) << "Rejecting public key chain for domain " << domain
618 << ". Validated chain: " << HashesToBase64String(hashes) 645 << ". Validated chain: " << HashesToBase64String(hashes)
619 << ", expected: " << HashesToBase64String(public_key_hashes); 646 << ", expected: " << HashesToBase64String(public_key_hashes);
620 647
621 return false; 648 return false;
622 } 649 }
623 650
624 } // namespace 651 } // namespace
OLDNEW
« no previous file with comments | « net/base/transport_security_state.h ('k') | net/base/transport_security_state_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698