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

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

Issue 6869043: Add command-line control of the HSTS preload list. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/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"
14 #include "base/string_tokenizer.h" 15 #include "base/string_tokenizer.h"
16 #include "base/string_split.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"
21 23
22 namespace net { 24 namespace net {
23 25
24 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year 26 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year
25 27
26 TransportSecurityState::TransportSecurityState() 28 TransportSecurityState::TransportSecurityState()
27 : delegate_(NULL) { 29 : delegate_(NULL) {
28 } 30 }
29 31
30 void TransportSecurityState::EnableHost(const std::string& host, 32 void TransportSecurityState::EnableHost(const std::string& host,
31 const DomainState& state) { 33 const DomainState& state) {
32 const std::string canonicalized_host = CanonicalizeHost(host); 34 const std::string canonicalized_host = CanonicalizeHost(host);
33 if (canonicalized_host.empty()) 35 if (canonicalized_host.empty())
34 return; 36 return;
35 37
36 // TODO(cevans) -- we likely want to permit a host to override a built-in, 38 // 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 39 // for at least the case where the override is stricter (i.e. includes
38 // subdomains, or includes certificate pinning). 40 // subdomains, or includes certificate pinning).
39 bool temp; 41 DomainState temp;
40 if (IsPreloadedSTS(canonicalized_host, true, &temp)) 42 if (IsPreloadedSTS(canonicalized_host, true, &temp))
41 return; 43 return;
42 44
43 char hashed[crypto::SHA256_LENGTH]; 45 char hashed[crypto::SHA256_LENGTH];
44 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); 46 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed));
45 47
46 // Use the original creation date if we already have this host. 48 // Use the original creation date if we already have this host.
47 DomainState state_copy(state); 49 DomainState state_copy(state);
48 DomainState existing_state; 50 DomainState existing_state;
49 if (IsEnabledForHost(&existing_state, host, true)) 51 if (IsEnabledForHost(&existing_state, host, true))
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
83 85
84 bool TransportSecurityState::IsEnabledForHost(DomainState* result, 86 bool TransportSecurityState::IsEnabledForHost(DomainState* result,
85 const std::string& host, 87 const std::string& host,
86 bool sni_available) { 88 bool sni_available) {
87 *result = DomainState(); 89 *result = DomainState();
88 90
89 const std::string canonicalized_host = CanonicalizeHost(host); 91 const std::string canonicalized_host = CanonicalizeHost(host);
90 if (canonicalized_host.empty()) 92 if (canonicalized_host.empty())
91 return false; 93 return false;
92 94
93 bool include_subdomains; 95 if (IsPreloadedSTS(canonicalized_host, sni_available, result)) {
94 if (IsPreloadedSTS(canonicalized_host, sni_available, &include_subdomains)) { 96 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 } 97 }
101 98
102 result->preloaded = false; 99 result->preloaded = false;
103 base::Time current_time(base::Time::Now()); 100 base::Time current_time(base::Time::Now());
104 101
105 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { 102 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
106 char hashed_domain[crypto::SHA256_LENGTH]; 103 char hashed_domain[crypto::SHA256_LENGTH];
107 104
108 crypto::SHA256HashString(IncludeNUL(&canonicalized_host[i]), &hashed_domain, 105 crypto::SHA256HashString(IncludeNUL(&canonicalized_host[i]), &hashed_domain,
109 sizeof(hashed_domain)); 106 sizeof(hashed_domain));
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 } 326 }
330 state->Set("public_key_hashes", pins); 327 state->Set("public_key_hashes", pins);
331 328
332 toplevel.Set(HashedDomainToExternalString(i->first), state); 329 toplevel.Set(HashedDomainToExternalString(i->first), state);
333 } 330 }
334 331
335 base::JSONWriter::Write(&toplevel, true /* pretty print */, output); 332 base::JSONWriter::Write(&toplevel, true /* pretty print */, output);
336 return true; 333 return true;
337 } 334 }
338 335
336 static void AddHash(const std::string& hash,
337 std::vector<SHA1Fingerprint>* out) {
338 std::string hash_str;
339 SHA1Fingerprint raw_hash;
340 if (hash.find("sha1/") == 0 &&
341 base::Base64Decode(hash.substr(5, hash.size() - 5), &hash_str) &&
abarth-chromium 2011/04/17 05:39:23 It would be nicer if 5 wasn't a magic number here.
342 hash_str.size() == base::SHA1_LENGTH) {
343 memcpy(raw_hash.data, hash_str.data(), sizeof(raw_hash.data));
344 out->push_back(raw_hash);
345 }
346 }
347
339 bool TransportSecurityState::Deserialise(const std::string& input, 348 bool TransportSecurityState::Deserialise(const std::string& input,
340 bool* dirty) { 349 bool* dirty) {
341 enabled_hosts_.clear(); 350 enabled_hosts_.clear();
342 351
343 scoped_ptr<Value> value( 352 scoped_ptr<Value> value(
344 base::JSONReader::Read(input, false /* do not allow trailing commas */)); 353 base::JSONReader::Read(input, false /* do not allow trailing commas */));
345 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) 354 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY))
346 return false; 355 return false;
347 356
348 DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get()); 357 DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get());
(...skipping 16 matching lines...) Expand all
365 !state->GetDouble("expiry", &expiry)) { 374 !state->GetDouble("expiry", &expiry)) {
366 continue; 375 continue;
367 } 376 }
368 377
369 ListValue* pins_list = NULL; 378 ListValue* pins_list = NULL;
370 std::vector<SHA1Fingerprint> public_key_hashes; 379 std::vector<SHA1Fingerprint> public_key_hashes;
371 if (state->GetList("public_key_hashes", &pins_list)) { 380 if (state->GetList("public_key_hashes", &pins_list)) {
372 size_t num_pins = pins_list->GetSize(); 381 size_t num_pins = pins_list->GetSize();
373 for (size_t i = 0; i < num_pins; ++i) { 382 for (size_t i = 0; i < num_pins; ++i) {
374 std::string type_and_base64; 383 std::string type_and_base64;
375 std::string hash_str; 384 if (pins_list->GetString(i, &type_and_base64))
376 SHA1Fingerprint hash; 385 AddHash(type_and_base64, &public_key_hashes);
377 if (pins_list->GetString(i, &type_and_base64) &&
378 type_and_base64.find("sha1/") == 0 &&
379 base::Base64Decode(
380 type_and_base64.substr(5, type_and_base64.size() - 5),
381 &hash_str) &&
382 hash_str.size() == base::SHA1_LENGTH) {
383 memcpy(hash.data, hash_str.data(), sizeof(hash.data));
384 public_key_hashes.push_back(hash);
385 }
386 } 386 }
387 } 387 }
388 388
389 DomainState::Mode mode; 389 DomainState::Mode mode;
390 if (mode_string == "strict") { 390 if (mode_string == "strict") {
391 mode = DomainState::MODE_STRICT; 391 mode = DomainState::MODE_STRICT;
392 } else if (mode_string == "opportunistic") { 392 } else if (mode_string == "opportunistic") {
393 mode = DomainState::MODE_OPPORTUNISTIC; 393 mode = DomainState::MODE_OPPORTUNISTIC;
394 } else if (mode_string == "spdy-only") { 394 } else if (mode_string == "spdy-only") {
395 mode = DomainState::MODE_SPDY_ONLY; 395 mode = DomainState::MODE_SPDY_ONLY;
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
478 478
479 return new_host; 479 return new_host;
480 } 480 }
481 481
482 // IsPreloadedSTS returns true if the canonicalized hostname should always be 482 // IsPreloadedSTS returns true if the canonicalized hostname should always be
483 // considered to have STS enabled. 483 // considered to have STS enabled.
484 // static 484 // static
485 bool TransportSecurityState::IsPreloadedSTS( 485 bool TransportSecurityState::IsPreloadedSTS(
486 const std::string& canonicalized_host, 486 const std::string& canonicalized_host,
487 bool sni_available, 487 bool sni_available,
488 bool *include_subdomains) { 488 DomainState* out) {
489 out->preloaded = true;
490 out->mode = DomainState::MODE_STRICT;
491 out->created = out->expiry = base::Time::FromTimeT(0);
abarth-chromium 2011/04/17 05:39:23 I'm not sure this is allowed by the style guide.
492 out->include_subdomains = false;
493
494 std::string cmd_line_hsts =
495 CommandLine::ForCurrentProcess()->GetSwitchValueASCII("hsts-hosts");
abarth-chromium 2011/04/17 05:39:23 Command line constants should be in a switches.cc
496 if (!cmd_line_hsts.empty()) {
497 std::vector<std::string> hosts;
498 base::SplitString(cmd_line_hsts, ',', &hosts);
499 for (std::vector<std::string>::const_iterator i = hosts.begin();
500 i != hosts.end();
501 ++i) {
abarth-chromium 2011/04/17 05:39:23 I would merge these last two lines.
502 std::string host_spec = *i;
503 std::vector<std::string> parts;
504 base::SplitString(host_spec, ':', &parts);
505 if (parts.size() != 4)
506 continue;
507 std::string host = CanonicalizeHost(parts[0]);
508 if (host != canonicalized_host)
509 continue;
510 if (parts[1] == "none")
511 out->mode = DomainState::MODE_NONE;
512 else if (parts[1] != "strict")
513 continue;
514 if (parts[2] == "true")
abarth-chromium 2011/04/17 05:39:23 True? Why not "includeSubdomains" ?
515 out->include_subdomains = true;
516 if (!parts[3].empty()) {
517 std::vector<SHA1Fingerprint> fingerprints;
518 std::vector<std::string> str_fingerprints;
519 base::SplitString(parts[3], '|', &str_fingerprints);
abarth-chromium 2011/04/17 05:39:23 This is getting complicated.
520 std::vector<std::string>::const_iterator j;
521 for (j = str_fingerprints.begin(); j != str_fingerprints.end(); ++j) {
522 AddHash(*j, &fingerprints);
523 }
524 out->public_key_hashes = fingerprints;
525 }
526 return true;
527 }
528 }
529
489 // In the medium term this list is likely to just be hardcoded here. This, 530 // 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. 531 // slightly odd, form removes the need for additional relocations records.
491 static const struct { 532 static const struct {
492 uint8 length; 533 uint8 length;
493 bool include_subdomains; 534 bool include_subdomains;
494 char dns_name[30]; 535 char dns_name[30];
495 } kPreloadedSTS[] = { 536 } kPreloadedSTS[] = {
496 {16, false, "\003www\006paypal\003com"}, 537 {16, false, "\003www\006paypal\003com"},
497 {16, false, "\003www\006elanex\003biz"}, 538 {16, false, "\003www\006elanex\003biz"},
498 {12, true, "\006jottit\003com"}, 539 {12, true, "\006jottit\003com"},
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
546 }; 587 };
547 static const size_t kNumPreloadedSNISTS = ARRAYSIZE_UNSAFE(kPreloadedSNISTS); 588 static const size_t kNumPreloadedSNISTS = ARRAYSIZE_UNSAFE(kPreloadedSNISTS);
548 589
549 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { 590 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
550 for (size_t j = 0; j < kNumPreloadedSTS; j++) { 591 for (size_t j = 0; j < kNumPreloadedSTS; j++) {
551 if (kPreloadedSTS[j].length == canonicalized_host.size() - i && 592 if (kPreloadedSTS[j].length == canonicalized_host.size() - i &&
552 memcmp(kPreloadedSTS[j].dns_name, &canonicalized_host[i], 593 memcmp(kPreloadedSTS[j].dns_name, &canonicalized_host[i],
553 kPreloadedSTS[j].length) == 0) { 594 kPreloadedSTS[j].length) == 0) {
554 if (!kPreloadedSTS[j].include_subdomains && i != 0) 595 if (!kPreloadedSTS[j].include_subdomains && i != 0)
555 return false; 596 return false;
556 *include_subdomains = kPreloadedSTS[j].include_subdomains; 597 out->include_subdomains = kPreloadedSTS[j].include_subdomains;
557 return true; 598 return true;
558 } 599 }
559 } 600 }
560 if (sni_available) { 601 if (sni_available) {
561 for (size_t j = 0; j < kNumPreloadedSNISTS; j++) { 602 for (size_t j = 0; j < kNumPreloadedSNISTS; j++) {
562 if (kPreloadedSNISTS[j].length == canonicalized_host.size() - i && 603 if (kPreloadedSNISTS[j].length == canonicalized_host.size() - i &&
563 memcmp(kPreloadedSNISTS[j].dns_name, &canonicalized_host[i], 604 memcmp(kPreloadedSNISTS[j].dns_name, &canonicalized_host[i],
564 kPreloadedSNISTS[j].length) == 0) { 605 kPreloadedSNISTS[j].length) == 0) {
565 if (!kPreloadedSNISTS[j].include_subdomains && i != 0) 606 if (!kPreloadedSNISTS[j].include_subdomains && i != 0)
566 return false; 607 return false;
567 *include_subdomains = kPreloadedSNISTS[j].include_subdomains; 608 out->include_subdomains = kPreloadedSNISTS[j].include_subdomains;
568 return true; 609 return true;
569 } 610 }
570 } 611 }
571 } 612 }
572 } 613 }
573 614
574 return false; 615 return false;
575 } 616 }
576 617
577 static std::string HashesToBase64String( 618 static std::string HashesToBase64String(
(...skipping 28 matching lines...) Expand all
606 647
607 for (std::vector<net::SHA1Fingerprint>::const_iterator 648 for (std::vector<net::SHA1Fingerprint>::const_iterator
608 i = hashes.begin(); i != hashes.end(); ++i) { 649 i = hashes.begin(); i != hashes.end(); ++i) {
609 for (std::vector<net::SHA1Fingerprint>::const_iterator 650 for (std::vector<net::SHA1Fingerprint>::const_iterator
610 j = public_key_hashes.begin(); j != public_key_hashes.end(); ++j) { 651 j = public_key_hashes.begin(); j != public_key_hashes.end(); ++j) {
611 if (i->Equals(*j)) 652 if (i->Equals(*j))
612 return true; 653 return true;
613 } 654 }
614 } 655 }
615 656
616
617 LOG(ERROR) << "Rejecting public key chain for domain " << domain 657 LOG(ERROR) << "Rejecting public key chain for domain " << domain
618 << ". Validated chain: " << HashesToBase64String(hashes) 658 << ". Validated chain: " << HashesToBase64String(hashes)
619 << ", expected: " << HashesToBase64String(public_key_hashes); 659 << ", expected: " << HashesToBase64String(public_key_hashes);
620 660
621 return false; 661 return false;
622 } 662 }
623 663
624 } // namespace 664 } // 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