OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |