Chromium Code Reviews| 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 HashValueVector* fingerprints) { |
| 250 // The base64'd fingerprint MUST be a quoted-string. 20 bytes base64'd is 28 | 252 // The base64'd fingerprint MUST be a quoted-string. 20 bytes base64'd is 28 |
| 251 // characters; 32 bytes base64'd is 44 characters. TODO(palmer): Support | 253 // characters; 32 bytes base64'd is 44 characters. |
| 252 // SHA256. | |
| 253 size_t size = value.size(); | 254 size_t size = value.size(); |
| 254 if (size != 30 || value[0] != '"' || value[size - 1] != '"') | 255 if ((size != 30 && size != 46) || value[0] != '"' || value[size - 1] != '"') |
|
hshi1
2012/08/11 01:05:40
Having hard-coded numerical values (such as 30 and
palmer
2012/08/14 19:40:42
Done.
| |
| 255 return false; | 256 return false; |
| 256 | 257 |
| 257 std::string unquoted = HttpUtil::Unquote(value); | 258 std::string unquoted = HttpUtil::Unquote(value); |
| 258 std::string decoded; | 259 std::string decoded; |
| 259 SHA1Fingerprint fp; | 260 HashValue fp; |
| 260 | 261 |
| 261 if (!base::Base64Decode(unquoted, &decoded) || | 262 // This code has to assume that 32 bytes is SHA-256 and 20 bytes is SHA-1. |
| 262 decoded.size() != arraysize(fp.data)) { | 263 // Currently, those are the only two possibilities, so the assumption is |
| 264 // valid. | |
| 265 if (!base::Base64Decode(unquoted, &decoded)) | |
| 263 return false; | 266 return false; |
| 264 } | |
| 265 | 267 |
| 266 memcpy(fp.data, decoded.data(), arraysize(fp.data)); | 268 if (decoded.size() == 20) |
|
hshi1
2012/08/11 01:05:40
Nit: would it be better to use base::kSHA1Length i
palmer
2012/08/14 19:40:42
Done.
| |
| 269 fp.tag = HASH_VALUE_SHA1; | |
| 270 else if (decoded.size() == 32) | |
|
hshi1
2012/08/11 01:05:40
Nit: would it be better to use crypto::kSHA256Leng
palmer
2012/08/14 19:40:42
Done.
| |
| 271 fp.tag = HASH_VALUE_SHA256; | |
| 272 else | |
| 273 return false; | |
| 274 | |
| 275 memcpy(fp.data(), decoded.data(), fp.size()); | |
| 267 fingerprints->push_back(fp); | 276 fingerprints->push_back(fp); |
| 268 return true; | 277 return true; |
| 269 } | 278 } |
| 270 | 279 |
| 271 struct FingerprintsEqualPredicate { | 280 struct HashValuesEqualPredicate { |
| 272 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : | 281 explicit HashValuesEqualPredicate(const HashValue& fingerprint) : |
| 273 fingerprint_(fingerprint) {} | 282 fingerprint_(fingerprint) {} |
| 274 | 283 |
| 275 bool operator()(const SHA1Fingerprint& other) const { | 284 bool operator()(const HashValue& other) const { |
| 276 return fingerprint_.Equals(other); | 285 return fingerprint_.Equals(other); |
| 277 } | 286 } |
| 278 | 287 |
| 279 const SHA1Fingerprint& fingerprint_; | 288 const HashValue& fingerprint_; |
| 280 }; | 289 }; |
| 281 | 290 |
| 282 // Returns true iff there is an item in |pins| which is not present in | 291 // 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". | 292 // |from_cert_chain|. Such an SPKI hash is called a "backup pin". |
| 284 static bool IsBackupPinPresent(const FingerprintVector& pins, | 293 static bool IsBackupPinPresent(const HashValueVector& pins, |
| 285 const FingerprintVector& from_cert_chain) { | 294 const HashValueVector& from_cert_chain) { |
| 286 for (FingerprintVector::const_iterator | 295 for (HashValueVector::const_iterator |
| 287 i = pins.begin(); i != pins.end(); ++i) { | 296 i = pins.begin(); i != pins.end(); ++i) { |
| 288 FingerprintVector::const_iterator j = | 297 HashValueVector::const_iterator j = |
| 289 std::find_if(from_cert_chain.begin(), from_cert_chain.end(), | 298 std::find_if(from_cert_chain.begin(), from_cert_chain.end(), |
| 290 FingerprintsEqualPredicate(*i)); | 299 HashValuesEqualPredicate(*i)); |
| 291 if (j == from_cert_chain.end()) | 300 if (j == from_cert_chain.end()) |
| 292 return true; | 301 return true; |
| 293 } | 302 } |
| 294 | 303 |
| 295 return false; | 304 return false; |
| 296 } | 305 } |
| 297 | 306 |
| 298 static bool HashesIntersect(const FingerprintVector& a, | 307 // Returns true if the intersection of |a| and |b| is not empty. If either |
| 299 const FingerprintVector& b) { | 308 // |a| or |b| is empty, returns false. |
| 300 for (FingerprintVector::const_iterator | 309 static bool HashesIntersect(const HashValueVector& a, |
| 301 i = a.begin(); i != a.end(); ++i) { | 310 const HashValueVector& b) { |
| 302 FingerprintVector::const_iterator j = | 311 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { |
| 303 std::find_if(b.begin(), b.end(), FingerprintsEqualPredicate(*i)); | 312 HashValueVector::const_iterator j = |
| 304 if (j != b.end()) | 313 std::find_if(b.begin(), b.end(), HashValuesEqualPredicate(*i)); |
| 305 return true; | 314 if (j != b.end()) |
| 315 return true; | |
| 306 } | 316 } |
| 307 | 317 |
| 308 return false; | 318 return false; |
| 309 } | 319 } |
| 310 | 320 |
| 311 // Returns true iff |pins| contains both a live and a backup pin. A live pin | 321 // 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 | 322 // 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 | 323 // 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 | 324 // thus must be absent from the certificate chain. The Public-Key-Pins header |
| 315 // specification requires both. | 325 // specification requires both. |
| 316 static bool IsPinListValid(const FingerprintVector& pins, | 326 static bool IsPinListValid(const HashValueVector& pins, |
| 317 const SSLInfo& ssl_info) { | 327 const SSLInfo& ssl_info) { |
| 328 // Fast fail: 1 live + 1 backup = at least 2 pins. (Check for actual | |
| 329 // liveness and backupness below.) | |
| 318 if (pins.size() < 2) | 330 if (pins.size() < 2) |
| 319 return false; | 331 return false; |
| 320 | 332 |
| 321 const FingerprintVector& from_cert_chain = ssl_info.public_key_hashes; | 333 // Site operators might pin a key using either the SHA-1 or SHA-256 hash |
| 322 if (from_cert_chain.empty()) | 334 // of the SPKI. So check for success using either hash function. |
| 335 // | |
| 336 // TODO(palmer): Make this generic so that it works regardless of what | |
| 337 // HashValueTags are defined in the future. | |
| 338 const HashValueVector& from_cert_chain_sha1 = | |
| 339 ssl_info.public_key_hashes[HASH_VALUE_SHA1]; | |
| 340 const HashValueVector& from_cert_chain_sha256 = | |
| 341 ssl_info.public_key_hashes[HASH_VALUE_SHA256]; | |
| 342 | |
| 343 if (from_cert_chain_sha1.empty() && from_cert_chain_sha256.empty()) | |
| 323 return false; | 344 return false; |
| 324 | 345 |
| 325 return IsBackupPinPresent(pins, from_cert_chain) && | 346 return (IsBackupPinPresent(pins, from_cert_chain_sha1) || |
| 326 HashesIntersect(pins, from_cert_chain); | 347 IsBackupPinPresent(pins, from_cert_chain_sha256)) && |
| 348 (HashesIntersect(pins, from_cert_chain_sha1) || | |
| 349 HashesIntersect(pins, from_cert_chain_sha256)); | |
| 327 } | 350 } |
| 328 | 351 |
| 329 // "Public-Key-Pins" ":" | 352 // "Public-Key-Pins" ":" |
| 330 // "max-age" "=" delta-seconds ";" | 353 // "max-age" "=" delta-seconds ";" |
| 331 // "pin-" algo "=" base64 [ ";" ... ] | 354 // "pin-" algo "=" base64 [ ";" ... ] |
| 332 bool TransportSecurityState::DomainState::ParsePinsHeader( | 355 bool TransportSecurityState::DomainState::ParsePinsHeader( |
| 333 const base::Time& now, | 356 const base::Time& now, |
| 334 const std::string& value, | 357 const std::string& value, |
| 335 const SSLInfo& ssl_info) { | 358 const SSLInfo& ssl_info) { |
| 336 bool parsed_max_age = false; | 359 bool parsed_max_age = false; |
| 337 int max_age_candidate = 0; | 360 int max_age_candidate = 0; |
| 338 FingerprintVector pins; | 361 HashValueVector pins; |
| 339 | 362 |
| 340 std::string source = value; | 363 std::string source = value; |
| 341 | 364 |
| 342 while (!source.empty()) { | 365 while (!source.empty()) { |
| 343 StringPair semicolon = Split(source, ';'); | 366 StringPair semicolon = Split(source, ';'); |
| 344 semicolon.first = Strip(semicolon.first); | 367 semicolon.first = Strip(semicolon.first); |
| 345 semicolon.second = Strip(semicolon.second); | 368 semicolon.second = Strip(semicolon.second); |
| 346 StringPair equals = Split(semicolon.first, '='); | 369 StringPair equals = Split(semicolon.first, '='); |
| 347 equals.first = Strip(equals.first); | 370 equals.first = Strip(equals.first); |
| 348 equals.second = Strip(equals.second); | 371 equals.second = Strip(equals.second); |
| 349 | 372 |
| 350 if (LowerCaseEqualsASCII(equals.first, "max-age")) { | 373 if (LowerCaseEqualsASCII(equals.first, "max-age")) { |
| 351 if (equals.second.empty() || | 374 if (equals.second.empty() || |
| 352 !MaxAgeToInt(equals.second.begin(), equals.second.end(), | 375 !MaxAgeToInt(equals.second.begin(), equals.second.end(), |
| 353 &max_age_candidate)) { | 376 &max_age_candidate)) { |
| 354 return false; | 377 return false; |
| 355 } | 378 } |
| 356 if (max_age_candidate > kMaxHSTSAgeSecs) | 379 if (max_age_candidate > kMaxHSTSAgeSecs) |
| 357 max_age_candidate = kMaxHSTSAgeSecs; | 380 max_age_candidate = kMaxHSTSAgeSecs; |
| 358 parsed_max_age = true; | 381 parsed_max_age = true; |
| 359 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { | 382 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1") || |
| 383 LowerCaseEqualsASCII(equals.first, "pin-sha256")) { | |
| 360 if (!ParseAndAppendPin(equals.second, &pins)) | 384 if (!ParseAndAppendPin(equals.second, &pins)) |
| 361 return false; | 385 return false; |
| 362 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { | |
| 363 // TODO(palmer) | |
| 364 } else { | 386 } else { |
| 365 // Silently ignore unknown directives for forward compatibility. | 387 // Silently ignore unknown directives for forward compatibility. |
| 366 } | 388 } |
| 367 | 389 |
| 368 source = semicolon.second; | 390 source = semicolon.second; |
| 369 } | 391 } |
| 370 | 392 |
| 371 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) | 393 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) |
| 372 return false; | 394 return false; |
| 373 | 395 |
| 374 dynamic_spki_hashes_expiry = | 396 dynamic_spki_hashes_expiry = |
| 375 now + base::TimeDelta::FromSeconds(max_age_candidate); | 397 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 376 | 398 |
| 377 dynamic_spki_hashes.clear(); | 399 dynamic_spki_hashes.clear(); |
| 378 if (max_age_candidate > 0) { | 400 if (max_age_candidate > 0) { |
| 379 for (FingerprintVector::const_iterator i = pins.begin(); | 401 for (HashValueVector::const_iterator i = pins.begin(); |
| 380 i != pins.end(); ++i) { | 402 i != pins.end(); ++i) { |
| 381 dynamic_spki_hashes.push_back(*i); | 403 dynamic_spki_hashes.push_back(*i); |
| 382 } | 404 } |
| 383 } | 405 } |
| 384 | 406 |
| 385 return true; | 407 return true; |
| 386 } | 408 } |
| 387 | 409 |
| 388 // "Strict-Transport-Security" ":" | 410 // "Strict-Transport-Security" ":" |
| 389 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] | 411 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 476 include_subdomains = true; | 498 include_subdomains = true; |
| 477 upgrade_mode = MODE_FORCE_HTTPS; | 499 upgrade_mode = MODE_FORCE_HTTPS; |
| 478 return true; | 500 return true; |
| 479 default: | 501 default: |
| 480 NOTREACHED(); | 502 NOTREACHED(); |
| 481 return false; | 503 return false; |
| 482 } | 504 } |
| 483 } | 505 } |
| 484 | 506 |
| 485 static bool AddHash(const std::string& type_and_base64, | 507 static bool AddHash(const std::string& type_and_base64, |
| 486 FingerprintVector* out) { | 508 HashValueVector* out) { |
| 487 SHA1Fingerprint hash; | 509 HashValue hash; |
| 488 | 510 |
| 489 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) | 511 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) |
| 490 return false; | 512 return false; |
| 491 | 513 |
| 492 out->push_back(hash); | 514 out->push_back(hash); |
| 493 return true; | 515 return true; |
| 494 } | 516 } |
| 495 | 517 |
| 496 TransportSecurityState::~TransportSecurityState() {} | 518 TransportSecurityState::~TransportSecurityState() {} |
| 497 | 519 |
| (...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 743 const std::string& hashed_host, const DomainState& state) { | 765 const std::string& hashed_host, const DomainState& state) { |
| 744 enabled_hosts_[hashed_host] = state; | 766 enabled_hosts_[hashed_host] = state; |
| 745 } | 767 } |
| 746 | 768 |
| 747 void TransportSecurityState::AddOrUpdateForcedHosts( | 769 void TransportSecurityState::AddOrUpdateForcedHosts( |
| 748 const std::string& hashed_host, const DomainState& state) { | 770 const std::string& hashed_host, const DomainState& state) { |
| 749 forced_hosts_[hashed_host] = state; | 771 forced_hosts_[hashed_host] = state; |
| 750 } | 772 } |
| 751 | 773 |
| 752 static std::string HashesToBase64String( | 774 static std::string HashesToBase64String( |
| 753 const FingerprintVector& hashes) { | 775 const HashValueVector& hashes) { |
| 754 std::vector<std::string> hashes_strs; | 776 std::vector<std::string> hashes_strs; |
| 755 for (FingerprintVector::const_iterator | 777 for (HashValueVector::const_iterator |
| 756 i = hashes.begin(); i != hashes.end(); i++) { | 778 i = hashes.begin(); i != hashes.end(); i++) { |
| 757 std::string s; | 779 std::string s; |
| 758 const std::string hash_str(reinterpret_cast<const char*>(i->data), | 780 const std::string hash_str(reinterpret_cast<const char*>(i->data()), |
| 759 sizeof(i->data)); | 781 i->size()); |
| 760 base::Base64Encode(hash_str, &s); | 782 base::Base64Encode(hash_str, &s); |
| 761 hashes_strs.push_back(s); | 783 hashes_strs.push_back(s); |
| 762 } | 784 } |
| 763 | 785 |
| 764 return JoinString(hashes_strs, ','); | 786 return JoinString(hashes_strs, ','); |
| 765 } | 787 } |
| 766 | 788 |
| 767 TransportSecurityState::DomainState::DomainState() | 789 TransportSecurityState::DomainState::DomainState() |
| 768 : upgrade_mode(MODE_FORCE_HTTPS), | 790 : upgrade_mode(MODE_FORCE_HTTPS), |
| 769 created(base::Time::Now()), | 791 created(base::Time::Now()), |
| 770 include_subdomains(false) { | 792 include_subdomains(false) { |
| 771 } | 793 } |
| 772 | 794 |
| 773 TransportSecurityState::DomainState::~DomainState() { | 795 TransportSecurityState::DomainState::~DomainState() { |
| 774 } | 796 } |
| 775 | 797 |
| 776 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( | 798 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( |
| 777 const FingerprintVector& hashes) const { | 799 const std::vector<HashValueVector>& hashes) const { |
| 778 if (HashesIntersect(bad_static_spki_hashes, hashes)) { | 800 // Validate that hashes is not empty. By the time this code is called (in |
| 779 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 801 // production), that should never happen, but it's good to be defensive. |
| 780 << ". Validated chain: " << HashesToBase64String(hashes) | 802 // And, hashes *can* be empty in some test scenarios. |
| 781 << ", matches one or more bad hashes: " | 803 bool empty = true; |
| 782 << HashesToBase64String(bad_static_spki_hashes); | 804 for (size_t i = 0; i < hashes.size(); ++i) { |
| 805 if (hashes[i].size()) { | |
| 806 empty = false; | |
| 807 break; | |
| 808 } | |
| 809 } | |
| 810 if (empty) { | |
| 811 LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned " | |
| 812 "domain " << domain; | |
| 783 return false; | 813 return false; |
| 784 } | 814 } |
| 785 | 815 |
| 786 if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) && | 816 bool found_good_pin = false; |
| 787 !HashesIntersect(dynamic_spki_hashes, hashes) && | 817 for (size_t i = 0; i < hashes.size(); ++i) { |
| 788 !HashesIntersect(static_spki_hashes, hashes)) { | 818 if (HashesIntersect(bad_static_spki_hashes, hashes[i])) { |
| 819 LOG(ERROR) << "Rejecting public key chain for domain " << domain | |
| 820 << ". Validated chain: " << HashesToBase64String(hashes[i]) | |
| 821 << ", matches one or more bad hashes: " | |
| 822 << HashesToBase64String(bad_static_spki_hashes); | |
| 823 return false; | |
| 824 } | |
| 825 | |
| 826 if (HashesIntersect(dynamic_spki_hashes, hashes[i]) || | |
| 827 HashesIntersect(static_spki_hashes, hashes[i])) { | |
| 828 found_good_pin = true; | |
| 829 } | |
| 830 } | |
| 831 | |
| 832 if (!found_good_pin) { | |
| 833 std::vector<std::string> base64s; | |
| 834 for (size_t i = 0; i < hashes.size(); ++i) | |
| 835 base64s.push_back(HashesToBase64String(hashes[i])); | |
| 836 | |
| 789 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 837 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
| 790 << ". Validated chain: " << HashesToBase64String(hashes) | 838 << ". Validated chain: " << JoinString(base64s, ',') |
| 791 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) | 839 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) |
| 792 << " or: " << HashesToBase64String(static_spki_hashes); | 840 << " or: " << HashesToBase64String(static_spki_hashes); |
| 793 | |
| 794 return false; | 841 return false; |
| 795 } | 842 } |
| 796 | 843 |
| 797 return true; | 844 return true; |
| 798 } | 845 } |
| 799 | 846 |
| 800 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const { | 847 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const { |
| 801 return upgrade_mode == MODE_FORCE_HTTPS; | 848 return upgrade_mode == MODE_FORCE_HTTPS; |
| 802 } | 849 } |
| 803 | 850 |
| 804 bool TransportSecurityState::DomainState::Equals( | 851 bool TransportSecurityState::DomainState::Equals( |
| 805 const DomainState& other) const { | 852 const DomainState& other) const { |
| 806 // TODO(palmer): Implement this | 853 // TODO(palmer): Implement this |
| 807 (void) other; | 854 (void) other; |
| 808 return true; | 855 return true; |
| 809 } | 856 } |
| 810 | 857 |
| 811 bool TransportSecurityState::DomainState::HasPins() const { | 858 bool TransportSecurityState::DomainState::HasPins() const { |
| 812 return static_spki_hashes.size() > 0 || | 859 return static_spki_hashes.size() > 0 || |
| 813 bad_static_spki_hashes.size() > 0 || | 860 bad_static_spki_hashes.size() > 0 || |
| 814 dynamic_spki_hashes.size() > 0; | 861 dynamic_spki_hashes.size() > 0; |
| 815 } | 862 } |
| 816 | 863 |
| 817 } // namespace | 864 } // namespace |
| OLD | NEW |