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> | 19 #include <utility> |
| 20 | 20 |
| 21 #include "base/base64.h" | 21 #include "base/base64.h" |
| 22 #include "base/json/json_reader.h" | |
| 23 #include "base/json/json_writer.h" | |
| 24 #include "base/logging.h" | 22 #include "base/logging.h" |
| 25 #include "base/memory/scoped_ptr.h" | 23 #include "base/memory/scoped_ptr.h" |
| 26 #include "base/metrics/histogram.h" | 24 #include "base/metrics/histogram.h" |
| 27 #include "base/sha1.h" | 25 #include "base/sha1.h" |
| 28 #include "base/string_number_conversions.h" | 26 #include "base/string_number_conversions.h" |
| 29 #include "base/string_tokenizer.h" | 27 #include "base/string_tokenizer.h" |
| 30 #include "base/string_util.h" | 28 #include "base/string_util.h" |
| 31 #include "base/time.h" | 29 #include "base/time.h" |
| 32 #include "base/utf_string_conversions.h" | 30 #include "base/utf_string_conversions.h" |
| 33 #include "base/values.h" | 31 #include "base/values.h" |
| 34 #include "crypto/sha2.h" | 32 #include "crypto/sha2.h" |
| 35 #include "googleurl/src/gurl.h" | 33 #include "googleurl/src/gurl.h" |
| 36 #include "net/base/asn1_util.h" | |
| 37 #include "net/base/dns_util.h" | 34 #include "net/base/dns_util.h" |
| 38 #include "net/base/ssl_info.h" | 35 #include "net/base/ssl_info.h" |
| 39 #include "net/base/x509_certificate.h" | 36 #include "net/base/x509_certificate.h" |
| 40 #include "net/http/http_util.h" | 37 #include "net/http/http_util.h" |
| 41 | 38 |
| 42 #if defined(USE_OPENSSL) | 39 #if defined(USE_OPENSSL) |
| 43 #include "crypto/openssl_util.h" | 40 #include "crypto/openssl_util.h" |
| 44 #endif | 41 #endif |
| 45 | 42 |
| 46 namespace net { | 43 namespace net { |
| 47 | 44 |
| 48 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year | 45 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year |
| 49 | 46 |
| 50 TransportSecurityState::TransportSecurityState(const std::string& hsts_hosts) | |
| 51 : delegate_(NULL) { | |
| 52 if (!hsts_hosts.empty()) { | |
| 53 bool dirty; | |
| 54 Deserialise(hsts_hosts, &dirty, &forced_hosts_); | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 static std::string HashHost(const std::string& canonicalized_host) { | 47 static std::string HashHost(const std::string& canonicalized_host) { |
| 59 char hashed[crypto::kSHA256Length]; | 48 char hashed[crypto::kSHA256Length]; |
| 60 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); | 49 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); |
| 61 return std::string(hashed, sizeof(hashed)); | 50 return std::string(hashed, sizeof(hashed)); |
| 62 } | 51 } |
| 63 | 52 |
| 53 TransportSecurityState::TransportSecurityState() | |
| 54 : delegate_(NULL) { } | |
|
Ryan Sleevi
2012/04/26 19:21:12
nit: move } to a new line
palmer
2012/04/27 23:52:34
Done.
| |
| 55 | |
| 64 void TransportSecurityState::SetDelegate( | 56 void TransportSecurityState::SetDelegate( |
| 65 TransportSecurityState::Delegate* delegate) { | 57 TransportSecurityState::Delegate* delegate) { |
| 66 delegate_ = delegate; | 58 delegate_ = delegate; |
| 67 } | 59 } |
| 68 | 60 |
| 69 void TransportSecurityState::EnableHost(const std::string& host, | 61 void TransportSecurityState::EnableHost(const std::string& host, |
| 70 const DomainState& state) { | 62 const DomainState& state) { |
| 71 DCHECK(CalledOnValidThread()); | 63 DCHECK(CalledOnValidThread()); |
| 72 | 64 |
| 73 const std::string canonicalized_host = CanonicalizeHost(host); | 65 const std::string canonicalized_host = CanonicalizeHost(host); |
| 74 if (canonicalized_host.empty()) | 66 if (canonicalized_host.empty()) |
| 75 return; | 67 return; |
| 76 | 68 |
| 77 // Only override a preloaded state if the new state describes a more strict | |
| 78 // policy. TODO(palmer): Reconsider this? | |
| 79 DomainState existing_state; | 69 DomainState existing_state; |
| 80 if (IsPreloadedSTS(canonicalized_host, true, &existing_state) && | |
| 81 canonicalized_host == CanonicalizeHost(existing_state.domain) && | |
| 82 existing_state.IsMoreStrict(state)) { | |
| 83 return; | |
| 84 } | |
| 85 | 70 |
| 86 // Use the original creation date if we already have this host. | 71 // Use the original creation date if we already have this host. (But note |
| 72 // that statically-defined states have no |created| date. Therefore, we do | |
| 73 // not bother to search the SNI-only static states.) | |
| 87 DomainState state_copy(state); | 74 DomainState state_copy(state); |
| 88 if (GetDomainState(&existing_state, host, true) && | 75 if (GetDomainState(host, false /* sni_enabled */, &existing_state) && |
| 89 !existing_state.created.is_null()) { | 76 !existing_state.created.is_null()) { |
| 90 state_copy.created = existing_state.created; | 77 state_copy.created = existing_state.created; |
| 91 } | 78 } |
| 92 | 79 |
| 93 // We don't store these values. | 80 // No need to store this value since it is redundant. (|canonicalized_host| |
| 94 state_copy.preloaded = false; | 81 // is the map key.) |
| 95 state_copy.domain.clear(); | 82 state_copy.domain.clear(); |
| 96 | 83 |
| 97 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; | 84 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; |
| 98 DirtyNotify(); | 85 DirtyNotify(); |
| 99 } | 86 } |
| 100 | 87 |
| 101 bool TransportSecurityState::DeleteHost(const std::string& host) { | 88 bool TransportSecurityState::DeleteHost(const std::string& host) { |
| 102 DCHECK(CalledOnValidThread()); | 89 DCHECK(CalledOnValidThread()); |
| 103 | 90 |
| 104 const std::string canonicalized_host = CanonicalizeHost(host); | 91 const std::string canonicalized_host = CanonicalizeHost(host); |
| 105 if (canonicalized_host.empty()) | 92 if (canonicalized_host.empty()) |
| 106 return false; | 93 return false; |
| 107 | 94 |
| 108 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find( | 95 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find( |
| 109 HashHost(canonicalized_host)); | 96 HashHost(canonicalized_host)); |
| 110 if (i != enabled_hosts_.end()) { | 97 if (i != enabled_hosts_.end()) { |
| 111 enabled_hosts_.erase(i); | 98 enabled_hosts_.erase(i); |
| 112 DirtyNotify(); | 99 DirtyNotify(); |
| 113 return true; | 100 return true; |
| 114 } | 101 } |
| 115 return false; | 102 return false; |
| 116 } | 103 } |
| 117 | 104 |
| 118 bool TransportSecurityState::HasPinsForHost(DomainState* result, | 105 bool TransportSecurityState::GetDomainState(const std::string& host, |
| 119 const std::string& host, | 106 bool sni_enabled, |
| 120 bool sni_available) { | 107 DomainState* result) { |
| 121 DCHECK(CalledOnValidThread()); | |
| 122 | |
| 123 return HasMetadata(result, host, sni_available) && | |
| 124 (!result->dynamic_spki_hashes.empty() || | |
| 125 !result->preloaded_spki_hashes.empty()); | |
| 126 } | |
| 127 | |
| 128 bool TransportSecurityState::GetDomainState(DomainState* result, | |
| 129 const std::string& host, | |
| 130 bool sni_available) { | |
| 131 DCHECK(CalledOnValidThread()); | |
| 132 | |
| 133 return HasMetadata(result, host, sni_available); | |
| 134 } | |
| 135 | |
| 136 bool TransportSecurityState::HasMetadata(DomainState* result, | |
| 137 const std::string& host, | |
| 138 bool sni_available) { | |
| 139 DCHECK(CalledOnValidThread()); | 108 DCHECK(CalledOnValidThread()); |
| 140 | 109 |
| 141 DomainState state; | 110 DomainState state; |
| 142 const std::string canonicalized_host = CanonicalizeHost(host); | 111 const std::string canonicalized_host = CanonicalizeHost(host); |
| 143 if (canonicalized_host.empty()) | 112 if (canonicalized_host.empty()) |
| 144 return false; | 113 return false; |
| 145 | 114 |
| 146 bool has_preload = IsPreloadedSTS(canonicalized_host, sni_available, &state); | 115 bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled, |
| 116 &state); | |
| 147 std::string canonicalized_preload = CanonicalizeHost(state.domain); | 117 std::string canonicalized_preload = CanonicalizeHost(state.domain); |
| 148 | 118 |
| 149 base::Time current_time(base::Time::Now()); | 119 base::Time current_time(base::Time::Now()); |
| 150 | 120 |
| 151 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 121 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
| 152 std::string host_sub_chunk(&canonicalized_host[i], | 122 std::string host_sub_chunk(&canonicalized_host[i], |
| 153 canonicalized_host.size() - i); | 123 canonicalized_host.size() - i); |
| 154 // Exact match of a preload always wins. | 124 // Exact match of a preload always wins. |
| 155 if (has_preload && host_sub_chunk == canonicalized_preload) { | 125 if (has_preload && host_sub_chunk == canonicalized_preload) { |
| 156 *result = state; | 126 *result = state; |
| 157 return true; | 127 return true; |
| 158 } | 128 } |
| 159 | 129 |
| 160 std::map<std::string, DomainState>::iterator j = | 130 std::map<std::string, DomainState>::iterator j = |
| 161 enabled_hosts_.find(HashHost(host_sub_chunk)); | 131 enabled_hosts_.find(HashHost(host_sub_chunk)); |
| 162 if (j == enabled_hosts_.end()) | 132 if (j == enabled_hosts_.end()) |
| 163 continue; | 133 continue; |
| 164 | 134 |
| 165 if (current_time > j->second.expiry && | 135 if (current_time > j->second.upgrade_expiry && |
| 166 current_time > j->second.dynamic_spki_hashes_expiry) { | 136 current_time > j->second.dynamic_spki_hashes_expiry) { |
| 167 enabled_hosts_.erase(j); | 137 enabled_hosts_.erase(j); |
| 168 DirtyNotify(); | 138 DirtyNotify(); |
| 169 continue; | 139 continue; |
| 170 } | 140 } |
| 171 | 141 |
| 172 state = j->second; | 142 state = j->second; |
| 173 state.domain = DNSDomainToString(host_sub_chunk); | 143 state.domain = DNSDomainToString(host_sub_chunk); |
| 174 | 144 |
| 175 // Succeed if we matched the domain exactly or if subdomain matches are | 145 // Succeed if we matched the domain exactly or if subdomain matches are |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 283 if (!base::Base64Decode(unquoted, &decoded) || | 253 if (!base::Base64Decode(unquoted, &decoded) || |
| 284 decoded.size() != arraysize(fp.data)) { | 254 decoded.size() != arraysize(fp.data)) { |
| 285 return false; | 255 return false; |
| 286 } | 256 } |
| 287 | 257 |
| 288 memcpy(fp.data, decoded.data(), arraysize(fp.data)); | 258 memcpy(fp.data, decoded.data(), arraysize(fp.data)); |
| 289 fingerprints->push_back(fp); | 259 fingerprints->push_back(fp); |
| 290 return true; | 260 return true; |
| 291 } | 261 } |
| 292 | 262 |
| 293 // static | |
| 294 bool TransportSecurityState::GetPublicKeyHash( | |
| 295 const X509Certificate& cert, SHA1Fingerprint* spki_hash) { | |
| 296 std::string der_bytes; | |
| 297 if (!X509Certificate::GetDEREncoded(cert.os_cert_handle(), &der_bytes)) | |
| 298 return false; | |
| 299 | |
| 300 base::StringPiece spki; | |
| 301 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) | |
| 302 return false; | |
| 303 | |
| 304 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(spki.data()), | |
| 305 spki.size(), spki_hash->data); | |
| 306 | |
| 307 return true; | |
| 308 } | |
| 309 | |
| 310 struct FingerprintsEqualPredicate { | 263 struct FingerprintsEqualPredicate { |
| 311 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : | 264 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : |
| 312 fingerprint_(fingerprint) {} | 265 fingerprint_(fingerprint) {} |
| 313 | 266 |
| 314 bool operator()(const SHA1Fingerprint& other) const { | 267 bool operator()(const SHA1Fingerprint& other) const { |
| 315 return fingerprint_.Equals(other); | 268 return fingerprint_.Equals(other); |
| 316 } | 269 } |
| 317 | 270 |
| 318 const SHA1Fingerprint& fingerprint_; | 271 const SHA1Fingerprint& fingerprint_; |
| 319 }; | 272 }; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 361 if (from_cert_chain.empty()) | 314 if (from_cert_chain.empty()) |
| 362 return false; | 315 return false; |
| 363 | 316 |
| 364 return IsBackupPinPresent(pins, from_cert_chain) && | 317 return IsBackupPinPresent(pins, from_cert_chain) && |
| 365 HashesIntersect(pins, from_cert_chain); | 318 HashesIntersect(pins, from_cert_chain); |
| 366 } | 319 } |
| 367 | 320 |
| 368 // "Public-Key-Pins" ":" | 321 // "Public-Key-Pins" ":" |
| 369 // "max-age" "=" delta-seconds ";" | 322 // "max-age" "=" delta-seconds ";" |
| 370 // "pin-" algo "=" base64 [ ";" ... ] | 323 // "pin-" algo "=" base64 [ ";" ... ] |
| 371 // | 324 bool TransportSecurityState::DomainState::ParsePinsHeader( |
| 372 // static | 325 const base::Time& now, |
| 373 bool TransportSecurityState::ParsePinsHeader(const std::string& value, | 326 const std::string& value, |
| 374 const SSLInfo& ssl_info, | 327 const SSLInfo& ssl_info) { |
| 375 DomainState* state) { | |
| 376 bool parsed_max_age = false; | 328 bool parsed_max_age = false; |
| 377 int max_age = 0; | 329 int max_age_candidate = 0; |
| 378 FingerprintVector pins; | 330 FingerprintVector pins; |
| 379 | 331 |
| 380 std::string source = value; | 332 std::string source = value; |
| 381 | 333 |
| 382 while (!source.empty()) { | 334 while (!source.empty()) { |
| 383 StringPair semicolon = Split(source, ';'); | 335 StringPair semicolon = Split(source, ';'); |
| 384 semicolon.first = Strip(semicolon.first); | 336 semicolon.first = Strip(semicolon.first); |
| 385 semicolon.second = Strip(semicolon.second); | 337 semicolon.second = Strip(semicolon.second); |
| 386 StringPair equals = Split(semicolon.first, '='); | 338 StringPair equals = Split(semicolon.first, '='); |
| 387 equals.first = Strip(equals.first); | 339 equals.first = Strip(equals.first); |
| 388 equals.second = Strip(equals.second); | 340 equals.second = Strip(equals.second); |
| 389 | 341 |
| 390 if (LowerCaseEqualsASCII(equals.first, "max-age")) { | 342 if (LowerCaseEqualsASCII(equals.first, "max-age")) { |
| 391 if (equals.second.empty() || | 343 if (equals.second.empty() || |
| 392 !MaxAgeToInt(equals.second.begin(), equals.second.end(), &max_age)) { | 344 !MaxAgeToInt(equals.second.begin(), equals.second.end(), |
| 345 &max_age_candidate)) { | |
| 393 return false; | 346 return false; |
| 394 } | 347 } |
| 395 if (max_age > kMaxHSTSAgeSecs) | 348 if (max_age_candidate > kMaxHSTSAgeSecs) |
| 396 max_age = kMaxHSTSAgeSecs; | 349 max_age_candidate = kMaxHSTSAgeSecs; |
| 397 parsed_max_age = true; | 350 parsed_max_age = true; |
| 398 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { | 351 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { |
| 399 if (!ParseAndAppendPin(equals.second, &pins)) | 352 if (!ParseAndAppendPin(equals.second, &pins)) |
| 400 return false; | 353 return false; |
| 401 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { | 354 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { |
| 402 // TODO(palmer) | 355 // TODO(palmer) |
| 403 } else { | 356 } else { |
| 404 // Silently ignore unknown directives for forward compatibility. | 357 // Silently ignore unknown directives for forward compatibility. |
| 405 } | 358 } |
| 406 | 359 |
| 407 source = semicolon.second; | 360 source = semicolon.second; |
| 408 } | 361 } |
| 409 | 362 |
| 410 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) | 363 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) |
| 411 return false; | 364 return false; |
| 412 | 365 |
| 413 state->max_age = max_age; | 366 dynamic_spki_hashes_expiry = |
| 414 state->dynamic_spki_hashes_expiry = | 367 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 415 base::Time::Now() + base::TimeDelta::FromSeconds(max_age); | |
| 416 | 368 |
| 417 state->dynamic_spki_hashes.clear(); | 369 dynamic_spki_hashes.clear(); |
| 418 if (max_age > 0) { | 370 if (max_age_candidate > 0) { |
| 419 for (FingerprintVector::const_iterator i = pins.begin(); | 371 for (FingerprintVector::const_iterator i = pins.begin(); |
| 420 i != pins.end(); i++) { | 372 i != pins.end(); ++i) { |
| 421 state->dynamic_spki_hashes.push_back(*i); | 373 dynamic_spki_hashes.push_back(*i); |
| 422 } | 374 } |
| 423 } | 375 } |
| 424 | 376 |
| 425 return true; | 377 return true; |
| 426 } | 378 } |
| 427 | 379 |
| 428 // "Strict-Transport-Security" ":" | 380 // "Strict-Transport-Security" ":" |
| 429 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] | 381 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] |
| 430 // | 382 bool TransportSecurityState::DomainState::ParseSTSHeader( |
| 431 // static | 383 const base::Time& now, |
| 432 bool TransportSecurityState::ParseHeader(const std::string& value, | 384 const std::string& value) { |
| 433 int* max_age, | |
| 434 bool* include_subdomains) { | |
| 435 DCHECK(max_age); | |
| 436 DCHECK(include_subdomains); | |
| 437 | |
| 438 int max_age_candidate = 0; | 385 int max_age_candidate = 0; |
| 439 | 386 |
| 440 enum ParserState { | 387 enum ParserState { |
| 441 START, | 388 START, |
| 442 AFTER_MAX_AGE_LABEL, | 389 AFTER_MAX_AGE_LABEL, |
| 443 AFTER_MAX_AGE_EQUALS, | 390 AFTER_MAX_AGE_EQUALS, |
| 444 AFTER_MAX_AGE, | 391 AFTER_MAX_AGE, |
| 445 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER, | 392 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER, |
| 446 AFTER_INCLUDE_SUBDOMAINS, | 393 AFTER_INCLUDE_SUBDOMAINS, |
| 447 } state = START; | 394 } state = START; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 504 } | 451 } |
| 505 } | 452 } |
| 506 | 453 |
| 507 // We've consumed all the input. Let's see what state we ended up in. | 454 // We've consumed all the input. Let's see what state we ended up in. |
| 508 switch (state) { | 455 switch (state) { |
| 509 case START: | 456 case START: |
| 510 case AFTER_MAX_AGE_LABEL: | 457 case AFTER_MAX_AGE_LABEL: |
| 511 case AFTER_MAX_AGE_EQUALS: | 458 case AFTER_MAX_AGE_EQUALS: |
| 512 return false; | 459 return false; |
| 513 case AFTER_MAX_AGE: | 460 case AFTER_MAX_AGE: |
| 514 *max_age = max_age_candidate; | 461 upgrade_expiry = |
| 515 *include_subdomains = false; | 462 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 463 include_subdomains = false; | |
| 464 upgrade_mode = MODE_FORCE_HTTPS; | |
| 516 return true; | 465 return true; |
| 517 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: | 466 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: |
| 518 return false; | 467 return false; |
| 519 case AFTER_INCLUDE_SUBDOMAINS: | 468 case AFTER_INCLUDE_SUBDOMAINS: |
| 520 *max_age = max_age_candidate; | 469 upgrade_expiry = |
| 521 *include_subdomains = true; | 470 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 471 include_subdomains = true; | |
| 472 upgrade_mode = MODE_FORCE_HTTPS; | |
| 522 return true; | 473 return true; |
| 523 default: | 474 default: |
| 524 NOTREACHED(); | 475 NOTREACHED(); |
| 525 return false; | 476 return false; |
| 526 } | 477 } |
| 527 } | 478 } |
| 528 | 479 |
| 529 // Side pinning and superfluous certificates: | |
| 530 // | |
| 531 // In SSLClientSocketNSS::DoVerifyCertComplete we look for certificates with a | |
| 532 // Subject of CN=meta. When we find one we'll currently try and parse side | |
| 533 // pinned key from it. | |
| 534 // | |
| 535 // A side pin is a key which can be pinned to, but also can be kept offline and | |
| 536 // still held by the site owner. The CN=meta certificate is just a backwards | |
| 537 // compatiable method of carrying a lump of bytes to the client. (We could use | |
| 538 // a TLS extension just as well, but it's a lot easier for admins to add extra | |
| 539 // certificates to the chain.) | |
| 540 | |
| 541 // A TagMap represents the simple key-value structure that we use. Keys are | |
| 542 // 32-bit ints. Values are byte strings. | |
| 543 typedef std::map<uint32, base::StringPiece> TagMap; | |
| 544 | |
| 545 // ParseTags parses a list of key-value pairs from |in| to |out| and advances | |
| 546 // |in| past the data. The key-value pair data is: | |
| 547 // u16le num_tags | |
| 548 // u32le tag[num_tags] | |
| 549 // u16le lengths[num_tags] | |
| 550 // ...data... | |
| 551 static bool ParseTags(base::StringPiece* in, TagMap *out) { | |
| 552 // Many part of Chrome already assume little-endian. This is just to help | |
| 553 // anyone who should try to port it in the future. | |
| 554 #if defined(__BYTE_ORDER) | |
| 555 // Linux check | |
| 556 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian); | |
| 557 #elif defined(__BIG_ENDIAN__) | |
| 558 // Mac check | |
| 559 #error assumes little endian | |
| 560 #endif | |
| 561 | |
| 562 uint16 num_tags_16; | |
| 563 if (in->size() < sizeof(num_tags_16)) | |
| 564 return false; | |
| 565 | |
| 566 memcpy(&num_tags_16, in->data(), sizeof(num_tags_16)); | |
| 567 in->remove_prefix(sizeof(num_tags_16)); | |
| 568 unsigned num_tags = num_tags_16; | |
| 569 | |
| 570 if (in->size() < 6 * num_tags) | |
| 571 return false; | |
| 572 | |
| 573 const uint32* tags = reinterpret_cast<const uint32*>(in->data()); | |
| 574 const uint16* lens = reinterpret_cast<const uint16*>( | |
| 575 in->data() + 4*num_tags); | |
| 576 in->remove_prefix(6*num_tags); | |
| 577 | |
| 578 uint32 prev_tag = 0; | |
| 579 for (unsigned i = 0; i < num_tags; i++) { | |
| 580 size_t len = lens[i]; | |
| 581 uint32 tag = tags[i]; | |
| 582 | |
| 583 if (in->size() < len) | |
| 584 return false; | |
| 585 // tags must be in ascending order. | |
| 586 if (i > 0 && prev_tag >= tag) | |
| 587 return false; | |
| 588 (*out)[tag] = base::StringPiece(in->data(), len); | |
| 589 in->remove_prefix(len); | |
| 590 prev_tag = tag; | |
| 591 } | |
| 592 | |
| 593 return true; | |
| 594 } | |
| 595 | |
| 596 // GetTag extracts the data associated with |tag| in |tags|. | |
| 597 static bool GetTag(uint32 tag, const TagMap& tags, base::StringPiece* out) { | |
| 598 TagMap::const_iterator i = tags.find(tag); | |
| 599 if (i == tags.end()) | |
| 600 return false; | |
| 601 | |
| 602 *out = i->second; | |
| 603 return true; | |
| 604 } | |
| 605 | |
| 606 // kP256SubjectPublicKeyInfoPrefix can be prepended onto a P256 elliptic curve | |
| 607 // point in X9.62 format in order to make a valid SubjectPublicKeyInfo. The | |
| 608 // ASN.1 interpretation of these bytes is: | |
| 609 // | |
| 610 // 0:d=0 hl=2 l= 89 cons: SEQUENCE | |
| 611 // 2:d=1 hl=2 l= 19 cons: SEQUENCE | |
| 612 // 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey | |
| 613 // 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 | |
| 614 // 23:d=1 hl=2 l= 66 prim: BIT STRING | |
| 615 static const uint8 kP256SubjectPublicKeyInfoPrefix[] = { | |
| 616 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, | |
| 617 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, | |
| 618 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, | |
| 619 0x42, 0x00, | |
| 620 }; | |
| 621 | |
| 622 // VerifySignature returns true iff |sig| is a valid signature of | |
| 623 // |hash| by |pubkey|. The actual implementation is crypto library | |
| 624 // specific. | |
| 625 static bool VerifySignature(const base::StringPiece& pubkey, | |
| 626 const base::StringPiece& sig, | |
| 627 const base::StringPiece& hash); | |
| 628 | |
| 629 #if defined(USE_OPENSSL) | |
| 630 | |
| 631 static EVP_PKEY* DecodeX962P256PublicKey( | |
| 632 const base::StringPiece& pubkey_bytes) { | |
| 633 // The public key is an X9.62 encoded P256 point. | |
| 634 if (pubkey_bytes.size() != 1 + 2*32) | |
| 635 return NULL; | |
| 636 | |
| 637 std::string pubkey_spki( | |
| 638 reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix), | |
| 639 sizeof(kP256SubjectPublicKeyInfoPrefix)); | |
| 640 pubkey_spki += pubkey_bytes.as_string(); | |
| 641 | |
| 642 EVP_PKEY* ret = NULL; | |
| 643 const unsigned char* der_pubkey = | |
| 644 reinterpret_cast<const unsigned char*>(pubkey_spki.data()); | |
| 645 d2i_PUBKEY(&ret, &der_pubkey, pubkey_spki.size()); | |
| 646 return ret; | |
| 647 } | |
| 648 | |
| 649 static bool VerifySignature(const base::StringPiece& pubkey, | |
| 650 const base::StringPiece& sig, | |
| 651 const base::StringPiece& hash) { | |
| 652 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> secpubkey( | |
| 653 DecodeX962P256PublicKey(pubkey)); | |
| 654 if (!secpubkey.get()) | |
| 655 return false; | |
| 656 | |
| 657 | |
| 658 crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ec_key( | |
| 659 EVP_PKEY_get1_EC_KEY(secpubkey.get())); | |
| 660 if (!ec_key.get()) | |
| 661 return false; | |
| 662 | |
| 663 return ECDSA_verify(0, reinterpret_cast<const unsigned char*>(hash.data()), | |
| 664 hash.size(), | |
| 665 reinterpret_cast<const unsigned char*>(sig.data()), | |
| 666 sig.size(), ec_key.get()) == 1; | |
| 667 } | |
| 668 | |
| 669 #else | |
| 670 | |
| 671 // DecodeX962P256PublicKey parses an uncompressed, X9.62 format, P256 elliptic | |
| 672 // curve point from |pubkey_bytes| and returns it as a SECKEYPublicKey. | |
| 673 static SECKEYPublicKey* DecodeX962P256PublicKey( | |
| 674 const base::StringPiece& pubkey_bytes) { | |
| 675 // The public key is an X9.62 encoded P256 point. | |
| 676 if (pubkey_bytes.size() != 1 + 2*32) | |
| 677 return NULL; | |
| 678 | |
| 679 std::string pubkey_spki( | |
| 680 reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix), | |
| 681 sizeof(kP256SubjectPublicKeyInfoPrefix)); | |
| 682 pubkey_spki += pubkey_bytes.as_string(); | |
| 683 | |
| 684 SECItem der; | |
| 685 memset(&der, 0, sizeof(der)); | |
| 686 der.data = reinterpret_cast<uint8*>(const_cast<char*>(pubkey_spki.data())); | |
| 687 der.len = pubkey_spki.size(); | |
| 688 | |
| 689 CERTSubjectPublicKeyInfo* spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der); | |
| 690 if (!spki) | |
| 691 return NULL; | |
| 692 SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); | |
| 693 SECKEY_DestroySubjectPublicKeyInfo(spki); | |
| 694 | |
| 695 return public_key; | |
| 696 } | |
| 697 | |
| 698 static bool VerifySignature(const base::StringPiece& pubkey, | |
| 699 const base::StringPiece& sig, | |
| 700 const base::StringPiece& hash) { | |
| 701 SECKEYPublicKey* secpubkey = DecodeX962P256PublicKey(pubkey); | |
| 702 if (!secpubkey) | |
| 703 return false; | |
| 704 | |
| 705 SECItem sigitem; | |
| 706 memset(&sigitem, 0, sizeof(sigitem)); | |
| 707 sigitem.data = reinterpret_cast<uint8*>(const_cast<char*>(sig.data())); | |
| 708 sigitem.len = sig.size(); | |
| 709 | |
| 710 // |decoded_sigitem| is newly allocated, as is the data that it points to. | |
| 711 SECItem* decoded_sigitem = DSAU_DecodeDerSigToLen( | |
| 712 &sigitem, SECKEY_SignatureLen(secpubkey)); | |
| 713 | |
| 714 if (!decoded_sigitem) { | |
| 715 SECKEY_DestroyPublicKey(secpubkey); | |
| 716 return false; | |
| 717 } | |
| 718 | |
| 719 SECItem hashitem; | |
| 720 memset(&hashitem, 0, sizeof(hashitem)); | |
| 721 hashitem.data = reinterpret_cast<unsigned char*>( | |
| 722 const_cast<char*>(hash.data())); | |
| 723 hashitem.len = hash.size(); | |
| 724 | |
| 725 SECStatus rv = PK11_Verify(secpubkey, decoded_sigitem, &hashitem, NULL); | |
| 726 SECKEY_DestroyPublicKey(secpubkey); | |
| 727 SECITEM_FreeItem(decoded_sigitem, PR_TRUE); | |
| 728 return rv == SECSuccess; | |
| 729 } | |
| 730 | |
| 731 #endif // !defined(USE_OPENSSL) | |
| 732 | |
| 733 // These are the tag values that we use. Tags are little-endian on the wire and | |
| 734 // these values correspond to the ASCII of the name. | |
| 735 static const uint32 kTagALGO = 0x4f474c41; | |
| 736 static const uint32 kTagP256 = 0x36353250; | |
| 737 static const uint32 kTagPUBK = 0x4b425550; | |
| 738 static const uint32 kTagSIG = 0x474953; | |
| 739 static const uint32 kTagSPIN = 0x4e495053; | |
| 740 | |
| 741 // static | |
| 742 bool TransportSecurityState::ParseSidePin( | |
| 743 const base::StringPiece& leaf_spki, | |
| 744 const base::StringPiece& in_side_info, | |
| 745 FingerprintVector* out_pub_key_hash) { | |
| 746 base::StringPiece side_info(in_side_info); | |
| 747 | |
| 748 TagMap outer; | |
| 749 if (!ParseTags(&side_info, &outer)) | |
| 750 return false; | |
| 751 // trailing data is not allowed | |
| 752 if (side_info.size()) | |
| 753 return false; | |
| 754 | |
| 755 base::StringPiece side_pin_bytes; | |
| 756 if (!GetTag(kTagSPIN, outer, &side_pin_bytes)) | |
| 757 return false; | |
| 758 | |
| 759 bool have_parsed_a_key = false; | |
| 760 uint8 leaf_spki_hash[crypto::kSHA256Length]; | |
| 761 bool have_leaf_spki_hash = false; | |
| 762 | |
| 763 while (side_pin_bytes.size() > 0) { | |
| 764 TagMap side_pin; | |
| 765 if (!ParseTags(&side_pin_bytes, &side_pin)) | |
| 766 return false; | |
| 767 | |
| 768 base::StringPiece algo, pubkey, sig; | |
| 769 if (!GetTag(kTagALGO, side_pin, &algo) || | |
| 770 !GetTag(kTagPUBK, side_pin, &pubkey) || | |
| 771 !GetTag(kTagSIG, side_pin, &sig)) { | |
| 772 return false; | |
| 773 } | |
| 774 | |
| 775 if (algo.size() != sizeof(kTagP256) || | |
| 776 0 != memcmp(algo.data(), &kTagP256, sizeof(kTagP256))) { | |
| 777 // We don't support anything but P256 at the moment. | |
| 778 continue; | |
| 779 } | |
| 780 | |
| 781 if (!have_leaf_spki_hash) { | |
| 782 crypto::SHA256HashString( | |
| 783 leaf_spki.as_string(), leaf_spki_hash, sizeof(leaf_spki_hash)); | |
| 784 have_leaf_spki_hash = true; | |
| 785 } | |
| 786 | |
| 787 if (VerifySignature(pubkey, sig, base::StringPiece( | |
| 788 reinterpret_cast<const char*>(leaf_spki_hash), | |
| 789 sizeof(leaf_spki_hash)))) { | |
| 790 SHA1Fingerprint fpr; | |
| 791 base::SHA1HashBytes( | |
| 792 reinterpret_cast<const uint8*>(pubkey.data()), | |
| 793 pubkey.size(), | |
| 794 fpr.data); | |
| 795 out_pub_key_hash->push_back(fpr); | |
| 796 have_parsed_a_key = true; | |
| 797 } | |
| 798 } | |
| 799 | |
| 800 return have_parsed_a_key; | |
| 801 } | |
| 802 | |
| 803 // This function converts the binary hashes, which we store in | |
| 804 // |enabled_hosts_|, to a base64 string which we can include in a JSON file. | |
| 805 static std::string HashedDomainToExternalString(const std::string& hashed) { | |
| 806 std::string out; | |
| 807 CHECK(base::Base64Encode(hashed, &out)); | |
| 808 return out; | |
| 809 } | |
| 810 | |
| 811 // This inverts |HashedDomainToExternalString|, above. It turns an external | |
| 812 // string (from a JSON file) into an internal (binary) string. | |
| 813 static std::string ExternalStringToHashedDomain(const std::string& external) { | |
| 814 std::string out; | |
| 815 if (!base::Base64Decode(external, &out) || | |
| 816 out.size() != crypto::kSHA256Length) { | |
| 817 return std::string(); | |
| 818 } | |
| 819 | |
| 820 return out; | |
| 821 } | |
| 822 | |
| 823 static ListValue* SPKIHashesToListValue(const FingerprintVector& hashes) { | |
| 824 ListValue* pins = new ListValue; | |
| 825 | |
| 826 for (FingerprintVector::const_iterator i = hashes.begin(); | |
| 827 i != hashes.end(); ++i) { | |
| 828 std::string hash_str(reinterpret_cast<const char*>(i->data), | |
| 829 sizeof(i->data)); | |
| 830 std::string b64; | |
| 831 base::Base64Encode(hash_str, &b64); | |
| 832 pins->Append(new StringValue("sha1/" + b64)); | |
| 833 } | |
| 834 | |
| 835 return pins; | |
| 836 } | |
| 837 | |
| 838 bool TransportSecurityState::Serialise(std::string* output) { | |
| 839 DCHECK(CalledOnValidThread()); | |
| 840 | |
| 841 DictionaryValue toplevel; | |
| 842 base::Time now = base::Time::Now(); | |
| 843 for (std::map<std::string, DomainState>::const_iterator | |
| 844 i = enabled_hosts_.begin(); i != enabled_hosts_.end(); ++i) { | |
| 845 DictionaryValue* state = new DictionaryValue; | |
| 846 state->SetBoolean("include_subdomains", i->second.include_subdomains); | |
| 847 state->SetDouble("created", i->second.created.ToDoubleT()); | |
| 848 state->SetDouble("expiry", i->second.expiry.ToDoubleT()); | |
| 849 state->SetDouble("dynamic_spki_hashes_expiry", | |
| 850 i->second.dynamic_spki_hashes_expiry.ToDoubleT()); | |
| 851 | |
| 852 switch (i->second.mode) { | |
| 853 case DomainState::MODE_STRICT: | |
| 854 state->SetString("mode", "strict"); | |
| 855 break; | |
| 856 case DomainState::MODE_SPDY_ONLY: | |
| 857 state->SetString("mode", "spdy-only"); | |
| 858 break; | |
| 859 case DomainState::MODE_PINNING_ONLY: | |
| 860 state->SetString("mode", "pinning-only"); | |
| 861 break; | |
| 862 default: | |
| 863 NOTREACHED() << "DomainState with unknown mode"; | |
| 864 delete state; | |
| 865 continue; | |
| 866 } | |
| 867 | |
| 868 state->Set("preloaded_spki_hashes", | |
| 869 SPKIHashesToListValue(i->second.preloaded_spki_hashes)); | |
| 870 | |
| 871 if (now < i->second.dynamic_spki_hashes_expiry) { | |
| 872 state->Set("dynamic_spki_hashes", | |
| 873 SPKIHashesToListValue(i->second.dynamic_spki_hashes)); | |
| 874 } | |
| 875 | |
| 876 toplevel.Set(HashedDomainToExternalString(i->first), state); | |
| 877 } | |
| 878 | |
| 879 base::JSONWriter::WriteWithOptions(&toplevel, | |
| 880 base::JSONWriter::OPTIONS_PRETTY_PRINT, | |
| 881 output); | |
| 882 return true; | |
| 883 } | |
| 884 | |
| 885 bool TransportSecurityState::LoadEntries(const std::string& input, | |
| 886 bool* dirty) { | |
| 887 DCHECK(CalledOnValidThread()); | |
| 888 | |
| 889 enabled_hosts_.clear(); | |
| 890 return Deserialise(input, dirty, &enabled_hosts_); | |
| 891 } | |
| 892 | |
| 893 static bool AddHash(const std::string& type_and_base64, | 480 static bool AddHash(const std::string& type_and_base64, |
| 894 FingerprintVector* out) { | 481 FingerprintVector* out) { |
| 895 SHA1Fingerprint hash; | 482 SHA1Fingerprint hash; |
| 896 | 483 |
| 897 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) | 484 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) |
| 898 return false; | 485 return false; |
| 899 | 486 |
| 900 out->push_back(hash); | 487 out->push_back(hash); |
| 901 return true; | 488 return true; |
| 902 } | 489 } |
| 903 | 490 |
| 904 static void SPKIHashesFromListValue(FingerprintVector* hashes, | |
| 905 const ListValue& pins) { | |
| 906 size_t num_pins = pins.GetSize(); | |
| 907 for (size_t i = 0; i < num_pins; ++i) { | |
| 908 std::string type_and_base64; | |
| 909 if (pins.GetString(i, &type_and_base64)) | |
| 910 AddHash(type_and_base64, hashes); | |
| 911 } | |
| 912 } | |
| 913 | |
| 914 // static | |
| 915 bool TransportSecurityState::Deserialise( | |
| 916 const std::string& input, | |
| 917 bool* dirty, | |
| 918 std::map<std::string, DomainState>* out) { | |
| 919 scoped_ptr<Value> value(base::JSONReader::Read(input)); | |
| 920 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) | |
| 921 return false; | |
| 922 | |
| 923 DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get()); | |
| 924 const base::Time current_time(base::Time::Now()); | |
| 925 bool dirtied = false; | |
| 926 | |
| 927 for (DictionaryValue::key_iterator i = dict_value->begin_keys(); | |
| 928 i != dict_value->end_keys(); ++i) { | |
| 929 DictionaryValue* state; | |
| 930 if (!dict_value->GetDictionaryWithoutPathExpansion(*i, &state)) | |
| 931 continue; | |
| 932 | |
| 933 bool include_subdomains; | |
| 934 std::string mode_string; | |
| 935 double created; | |
| 936 double expiry; | |
| 937 double dynamic_spki_hashes_expiry = 0.0; | |
| 938 | |
| 939 if (!state->GetBoolean("include_subdomains", &include_subdomains) || | |
| 940 !state->GetString("mode", &mode_string) || | |
| 941 !state->GetDouble("expiry", &expiry)) { | |
| 942 continue; | |
| 943 } | |
| 944 | |
| 945 // Don't fail if this key is not present. | |
| 946 (void) state->GetDouble("dynamic_spki_hashes_expiry", | |
| 947 &dynamic_spki_hashes_expiry); | |
| 948 | |
| 949 ListValue* pins_list = NULL; | |
| 950 FingerprintVector preloaded_spki_hashes; | |
| 951 if (state->GetList("preloaded_spki_hashes", &pins_list)) | |
| 952 SPKIHashesFromListValue(&preloaded_spki_hashes, *pins_list); | |
| 953 | |
| 954 FingerprintVector dynamic_spki_hashes; | |
| 955 if (state->GetList("dynamic_spki_hashes", &pins_list)) | |
| 956 SPKIHashesFromListValue(&dynamic_spki_hashes, *pins_list); | |
| 957 | |
| 958 DomainState::Mode mode; | |
| 959 if (mode_string == "strict") { | |
| 960 mode = DomainState::MODE_STRICT; | |
| 961 } else if (mode_string == "spdy-only") { | |
| 962 mode = DomainState::MODE_SPDY_ONLY; | |
| 963 } else if (mode_string == "pinning-only") { | |
| 964 mode = DomainState::MODE_PINNING_ONLY; | |
| 965 } else { | |
| 966 LOG(WARNING) << "Unknown TransportSecurityState mode string found: " | |
| 967 << mode_string; | |
| 968 continue; | |
| 969 } | |
| 970 | |
| 971 base::Time expiry_time = base::Time::FromDoubleT(expiry); | |
| 972 base::Time dynamic_spki_hashes_expiry_time = | |
| 973 base::Time::FromDoubleT(dynamic_spki_hashes_expiry); | |
| 974 base::Time created_time; | |
| 975 if (state->GetDouble("created", &created)) { | |
| 976 created_time = base::Time::FromDoubleT(created); | |
| 977 } else { | |
| 978 // We're migrating an old entry with no creation date. Make sure we | |
| 979 // write the new date back in a reasonable time frame. | |
| 980 dirtied = true; | |
| 981 created_time = base::Time::Now(); | |
| 982 } | |
| 983 | |
| 984 if (expiry_time <= current_time && | |
| 985 dynamic_spki_hashes_expiry_time <= current_time) { | |
| 986 // Make sure we dirty the state if we drop an entry. | |
| 987 dirtied = true; | |
| 988 continue; | |
| 989 } | |
| 990 | |
| 991 std::string hashed = ExternalStringToHashedDomain(*i); | |
| 992 if (hashed.empty()) { | |
| 993 dirtied = true; | |
| 994 continue; | |
| 995 } | |
| 996 | |
| 997 DomainState new_state; | |
| 998 new_state.mode = mode; | |
| 999 new_state.created = created_time; | |
| 1000 new_state.expiry = expiry_time; | |
| 1001 new_state.include_subdomains = include_subdomains; | |
| 1002 new_state.preloaded_spki_hashes = preloaded_spki_hashes; | |
| 1003 new_state.dynamic_spki_hashes = dynamic_spki_hashes; | |
| 1004 new_state.dynamic_spki_hashes_expiry = dynamic_spki_hashes_expiry_time; | |
| 1005 (*out)[hashed] = new_state; | |
| 1006 } | |
| 1007 | |
| 1008 *dirty = dirtied; | |
| 1009 return true; | |
| 1010 } | |
| 1011 | |
| 1012 TransportSecurityState::~TransportSecurityState() { | 491 TransportSecurityState::~TransportSecurityState() { |
| 1013 } | 492 } |
|
Ryan Sleevi
2012/04/26 19:21:12
nit: move } to line 491
palmer
2012/04/27 23:52:34
Done.
| |
| 1014 | 493 |
| 1015 void TransportSecurityState::DirtyNotify() { | 494 void TransportSecurityState::DirtyNotify() { |
| 1016 DCHECK(CalledOnValidThread()); | 495 DCHECK(CalledOnValidThread()); |
| 1017 | 496 |
| 1018 if (delegate_) | 497 if (delegate_) |
| 1019 delegate_->StateIsDirty(this); | 498 delegate_->StateIsDirty(this); |
| 1020 } | 499 } |
| 1021 | 500 |
| 1022 // static | 501 // static |
| 1023 std::string TransportSecurityState::CanonicalizeHost(const std::string& host) { | 502 std::string TransportSecurityState::CanonicalizeHost(const std::string& host) { |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1115 for (size_t j = 0; j < num_entries; j++) { | 594 for (size_t j = 0; j < num_entries; j++) { |
| 1116 if (entries[j].length == canonicalized_host.size() - i && | 595 if (entries[j].length == canonicalized_host.size() - i && |
| 1117 memcmp(entries[j].dns_name, &canonicalized_host[i], | 596 memcmp(entries[j].dns_name, &canonicalized_host[i], |
| 1118 entries[j].length) == 0) { | 597 entries[j].length) == 0) { |
| 1119 if (!entries[j].include_subdomains && i != 0) { | 598 if (!entries[j].include_subdomains && i != 0) { |
| 1120 *ret = false; | 599 *ret = false; |
| 1121 } else { | 600 } else { |
| 1122 out->include_subdomains = entries[j].include_subdomains; | 601 out->include_subdomains = entries[j].include_subdomains; |
| 1123 *ret = true; | 602 *ret = true; |
| 1124 if (!entries[j].https_required) | 603 if (!entries[j].https_required) |
| 1125 out->mode = TransportSecurityState::DomainState::MODE_PINNING_ONLY; | 604 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; |
| 1126 if (entries[j].pins.required_hashes) { | 605 if (entries[j].pins.required_hashes) { |
| 1127 const char* const* hash = entries[j].pins.required_hashes; | 606 const char* const* hash = entries[j].pins.required_hashes; |
| 1128 while (*hash) { | 607 while (*hash) { |
| 1129 bool ok = AddHash(*hash, &out->preloaded_spki_hashes); | 608 bool ok = AddHash(*hash, &out->static_spki_hashes); |
| 1130 DCHECK(ok) << " failed to parse " << *hash; | 609 DCHECK(ok) << " failed to parse " << *hash; |
| 1131 hash++; | 610 hash++; |
| 1132 } | 611 } |
| 1133 } | 612 } |
| 1134 if (entries[j].pins.excluded_hashes) { | 613 if (entries[j].pins.excluded_hashes) { |
| 1135 const char* const* hash = entries[j].pins.excluded_hashes; | 614 const char* const* hash = entries[j].pins.excluded_hashes; |
| 1136 while (*hash) { | 615 while (*hash) { |
| 1137 bool ok = AddHash(*hash, &out->bad_preloaded_spki_hashes); | 616 bool ok = AddHash(*hash, &out->bad_static_spki_hashes); |
| 1138 DCHECK(ok) << " failed to parse " << *hash; | 617 DCHECK(ok) << " failed to parse " << *hash; |
| 1139 hash++; | 618 hash++; |
| 1140 } | 619 } |
| 1141 } | 620 } |
| 1142 } | 621 } |
| 1143 return true; | 622 return true; |
| 1144 } | 623 } |
| 1145 } | 624 } |
| 1146 return false; | 625 return false; |
| 1147 } | 626 } |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 1170 return entry; | 649 return entry; |
| 1171 } | 650 } |
| 1172 } | 651 } |
| 1173 } | 652 } |
| 1174 | 653 |
| 1175 return NULL; | 654 return NULL; |
| 1176 } | 655 } |
| 1177 | 656 |
| 1178 // static | 657 // static |
| 1179 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, | 658 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, |
| 1180 bool sni_available) { | 659 bool sni_enabled) { |
| 1181 std::string canonicalized_host = CanonicalizeHost(host); | 660 std::string canonicalized_host = CanonicalizeHost(host); |
| 1182 const struct HSTSPreload* entry = | 661 const struct HSTSPreload* entry = |
| 1183 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 662 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
| 1184 | 663 |
| 1185 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 664 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
| 1186 return true; | 665 return true; |
| 1187 | 666 |
| 1188 if (sni_available) { | 667 if (sni_enabled) { |
| 1189 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 668 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, |
| 1190 kNumPreloadedSNISTS); | 669 kNumPreloadedSNISTS); |
| 1191 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 670 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
| 1192 return true; | 671 return true; |
| 1193 } | 672 } |
| 1194 | 673 |
| 1195 return false; | 674 return false; |
| 1196 } | 675 } |
| 1197 | 676 |
| 1198 // static | 677 // static |
| 1199 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { | 678 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { |
| 1200 std::string canonicalized_host = CanonicalizeHost(host); | 679 std::string canonicalized_host = CanonicalizeHost(host); |
| 1201 | 680 |
| 1202 const struct HSTSPreload* entry = | 681 const struct HSTSPreload* entry = |
| 1203 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 682 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
| 1204 | 683 |
| 1205 if (!entry) { | 684 if (!entry) { |
| 1206 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 685 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, |
| 1207 kNumPreloadedSNISTS); | 686 kNumPreloadedSNISTS); |
| 1208 } | 687 } |
| 1209 | 688 |
| 1210 DCHECK(entry); | 689 DCHECK(entry); |
| 1211 DCHECK(entry->pins.required_hashes); | 690 DCHECK(entry->pins.required_hashes); |
| 1212 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | 691 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); |
| 1213 | 692 |
| 1214 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | 693 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", |
| 1215 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | 694 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); |
| 1216 } | 695 } |
| 1217 | 696 |
| 1218 // IsPreloadedSTS returns true if the canonicalized hostname should always be | 697 bool TransportSecurityState::GetStaticDomainState( |
| 1219 // considered to have STS enabled. | |
| 1220 bool TransportSecurityState::IsPreloadedSTS( | |
| 1221 const std::string& canonicalized_host, | 698 const std::string& canonicalized_host, |
| 1222 bool sni_available, | 699 bool sni_enabled, |
| 1223 DomainState* out) { | 700 DomainState* out) { |
| 1224 DCHECK(CalledOnValidThread()); | 701 DCHECK(CalledOnValidThread()); |
| 1225 | 702 |
| 1226 out->preloaded = true; | 703 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
| 1227 out->mode = DomainState::MODE_STRICT; | |
| 1228 out->include_subdomains = false; | 704 out->include_subdomains = false; |
| 1229 | 705 |
| 1230 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 706 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
| 1231 std::string host_sub_chunk(&canonicalized_host[i], | 707 std::string host_sub_chunk(&canonicalized_host[i], |
| 1232 canonicalized_host.size() - i); | 708 canonicalized_host.size() - i); |
| 1233 out->domain = DNSDomainToString(host_sub_chunk); | 709 out->domain = DNSDomainToString(host_sub_chunk); |
| 1234 std::string hashed_host(HashHost(host_sub_chunk)); | 710 std::string hashed_host(HashHost(host_sub_chunk)); |
| 1235 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { | 711 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { |
| 1236 *out = forced_hosts_[hashed_host]; | 712 *out = forced_hosts_[hashed_host]; |
| 1237 out->domain = DNSDomainToString(host_sub_chunk); | 713 out->domain = DNSDomainToString(host_sub_chunk); |
| 1238 out->preloaded = true; | |
| 1239 return true; | 714 return true; |
| 1240 } | 715 } |
| 1241 bool ret; | 716 bool ret; |
| 1242 if (HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, | 717 if (HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, |
| 1243 &ret)) { | 718 &ret)) { |
| 1244 return ret; | 719 return ret; |
| 1245 } | 720 } |
| 1246 if (sni_available && | 721 if (sni_enabled && |
| 1247 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, | 722 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, |
| 1248 out, &ret)) { | 723 out, &ret)) { |
| 1249 return ret; | 724 return ret; |
| 1250 } | 725 } |
| 1251 } | 726 } |
| 1252 | 727 |
| 1253 return false; | 728 return false; |
| 1254 } | 729 } |
| 1255 | 730 |
| 1256 static std::string HashesToBase64String( | 731 static std::string HashesToBase64String( |
| 1257 const FingerprintVector& hashes) { | 732 const FingerprintVector& hashes) { |
| 1258 std::vector<std::string> hashes_strs; | 733 std::vector<std::string> hashes_strs; |
| 1259 for (FingerprintVector::const_iterator | 734 for (FingerprintVector::const_iterator |
| 1260 i = hashes.begin(); i != hashes.end(); i++) { | 735 i = hashes.begin(); i != hashes.end(); i++) { |
| 1261 std::string s; | 736 std::string s; |
| 1262 const std::string hash_str(reinterpret_cast<const char*>(i->data), | 737 const std::string hash_str(reinterpret_cast<const char*>(i->data), |
| 1263 sizeof(i->data)); | 738 sizeof(i->data)); |
| 1264 base::Base64Encode(hash_str, &s); | 739 base::Base64Encode(hash_str, &s); |
| 1265 hashes_strs.push_back(s); | 740 hashes_strs.push_back(s); |
| 1266 } | 741 } |
| 1267 | 742 |
| 1268 return JoinString(hashes_strs, ','); | 743 return JoinString(hashes_strs, ','); |
| 1269 } | 744 } |
| 1270 | 745 |
| 1271 TransportSecurityState::DomainState::DomainState() | 746 TransportSecurityState::DomainState::DomainState() |
| 1272 : mode(MODE_STRICT), | 747 : upgrade_mode(MODE_FORCE_HTTPS), |
| 1273 created(base::Time::Now()), | 748 created(base::Time::Now()), |
| 1274 include_subdomains(false), | 749 include_subdomains(false) { |
| 1275 preloaded(false) { | |
| 1276 } | 750 } |
| 1277 | 751 |
| 1278 TransportSecurityState::DomainState::~DomainState() { | 752 TransportSecurityState::DomainState::~DomainState() { |
| 1279 } | 753 } |
| 1280 | 754 |
| 1281 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( | 755 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( |
| 1282 const FingerprintVector& hashes) { | 756 const FingerprintVector& hashes) const { |
| 1283 | 757 if (HashesIntersect(bad_static_spki_hashes, hashes)) { |
| 1284 if (HashesIntersect(bad_preloaded_spki_hashes, hashes)) { | |
| 1285 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 758 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
| 1286 << ". Validated chain: " << HashesToBase64String(hashes) | 759 << ". Validated chain: " << HashesToBase64String(hashes) |
| 1287 << ", matches one or more bad hashes: " | 760 << ", matches one or more bad hashes: " |
| 1288 << HashesToBase64String(bad_preloaded_spki_hashes); | 761 << HashesToBase64String(bad_static_spki_hashes); |
| 1289 return false; | 762 return false; |
| 1290 } | 763 } |
| 1291 | 764 |
| 1292 if (!(dynamic_spki_hashes.empty() && preloaded_spki_hashes.empty()) && | 765 if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) && |
| 1293 !HashesIntersect(dynamic_spki_hashes, hashes) && | 766 !HashesIntersect(dynamic_spki_hashes, hashes) && |
| 1294 !HashesIntersect(preloaded_spki_hashes, hashes)) { | 767 !HashesIntersect(static_spki_hashes, hashes)) { |
| 1295 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 768 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
| 1296 << ". Validated chain: " << HashesToBase64String(hashes) | 769 << ". Validated chain: " << HashesToBase64String(hashes) |
| 1297 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) | 770 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) |
| 1298 << " or: " << HashesToBase64String(preloaded_spki_hashes); | 771 << " or: " << HashesToBase64String(static_spki_hashes); |
| 1299 | 772 |
| 1300 return false; | 773 return false; |
| 1301 } | 774 } |
| 1302 | 775 |
| 1303 return true; | 776 return true; |
| 1304 } | 777 } |
| 1305 | 778 |
| 1306 bool TransportSecurityState::DomainState::IsMoreStrict( | 779 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const { |
| 1307 const TransportSecurityState::DomainState& other) { | 780 return upgrade_mode == MODE_FORCE_HTTPS; |
| 1308 if (this->dynamic_spki_hashes.empty() && !other.dynamic_spki_hashes.empty()) | |
| 1309 return false; | |
| 1310 | |
| 1311 if (!this->include_subdomains && other.include_subdomains) | |
| 1312 return false; | |
| 1313 | |
| 1314 return true; | |
| 1315 } | 781 } |
| 1316 | 782 |
| 1317 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() | 783 bool TransportSecurityState::DomainState::HasPins() const { |
| 1318 const { | 784 return static_spki_hashes.size() > 0 || |
| 1319 return mode == MODE_STRICT; | 785 bad_static_spki_hashes.size() > 0 || |
| 786 dynamic_spki_hashes.size() > 0; | |
| 1320 } | 787 } |
| 1321 | 788 |
| 1322 } // namespace | 789 } // namespace |
| OLD | NEW |