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

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