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

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

Issue 6894026: Add support for built-in certificate pins, and add a pin list suitable for (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 | « no previous file | 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/command_line.h"
9 #include "base/json/json_reader.h" 9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h" 10 #include "base/json/json_writer.h"
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after
329 base::JSONWriter::Write(&toplevel, true /* pretty print */, output); 329 base::JSONWriter::Write(&toplevel, true /* pretty print */, output);
330 return true; 330 return true;
331 } 331 }
332 332
333 bool TransportSecurityState::LoadEntries(const std::string& input, 333 bool TransportSecurityState::LoadEntries(const std::string& input,
334 bool* dirty) { 334 bool* dirty) {
335 enabled_hosts_.clear(); 335 enabled_hosts_.clear();
336 return Deserialise(input, dirty, &enabled_hosts_); 336 return Deserialise(input, dirty, &enabled_hosts_);
337 } 337 }
338 338
339 static bool AddHash(const std::string& type_and_base64,
340 std::vector<SHA1Fingerprint>* out) {
341 std::string hash_str;
342 if (type_and_base64.find("sha1/") == 0 &&
343 base::Base64Decode(type_and_base64.substr(5, type_and_base64.size() - 5),
344 &hash_str) &&
345 hash_str.size() == base::SHA1_LENGTH) {
346 SHA1Fingerprint hash;
347 memcpy(hash.data, hash_str.data(), sizeof(hash.data));
348 out->push_back(hash);
349 return true;
350 }
351 return false;
352 }
353
339 // static 354 // static
340 bool TransportSecurityState::Deserialise( 355 bool TransportSecurityState::Deserialise(
341 const std::string& input, 356 const std::string& input,
342 bool* dirty, 357 bool* dirty,
343 std::map<std::string, DomainState>* out) { 358 std::map<std::string, DomainState>* out) {
344 scoped_ptr<Value> value( 359 scoped_ptr<Value> value(
345 base::JSONReader::Read(input, false /* do not allow trailing commas */)); 360 base::JSONReader::Read(input, false /* do not allow trailing commas */));
346 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) 361 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY))
347 return false; 362 return false;
348 363
(...skipping 17 matching lines...) Expand all
366 !state->GetDouble("expiry", &expiry)) { 381 !state->GetDouble("expiry", &expiry)) {
367 continue; 382 continue;
368 } 383 }
369 384
370 ListValue* pins_list = NULL; 385 ListValue* pins_list = NULL;
371 std::vector<SHA1Fingerprint> public_key_hashes; 386 std::vector<SHA1Fingerprint> public_key_hashes;
372 if (state->GetList("public_key_hashes", &pins_list)) { 387 if (state->GetList("public_key_hashes", &pins_list)) {
373 size_t num_pins = pins_list->GetSize(); 388 size_t num_pins = pins_list->GetSize();
374 for (size_t i = 0; i < num_pins; ++i) { 389 for (size_t i = 0; i < num_pins; ++i) {
375 std::string type_and_base64; 390 std::string type_and_base64;
376 std::string hash_str; 391 if (pins_list->GetString(i, &type_and_base64))
377 SHA1Fingerprint hash; 392 AddHash(type_and_base64, &public_key_hashes);
378 if (pins_list->GetString(i, &type_and_base64) &&
379 type_and_base64.find("sha1/") == 0 &&
380 base::Base64Decode(
381 type_and_base64.substr(5, type_and_base64.size() - 5),
382 &hash_str) &&
383 hash_str.size() == base::SHA1_LENGTH) {
384 memcpy(hash.data, hash_str.data(), sizeof(hash.data));
385 public_key_hashes.push_back(hash);
386 }
387 } 393 }
388 } 394 }
389 395
390 DomainState::Mode mode; 396 DomainState::Mode mode;
391 if (mode_string == "strict") { 397 if (mode_string == "strict") {
392 mode = DomainState::MODE_STRICT; 398 mode = DomainState::MODE_STRICT;
393 } else if (mode_string == "opportunistic") { 399 } else if (mode_string == "opportunistic") {
394 mode = DomainState::MODE_OPPORTUNISTIC; 400 mode = DomainState::MODE_OPPORTUNISTIC;
395 } else if (mode_string == "spdy-only") { 401 } else if (mode_string == "spdy-only") {
396 mode = DomainState::MODE_SPDY_ONLY; 402 mode = DomainState::MODE_SPDY_ONLY;
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
475 // step 3(b) 481 // step 3(b)
476 if (new_host[i + 1] == '-' || 482 if (new_host[i + 1] == '-' ||
477 new_host[i + label_length] == '-') { 483 new_host[i + label_length] == '-') {
478 return std::string(); 484 return std::string();
479 } 485 }
480 } 486 }
481 487
482 return new_host; 488 return new_host;
483 } 489 }
484 490
491 struct HSTSPreload {
492 uint8 length;
493 bool include_subdomains;
494 char dns_name[30];
495 bool https_required;
496 const char** required_hashes;
497 };
498
499 static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries,
500 const std::string& canonicalized_host, size_t i,
501 TransportSecurityState::DomainState* out, bool* ret) {
502 for (size_t j = 0; j < num_entries; j++) {
503 if (entries[j].length == canonicalized_host.size() - i &&
504 memcmp(entries[j].dns_name, &canonicalized_host[i],
505 entries[j].length) == 0) {
506 if (!entries[j].include_subdomains && i != 0) {
507 *ret = false;
508 } else {
509 out->include_subdomains = entries[j].include_subdomains;
510 *ret = true;
511 if (!entries[j].https_required)
512 out->mode = TransportSecurityState::DomainState::MODE_NONE;
513 if (entries[j].required_hashes) {
514 const char** hash = entries[j].required_hashes;
515 while (*hash) {
516 bool ok = AddHash(*hash, &out->public_key_hashes);
517 DCHECK(ok);
518 hash++;
519 }
520 }
521 }
522 return true;
523 }
524 }
525 return false;
526 }
527
485 // IsPreloadedSTS returns true if the canonicalized hostname should always be 528 // IsPreloadedSTS returns true if the canonicalized hostname should always be
486 // considered to have STS enabled. 529 // considered to have STS enabled.
487 // static 530 // static
488 bool TransportSecurityState::IsPreloadedSTS( 531 bool TransportSecurityState::IsPreloadedSTS(
489 const std::string& canonicalized_host, 532 const std::string& canonicalized_host,
490 bool sni_available, 533 bool sni_available,
491 DomainState* out) { 534 DomainState* out) {
492 out->preloaded = true; 535 out->preloaded = true;
493 out->mode = DomainState::MODE_STRICT; 536 out->mode = DomainState::MODE_STRICT;
494 out->created = base::Time::FromTimeT(0); 537 out->created = base::Time::FromTimeT(0);
495 out->expiry = out->created; 538 out->expiry = out->created;
496 out->include_subdomains = false; 539 out->include_subdomains = false;
497 540
498 std::map<std::string, DomainState> hosts; 541 std::map<std::string, DomainState> hosts;
499 std::string cmd_line_hsts = 542 std::string cmd_line_hsts =
500 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 543 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
501 switches::kHstsHosts); 544 switches::kHstsHosts);
502 if (!cmd_line_hsts.empty()) { 545 if (!cmd_line_hsts.empty()) {
503 bool dirty; 546 bool dirty;
504 Deserialise(cmd_line_hsts, &dirty, &hosts); 547 Deserialise(cmd_line_hsts, &dirty, &hosts);
505 } 548 }
506 549
550 // These hashes are base64 encodings of SHA1 hashes for cert public keys.
551 static const char* kCertPKHashVerisignClass3 =
agl 2011/04/25 19:39:46 I'm conflicted about parsing these strings all the
agl 2011/04/25 19:39:46 Need to change all of these from "const char*" to
552 "sha1/4n972HfV354KP560yw4uqe/baXc=";
553 static const char* kCertPKHashVerisignClass3G3 =
554 "sha1/IvGeLsbqzPxdI0b0wuj2xVTdXgc=";
555 static const char* kCertPKHashGoogle1024 =
556 "sha1/QMVAHW+MuvCLAO3vse6H0AWzuc0=";
557 static const char* kCertPKHashGoogle2048 =
558 "sha1/AbkhxY0L343gKf+cki7NVWp+ozk=";
559 static const char* kCertPKHashEquifaxSecureCA =
560 "sha1/SOZo+SvSspXXR9gjIBBPM5iQn9Q=";
561 static const char* kCertPKHashGeoTrustGlobalCA =
562 "sha1/wHqYaI2J+6sFZAwRfap9ZbjKzE4=";
563 static const char* kGoogleAcceptableCerts[] = {
564 kCertPKHashVerisignClass3,
565 kCertPKHashVerisignClass3G3,
566 kCertPKHashGoogle1024,
567 kCertPKHashGoogle2048,
568 kCertPKHashEquifaxSecureCA,
569 kCertPKHashGeoTrustGlobalCA,
570 0,
571 };
572
507 // In the medium term this list is likely to just be hardcoded here. This, 573 // In the medium term this list is likely to just be hardcoded here. This,
508 // slightly odd, form removes the need for additional relocations records. 574 // slightly odd, form removes the need for additional relocations records.
509 static const struct { 575 static const struct HSTSPreload kPreloadedSTS[] = {
510 uint8 length; 576 {16, false, "\003www\006paypal\003com", true, 0 },
511 bool include_subdomains; 577 {16, false, "\003www\006elanex\003biz", true, 0 },
512 char dns_name[30]; 578 {12, true, "\006jottit\003com", true, 0 },
513 } kPreloadedSTS[] = { 579 {19, true, "\015sunshinepress\003org", true, 0 },
514 {16, false, "\003www\006paypal\003com"}, 580 {21, false, "\003www\013noisebridge\003net", true, 0 },
515 {16, false, "\003www\006elanex\003biz"}, 581 {10, false, "\004neg9\003org", true, 0 },
516 {12, true, "\006jottit\003com"}, 582 {12, true, "\006riseup\003net", true, 0 },
517 {19, true, "\015sunshinepress\003org"}, 583 {11, false, "\006factor\002cc", true, 0 },
518 {21, false, "\003www\013noisebridge\003net"}, 584 {22, false, "\007members\010mayfirst\003org", true, 0 },
519 {10, false, "\004neg9\003org"}, 585 {22, false, "\007support\010mayfirst\003org", true, 0 },
520 {12, true, "\006riseup\003net"}, 586 {17, false, "\002id\010mayfirst\003org", true, 0 },
521 {11, false, "\006factor\002cc"}, 587 {20, false, "\005lists\010mayfirst\003org", true, 0 },
522 {22, false, "\007members\010mayfirst\003org"}, 588 {19, true, "\015splendidbacon\003com", true, 0 },
523 {22, false, "\007support\010mayfirst\003org"}, 589 {19, true, "\006health\006google\003com", true, 0 },
524 {17, false, "\002id\010mayfirst\003org"}, 590 {21, true, "\010checkout\006google\003com", true, 0 },
525 {20, false, "\005lists\010mayfirst\003org"}, 591 {19, true, "\006chrome\006google\003com", true, kGoogleAcceptableCerts },
526 {19, true, "\015splendidbacon\003com"}, 592 {26, false, "\006latest\006chrome\006google\003com", true, 0 },
527 {19, true, "\006health\006google\003com"}, 593 {28, false, "\016aladdinschools\007appspot\003com", true, 0 },
528 {21, true, "\010checkout\006google\003com"}, 594 {14, true, "\011ottospora\002nl", true, 0 },
529 {19, true, "\006chrome\006google\003com"}, 595 {17, true, "\004docs\006google\003com", true, 0 },
530 {26, false, "\006latest\006chrome\006google\003com"}, 596 {18, true, "\005sites\006google\003com", true, 0 },
531 {28, false, "\016aladdinschools\007appspot\003com"}, 597 {25, true, "\014spreadsheets\006google\003com", true, 0 },
532 {14, true, "\011ottospora\002nl"}, 598 {22, false, "\011appengine\006google\003com", true, 0 },
533 {17, true, "\004docs\006google\003com"}, 599 {25, false, "\003www\017paycheckrecords\003com", true, 0 },
534 {18, true, "\005sites\006google\003com"}, 600 {20, true, "\006market\007android\003com", true, 0 },
535 {25, true, "\014spreadsheets\006google\003com"}, 601 {14, false, "\010lastpass\003com", true, 0 },
536 {22, false, "\011appengine\006google\003com"}, 602 {18, false, "\003www\010lastpass\003com", true, 0 },
537 {25, false, "\003www\017paycheckrecords\003com"}, 603 {14, true, "\010keyerror\003com", true, 0 },
538 {20, true, "\006market\007android\003com"}, 604 {22, true, "\011encrypted\006google\003com", true, 0 },
539 {14, false, "\010lastpass\003com"}, 605 {13, false, "\010entropia\002de", true, 0 },
540 {18, false, "\003www\010lastpass\003com"}, 606 {17, false, "\003www\010entropia\002de", true, 0 },
541 {14, true, "\010keyerror\003com"}, 607 {21, true, "\010accounts\006google\003com", true, 0 },
542 {22, true, "\011encrypted\006google\003com"},
543 {13, false, "\010entropia\002de"},
544 {17, false, "\003www\010entropia\002de"},
545 {21, true, "\010accounts\006google\003com"},
546 #if defined(OS_CHROMEOS) 608 #if defined(OS_CHROMEOS)
547 {17, true, "\004mail\006google\003com"}, 609 {17, true, "\004mail\006google\003com", true, 0 },
548 {13, false, "\007twitter\003com"}, 610 {13, false, "\007twitter\003com", true, 0 },
549 {17, false, "\003www\007twitter\003com"}, 611 {17, false, "\003www\007twitter\003com", true, 0 },
550 {17, false, "\003api\007twitter\003com"}, 612 {17, false, "\003api\007twitter\003com", true, 0 },
551 {17, false, "\003dev\007twitter\003com"}, 613 {17, false, "\003dev\007twitter\003com", true, 0},
552 {22, false, "\010business\007twitter\003com"}, 614 {22, false, "\010business\007twitter\003com", true, 0 },
553 #endif 615 #endif
554 }; 616 };
555 static const size_t kNumPreloadedSTS = ARRAYSIZE_UNSAFE(kPreloadedSTS); 617 static const size_t kNumPreloadedSTS = ARRAYSIZE_UNSAFE(kPreloadedSTS);
556 618
557 static const struct { 619 static const struct HSTSPreload kPreloadedSNISTS[] = {
558 uint8 length; 620 {11, true, "\005gmail\003com", true, 0 },
559 bool include_subdomains; 621 {16, true, "\012googlemail\003com", true, 0 },
560 char dns_name[30];
561 } kPreloadedSNISTS[] = {
562 {11, true, "\005gmail\003com"},
563 {16, true, "\012googlemail\003com"},
564 }; 622 };
565 static const size_t kNumPreloadedSNISTS = ARRAYSIZE_UNSAFE(kPreloadedSNISTS); 623 static const size_t kNumPreloadedSNISTS = ARRAYSIZE_UNSAFE(kPreloadedSNISTS);
566 624
567 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { 625 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) {
568 std::string host_sub_chunk(&canonicalized_host[i], 626 std::string host_sub_chunk(&canonicalized_host[i],
569 canonicalized_host.size() - i); 627 canonicalized_host.size() - i);
570 out->domain = DNSDomainToString(host_sub_chunk); 628 out->domain = DNSDomainToString(host_sub_chunk);
571 std::string hashed_host(HashHost(host_sub_chunk)); 629 std::string hashed_host(HashHost(host_sub_chunk));
572 if (hosts.find(hashed_host) != hosts.end()) { 630 if (hosts.find(hashed_host) != hosts.end()) {
573 *out = hosts[hashed_host]; 631 *out = hosts[hashed_host];
574 out->domain = DNSDomainToString(host_sub_chunk); 632 out->domain = DNSDomainToString(host_sub_chunk);
575 out->preloaded = true; 633 out->preloaded = true;
576 return true; 634 return true;
577 } 635 }
578 for (size_t j = 0; j < kNumPreloadedSTS; j++) { 636 bool ret;
579 if (kPreloadedSTS[j].length == canonicalized_host.size() - i && 637 if (HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out,
580 memcmp(kPreloadedSTS[j].dns_name, &canonicalized_host[i], 638 &ret))
agl 2011/04/25 19:39:46 I think you should have { } around this if body be
581 kPreloadedSTS[j].length) == 0) { 639 return ret;
582 if (!kPreloadedSTS[j].include_subdomains && i != 0) 640 if (sni_available &&
583 return false; 641 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i,
agl 2011/04/25 19:39:46 ditto.
584 out->include_subdomains = kPreloadedSTS[j].include_subdomains; 642 out, &ret))
585 return true; 643 return ret;
586 }
587 }
588 if (sni_available) {
589 for (size_t j = 0; j < kNumPreloadedSNISTS; j++) {
590 if (kPreloadedSNISTS[j].length == canonicalized_host.size() - i &&
591 memcmp(kPreloadedSNISTS[j].dns_name, &canonicalized_host[i],
592 kPreloadedSNISTS[j].length) == 0) {
593 if (!kPreloadedSNISTS[j].include_subdomains && i != 0)
594 return false;
595 out->include_subdomains = kPreloadedSNISTS[j].include_subdomains;
596 return true;
597 }
598 }
599 }
600 } 644 }
601 645
602 return false; 646 return false;
603 } 647 }
604 648
605 static std::string HashesToBase64String( 649 static std::string HashesToBase64String(
606 const std::vector<net::SHA1Fingerprint>& hashes) { 650 const std::vector<net::SHA1Fingerprint>& hashes) {
607 std::vector<std::string> hashes_strs; 651 std::vector<std::string> hashes_strs;
608 for (std::vector<net::SHA1Fingerprint>::const_iterator 652 for (std::vector<net::SHA1Fingerprint>::const_iterator
609 i = hashes.begin(); i != hashes.end(); i++) { 653 i = hashes.begin(); i != hashes.end(); i++) {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
642 } 686 }
643 687
644 LOG(ERROR) << "Rejecting public key chain for domain " << domain 688 LOG(ERROR) << "Rejecting public key chain for domain " << domain
645 << ". Validated chain: " << HashesToBase64String(hashes) 689 << ". Validated chain: " << HashesToBase64String(hashes)
646 << ", expected: " << HashesToBase64String(public_key_hashes); 690 << ", expected: " << HashesToBase64String(public_key_hashes);
647 691
648 return false; 692 return false;
649 } 693 }
650 694
651 } // namespace 695 } // namespace
OLDNEW
« no previous file with comments | « no previous file | net/base/transport_security_state_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698