| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #if defined(USE_OPENSSL) | 7 #if defined(USE_OPENSSL) |
| 8 #include <openssl/ecdsa.h> | 8 #include <openssl/ecdsa.h> |
| 9 #include <openssl/ssl.h> | 9 #include <openssl/ssl.h> |
| 10 #else // !defined(USE_OPENSSL) | 10 #else // !defined(USE_OPENSSL) |
| 11 #include <cryptohi.h> | 11 #include <cryptohi.h> |
| 12 #include <hasht.h> | 12 #include <hasht.h> |
| 13 #include <keyhi.h> | 13 #include <keyhi.h> |
| 14 #include <pk11pub.h> | 14 #include <pk11pub.h> |
| 15 #include <nspr.h> | 15 #include <nspr.h> |
| 16 #endif | 16 #endif |
| 17 | 17 |
| 18 #include <algorithm> | 18 #include <algorithm> |
| 19 #include <utility> | |
| 20 | 19 |
| 21 #include "base/base64.h" | 20 #include "base/base64.h" |
| 22 #include "base/logging.h" | 21 #include "base/logging.h" |
| 23 #include "base/memory/scoped_ptr.h" | 22 #include "base/memory/scoped_ptr.h" |
| 24 #include "base/metrics/histogram.h" | 23 #include "base/metrics/histogram.h" |
| 25 #include "base/sha1.h" | 24 #include "base/sha1.h" |
| 26 #include "base/string_number_conversions.h" | 25 #include "base/string_number_conversions.h" |
| 27 #include "base/string_tokenizer.h" | 26 #include "base/string_tokenizer.h" |
| 28 #include "base/string_util.h" | 27 #include "base/string_util.h" |
| 29 #include "base/time.h" | 28 #include "base/time.h" |
| 30 #include "base/utf_string_conversions.h" | 29 #include "base/utf_string_conversions.h" |
| 31 #include "base/values.h" | 30 #include "base/values.h" |
| 32 #include "crypto/sha2.h" | 31 #include "crypto/sha2.h" |
| 33 #include "googleurl/src/gurl.h" | 32 #include "googleurl/src/gurl.h" |
| 34 #include "net/base/dns_util.h" | 33 #include "net/base/dns_util.h" |
| 35 #include "net/base/ssl_info.h" | 34 #include "net/base/ssl_info.h" |
| 35 #include "net/base/x509_cert_types.h" |
| 36 #include "net/base/x509_certificate.h" | 36 #include "net/base/x509_certificate.h" |
| 37 #include "net/http/http_util.h" | 37 #include "net/http/http_util.h" |
| 38 | 38 |
| 39 #if defined(USE_OPENSSL) | 39 #if defined(USE_OPENSSL) |
| 40 #include "crypto/openssl_util.h" | 40 #include "crypto/openssl_util.h" |
| 41 #endif | 41 #endif |
| 42 | 42 |
| 43 namespace net { | 43 namespace net { |
| 44 | 44 |
| 45 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year | 45 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 218 StringPair pair; | 218 StringPair pair; |
| 219 size_t point = source.find(delimiter); | 219 size_t point = source.find(delimiter); |
| 220 | 220 |
| 221 pair.first = source.substr(0, point); | 221 pair.first = source.substr(0, point); |
| 222 if (std::string::npos != point) | 222 if (std::string::npos != point) |
| 223 pair.second = source.substr(point + 1); | 223 pair.second = source.substr(point + 1); |
| 224 | 224 |
| 225 return pair; | 225 return pair; |
| 226 } | 226 } |
| 227 | 227 |
| 228 // TODO(palmer): Support both sha256 and sha1. This will require additional | |
| 229 // infrastructure code changes and can come in a later patch. | |
| 230 // | |
| 231 // static | 228 // static |
| 232 bool TransportSecurityState::ParsePin(const std::string& value, | 229 bool TransportSecurityState::ParsePin(const std::string& value, |
| 233 SHA1Fingerprint* out) { | 230 HashValue* out) { |
| 234 StringPair slash = Split(Strip(value), '/'); | 231 StringPair slash = Split(Strip(value), '/'); |
| 235 if (slash.first != "sha1") | 232 |
| 233 if (slash.first == "sha1") |
| 234 out->tag = HASH_VALUE_SHA1; |
| 235 else if (slash.first == "sha256") |
| 236 out->tag = HASH_VALUE_SHA256; |
| 237 else |
| 236 return false; | 238 return false; |
| 237 | 239 |
| 238 std::string decoded; | 240 std::string decoded; |
| 239 if (!base::Base64Decode(slash.second, &decoded) || | 241 if (!base::Base64Decode(slash.second, &decoded) || |
| 240 decoded.size() != arraysize(out->data)) { | 242 decoded.size() != out->size()) { |
| 241 return false; | 243 return false; |
| 242 } | 244 } |
| 243 | 245 |
| 244 memcpy(out->data, decoded.data(), arraysize(out->data)); | 246 memcpy(out->data(), decoded.data(), out->size()); |
| 245 return true; | 247 return true; |
| 246 } | 248 } |
| 247 | 249 |
| 248 static bool ParseAndAppendPin(const std::string& value, | 250 static bool ParseAndAppendPin(const std::string& value, |
| 249 FingerprintVector* fingerprints) { | 251 HashValueTag tag, |
| 250 // The base64'd fingerprint MUST be a quoted-string. 20 bytes base64'd is 28 | 252 HashValueVector* hashes) { |
| 251 // characters; 32 bytes base64'd is 44 characters. TODO(palmer): Support | 253 std::string unquoted = HttpUtil::Unquote(value); |
| 252 // SHA256. | 254 std::string decoded; |
| 253 size_t size = value.size(); | 255 |
| 254 if (size != 30 || value[0] != '"' || value[size - 1] != '"') | 256 // This code has to assume that 32 bytes is SHA-256 and 20 bytes is SHA-1. |
| 257 // Currently, those are the only two possibilities, so the assumption is |
| 258 // valid. |
| 259 if (!base::Base64Decode(unquoted, &decoded)) |
| 255 return false; | 260 return false; |
| 256 | 261 |
| 257 std::string unquoted = HttpUtil::Unquote(value); | 262 HashValue hash(tag); |
| 258 std::string decoded; | 263 if (decoded.size() != hash.size()) |
| 259 SHA1Fingerprint fp; | 264 return false; |
| 260 | 265 |
| 261 if (!base::Base64Decode(unquoted, &decoded) || | 266 memcpy(hash.data(), decoded.data(), hash.size()); |
| 262 decoded.size() != arraysize(fp.data)) { | 267 hashes->push_back(hash); |
| 263 return false; | |
| 264 } | |
| 265 | |
| 266 memcpy(fp.data, decoded.data(), arraysize(fp.data)); | |
| 267 fingerprints->push_back(fp); | |
| 268 return true; | 268 return true; |
| 269 } | 269 } |
| 270 | 270 |
| 271 struct FingerprintsEqualPredicate { | 271 struct HashValuesEqualPredicate { |
| 272 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : | 272 explicit HashValuesEqualPredicate(const HashValue& fingerprint) : |
| 273 fingerprint_(fingerprint) {} | 273 fingerprint_(fingerprint) {} |
| 274 | 274 |
| 275 bool operator()(const SHA1Fingerprint& other) const { | 275 bool operator()(const HashValue& other) const { |
| 276 return fingerprint_.Equals(other); | 276 return fingerprint_.Equals(other); |
| 277 } | 277 } |
| 278 | 278 |
| 279 const SHA1Fingerprint& fingerprint_; | 279 const HashValue& fingerprint_; |
| 280 }; | 280 }; |
| 281 | 281 |
| 282 // Returns true iff there is an item in |pins| which is not present in | 282 // Returns true iff there is an item in |pins| which is not present in |
| 283 // |from_cert_chain|. Such an SPKI hash is called a "backup pin". | 283 // |from_cert_chain|. Such an SPKI hash is called a "backup pin". |
| 284 static bool IsBackupPinPresent(const FingerprintVector& pins, | 284 static bool IsBackupPinPresent(const HashValueVector& pins, |
| 285 const FingerprintVector& from_cert_chain) { | 285 const HashValueVector& from_cert_chain) { |
| 286 for (FingerprintVector::const_iterator | 286 for (HashValueVector::const_iterator |
| 287 i = pins.begin(); i != pins.end(); ++i) { | 287 i = pins.begin(); i != pins.end(); ++i) { |
| 288 FingerprintVector::const_iterator j = | 288 HashValueVector::const_iterator j = |
| 289 std::find_if(from_cert_chain.begin(), from_cert_chain.end(), | 289 std::find_if(from_cert_chain.begin(), from_cert_chain.end(), |
| 290 FingerprintsEqualPredicate(*i)); | 290 HashValuesEqualPredicate(*i)); |
| 291 if (j == from_cert_chain.end()) | 291 if (j == from_cert_chain.end()) |
| 292 return true; | 292 return true; |
| 293 } | 293 } |
| 294 | 294 |
| 295 return false; | 295 return false; |
| 296 } | 296 } |
| 297 | 297 |
| 298 static bool HashesIntersect(const FingerprintVector& a, | 298 // Returns true if the intersection of |a| and |b| is not empty. If either |
| 299 const FingerprintVector& b) { | 299 // |a| or |b| is empty, returns false. |
| 300 for (FingerprintVector::const_iterator | 300 static bool HashesIntersect(const HashValueVector& a, |
| 301 i = a.begin(); i != a.end(); ++i) { | 301 const HashValueVector& b) { |
| 302 FingerprintVector::const_iterator j = | 302 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { |
| 303 std::find_if(b.begin(), b.end(), FingerprintsEqualPredicate(*i)); | 303 HashValueVector::const_iterator j = |
| 304 if (j != b.end()) | 304 std::find_if(b.begin(), b.end(), HashValuesEqualPredicate(*i)); |
| 305 return true; | 305 if (j != b.end()) |
| 306 return true; |
| 306 } | 307 } |
| 307 | 308 |
| 308 return false; | 309 return false; |
| 309 } | 310 } |
| 310 | 311 |
| 311 // Returns true iff |pins| contains both a live and a backup pin. A live pin | 312 // Returns true iff |pins| contains both a live and a backup pin. A live pin |
| 312 // is a pin whose SPKI is present in the certificate chain in |ssl_info|. A | 313 // is a pin whose SPKI is present in the certificate chain in |ssl_info|. A |
| 313 // backup pin is a pin intended for disaster recovery, not day-to-day use, and | 314 // backup pin is a pin intended for disaster recovery, not day-to-day use, and |
| 314 // thus must be absent from the certificate chain. The Public-Key-Pins header | 315 // thus must be absent from the certificate chain. The Public-Key-Pins header |
| 315 // specification requires both. | 316 // specification requires both. |
| 316 static bool IsPinListValid(const FingerprintVector& pins, | 317 static bool IsPinListValid(const HashValueVector& pins, |
| 317 const SSLInfo& ssl_info) { | 318 const SSLInfo& ssl_info) { |
| 319 // Fast fail: 1 live + 1 backup = at least 2 pins. (Check for actual |
| 320 // liveness and backupness below.) |
| 318 if (pins.size() < 2) | 321 if (pins.size() < 2) |
| 319 return false; | 322 return false; |
| 320 | 323 |
| 321 const FingerprintVector& from_cert_chain = ssl_info.public_key_hashes; | 324 const HashValueVector& from_cert_chain = ssl_info.public_key_hashes; |
| 322 if (from_cert_chain.empty()) | 325 if (from_cert_chain.empty()) |
| 323 return false; | 326 return false; |
| 324 | 327 |
| 325 return IsBackupPinPresent(pins, from_cert_chain) && | 328 return IsBackupPinPresent(pins, from_cert_chain) && |
| 326 HashesIntersect(pins, from_cert_chain); | 329 HashesIntersect(pins, from_cert_chain); |
| 327 } | 330 } |
| 328 | 331 |
| 329 // "Public-Key-Pins" ":" | 332 // "Public-Key-Pins" ":" |
| 330 // "max-age" "=" delta-seconds ";" | 333 // "max-age" "=" delta-seconds ";" |
| 331 // "pin-" algo "=" base64 [ ";" ... ] | 334 // "pin-" algo "=" base64 [ ";" ... ] |
| 332 bool TransportSecurityState::DomainState::ParsePinsHeader( | 335 bool TransportSecurityState::DomainState::ParsePinsHeader( |
| 333 const base::Time& now, | 336 const base::Time& now, |
| 334 const std::string& value, | 337 const std::string& value, |
| 335 const SSLInfo& ssl_info) { | 338 const SSLInfo& ssl_info) { |
| 336 bool parsed_max_age = false; | 339 bool parsed_max_age = false; |
| 337 int max_age_candidate = 0; | 340 int max_age_candidate = 0; |
| 338 FingerprintVector pins; | 341 HashValueVector pins; |
| 339 | 342 |
| 340 std::string source = value; | 343 std::string source = value; |
| 341 | 344 |
| 342 while (!source.empty()) { | 345 while (!source.empty()) { |
| 343 StringPair semicolon = Split(source, ';'); | 346 StringPair semicolon = Split(source, ';'); |
| 344 semicolon.first = Strip(semicolon.first); | 347 semicolon.first = Strip(semicolon.first); |
| 345 semicolon.second = Strip(semicolon.second); | 348 semicolon.second = Strip(semicolon.second); |
| 346 StringPair equals = Split(semicolon.first, '='); | 349 StringPair equals = Split(semicolon.first, '='); |
| 347 equals.first = Strip(equals.first); | 350 equals.first = Strip(equals.first); |
| 348 equals.second = Strip(equals.second); | 351 equals.second = Strip(equals.second); |
| 349 | 352 |
| 350 if (LowerCaseEqualsASCII(equals.first, "max-age")) { | 353 if (LowerCaseEqualsASCII(equals.first, "max-age")) { |
| 351 if (equals.second.empty() || | 354 if (equals.second.empty() || |
| 352 !MaxAgeToInt(equals.second.begin(), equals.second.end(), | 355 !MaxAgeToInt(equals.second.begin(), equals.second.end(), |
| 353 &max_age_candidate)) { | 356 &max_age_candidate)) { |
| 354 return false; | 357 return false; |
| 355 } | 358 } |
| 356 if (max_age_candidate > kMaxHSTSAgeSecs) | 359 if (max_age_candidate > kMaxHSTSAgeSecs) |
| 357 max_age_candidate = kMaxHSTSAgeSecs; | 360 max_age_candidate = kMaxHSTSAgeSecs; |
| 358 parsed_max_age = true; | 361 parsed_max_age = true; |
| 359 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { | 362 } else if (StartsWithASCII(equals.first, "pin-", false)) { |
| 360 if (!ParseAndAppendPin(equals.second, &pins)) | 363 HashValueTag tag; |
| 364 if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { |
| 365 tag = HASH_VALUE_SHA1; |
| 366 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { |
| 367 tag = HASH_VALUE_SHA256; |
| 368 } else { |
| 369 LOG(WARNING) << "Ignoring pin of unknown type: " << equals.first; |
| 361 return false; | 370 return false; |
| 362 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { | 371 } |
| 363 // TODO(palmer) | 372 if (!ParseAndAppendPin(equals.second, tag, &pins)) |
| 373 return false; |
| 364 } else { | 374 } else { |
| 365 // Silently ignore unknown directives for forward compatibility. | 375 // Silently ignore unknown directives for forward compatibility. |
| 366 } | 376 } |
| 367 | 377 |
| 368 source = semicolon.second; | 378 source = semicolon.second; |
| 369 } | 379 } |
| 370 | 380 |
| 371 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) | 381 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) |
| 372 return false; | 382 return false; |
| 373 | 383 |
| 374 dynamic_spki_hashes_expiry = | 384 dynamic_spki_hashes_expiry = |
| 375 now + base::TimeDelta::FromSeconds(max_age_candidate); | 385 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 376 | 386 |
| 377 dynamic_spki_hashes.clear(); | 387 dynamic_spki_hashes.clear(); |
| 378 if (max_age_candidate > 0) { | 388 if (max_age_candidate > 0) { |
| 379 for (FingerprintVector::const_iterator i = pins.begin(); | 389 for (HashValueVector::const_iterator i = pins.begin(); |
| 380 i != pins.end(); ++i) { | 390 i != pins.end(); ++i) { |
| 381 dynamic_spki_hashes.push_back(*i); | 391 dynamic_spki_hashes.push_back(*i); |
| 382 } | 392 } |
| 383 } | 393 } |
| 384 | 394 |
| 385 return true; | 395 return true; |
| 386 } | 396 } |
| 387 | 397 |
| 388 // "Strict-Transport-Security" ":" | 398 // "Strict-Transport-Security" ":" |
| 389 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] | 399 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 476 include_subdomains = true; | 486 include_subdomains = true; |
| 477 upgrade_mode = MODE_FORCE_HTTPS; | 487 upgrade_mode = MODE_FORCE_HTTPS; |
| 478 return true; | 488 return true; |
| 479 default: | 489 default: |
| 480 NOTREACHED(); | 490 NOTREACHED(); |
| 481 return false; | 491 return false; |
| 482 } | 492 } |
| 483 } | 493 } |
| 484 | 494 |
| 485 static bool AddHash(const std::string& type_and_base64, | 495 static bool AddHash(const std::string& type_and_base64, |
| 486 FingerprintVector* out) { | 496 HashValueVector* out) { |
| 487 SHA1Fingerprint hash; | 497 HashValue hash; |
| 488 | 498 |
| 489 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) | 499 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) |
| 490 return false; | 500 return false; |
| 491 | 501 |
| 492 out->push_back(hash); | 502 out->push_back(hash); |
| 493 return true; | 503 return true; |
| 494 } | 504 } |
| 495 | 505 |
| 496 TransportSecurityState::~TransportSecurityState() {} | 506 TransportSecurityState::~TransportSecurityState() {} |
| 497 | 507 |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 698 } | 708 } |
| 699 | 709 |
| 700 DCHECK(entry); | 710 DCHECK(entry); |
| 701 DCHECK(entry->pins.required_hashes); | 711 DCHECK(entry->pins.required_hashes); |
| 702 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | 712 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); |
| 703 | 713 |
| 704 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | 714 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", |
| 705 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | 715 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); |
| 706 } | 716 } |
| 707 | 717 |
| 718 // static |
| 719 const char* TransportSecurityState::HashValueLabel( |
| 720 const HashValue& hash_value) { |
| 721 switch (hash_value.tag) { |
| 722 case HASH_VALUE_SHA1: |
| 723 return "sha1/"; |
| 724 case HASH_VALUE_SHA256: |
| 725 return "sha256/"; |
| 726 default: |
| 727 NOTREACHED(); |
| 728 LOG(WARNING) << "Invalid fingerprint of unknown type " << hash_value.tag; |
| 729 return "unknown/"; |
| 730 } |
| 731 } |
| 732 |
| 708 bool TransportSecurityState::GetStaticDomainState( | 733 bool TransportSecurityState::GetStaticDomainState( |
| 709 const std::string& canonicalized_host, | 734 const std::string& canonicalized_host, |
| 710 bool sni_enabled, | 735 bool sni_enabled, |
| 711 DomainState* out) { | 736 DomainState* out) { |
| 712 DCHECK(CalledOnValidThread()); | 737 DCHECK(CalledOnValidThread()); |
| 713 | 738 |
| 714 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; | 739 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
| 715 out->include_subdomains = false; | 740 out->include_subdomains = false; |
| 716 | 741 |
| 717 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 742 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 743 const std::string& hashed_host, const DomainState& state) { | 768 const std::string& hashed_host, const DomainState& state) { |
| 744 enabled_hosts_[hashed_host] = state; | 769 enabled_hosts_[hashed_host] = state; |
| 745 } | 770 } |
| 746 | 771 |
| 747 void TransportSecurityState::AddOrUpdateForcedHosts( | 772 void TransportSecurityState::AddOrUpdateForcedHosts( |
| 748 const std::string& hashed_host, const DomainState& state) { | 773 const std::string& hashed_host, const DomainState& state) { |
| 749 forced_hosts_[hashed_host] = state; | 774 forced_hosts_[hashed_host] = state; |
| 750 } | 775 } |
| 751 | 776 |
| 752 static std::string HashesToBase64String( | 777 static std::string HashesToBase64String( |
| 753 const FingerprintVector& hashes) { | 778 const HashValueVector& hashes) { |
| 754 std::vector<std::string> hashes_strs; | 779 std::vector<std::string> hashes_strs; |
| 755 for (FingerprintVector::const_iterator | 780 for (HashValueVector::const_iterator |
| 756 i = hashes.begin(); i != hashes.end(); i++) { | 781 i = hashes.begin(); i != hashes.end(); i++) { |
| 757 std::string s; | 782 std::string s; |
| 758 const std::string hash_str(reinterpret_cast<const char*>(i->data), | 783 const std::string hash_str(reinterpret_cast<const char*>(i->data()), |
| 759 sizeof(i->data)); | 784 i->size()); |
| 760 base::Base64Encode(hash_str, &s); | 785 base::Base64Encode(hash_str, &s); |
| 761 hashes_strs.push_back(s); | 786 hashes_strs.push_back(s); |
| 762 } | 787 } |
| 763 | 788 |
| 764 return JoinString(hashes_strs, ','); | 789 return JoinString(hashes_strs, ','); |
| 765 } | 790 } |
| 766 | 791 |
| 767 TransportSecurityState::DomainState::DomainState() | 792 TransportSecurityState::DomainState::DomainState() |
| 768 : upgrade_mode(MODE_FORCE_HTTPS), | 793 : upgrade_mode(MODE_FORCE_HTTPS), |
| 769 created(base::Time::Now()), | 794 created(base::Time::Now()), |
| 770 include_subdomains(false) { | 795 include_subdomains(false) { |
| 771 } | 796 } |
| 772 | 797 |
| 773 TransportSecurityState::DomainState::~DomainState() { | 798 TransportSecurityState::DomainState::~DomainState() { |
| 774 } | 799 } |
| 775 | 800 |
| 776 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( | 801 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( |
| 777 const FingerprintVector& hashes) const { | 802 const HashValueVector& hashes) const { |
| 803 // Validate that hashes is not empty. By the time this code is called (in |
| 804 // production), that should never happen, but it's good to be defensive. |
| 805 // And, hashes *can* be empty in some test scenarios. |
| 806 if (hashes.empty()) { |
| 807 LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned " |
| 808 "domain " << domain; |
| 809 return false; |
| 810 } |
| 811 |
| 778 if (HashesIntersect(bad_static_spki_hashes, hashes)) { | 812 if (HashesIntersect(bad_static_spki_hashes, hashes)) { |
| 779 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 813 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
| 780 << ". Validated chain: " << HashesToBase64String(hashes) | 814 << ". Validated chain: " << HashesToBase64String(hashes) |
| 781 << ", matches one or more bad hashes: " | 815 << ", matches one or more bad hashes: " |
| 782 << HashesToBase64String(bad_static_spki_hashes); | 816 << HashesToBase64String(bad_static_spki_hashes); |
| 783 return false; | 817 return false; |
| 784 } | 818 } |
| 785 | 819 |
| 786 if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) && | 820 // If there are no pins, then any valid chain is acceptable. |
| 787 !HashesIntersect(dynamic_spki_hashes, hashes) && | 821 if (dynamic_spki_hashes.empty() && static_spki_hashes.empty()) |
| 788 !HashesIntersect(static_spki_hashes, hashes)) { | 822 return true; |
| 789 LOG(ERROR) << "Rejecting public key chain for domain " << domain | |
| 790 << ". Validated chain: " << HashesToBase64String(hashes) | |
| 791 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) | |
| 792 << " or: " << HashesToBase64String(static_spki_hashes); | |
| 793 | 823 |
| 794 return false; | 824 if (HashesIntersect(dynamic_spki_hashes, hashes) || |
| 825 HashesIntersect(static_spki_hashes, hashes)) { |
| 826 return true; |
| 795 } | 827 } |
| 796 | 828 |
| 797 return true; | 829 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
| 830 << ". Validated chain: " << HashesToBase64String(hashes) |
| 831 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) |
| 832 << " or: " << HashesToBase64String(static_spki_hashes); |
| 833 return false; |
| 798 } | 834 } |
| 799 | 835 |
| 800 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const { | 836 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const { |
| 801 return upgrade_mode == MODE_FORCE_HTTPS; | 837 return upgrade_mode == MODE_FORCE_HTTPS; |
| 802 } | 838 } |
| 803 | 839 |
| 804 bool TransportSecurityState::DomainState::Equals( | 840 bool TransportSecurityState::DomainState::Equals( |
| 805 const DomainState& other) const { | 841 const DomainState& other) const { |
| 806 // TODO(palmer): Implement this | 842 // TODO(palmer): Implement this |
| 807 (void) other; | 843 (void) other; |
| 808 return true; | 844 return true; |
| 809 } | 845 } |
| 810 | 846 |
| 811 bool TransportSecurityState::DomainState::HasPins() const { | 847 bool TransportSecurityState::DomainState::HasPins() const { |
| 812 return static_spki_hashes.size() > 0 || | 848 return static_spki_hashes.size() > 0 || |
| 813 bad_static_spki_hashes.size() > 0 || | 849 bad_static_spki_hashes.size() > 0 || |
| 814 dynamic_spki_hashes.size() > 0; | 850 dynamic_spki_hashes.size() > 0; |
| 815 } | 851 } |
| 816 | 852 |
| 817 } // namespace | 853 } // namespace |
| OLD | NEW |