| 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/public_key_hashes.h" | 35 #include "net/base/public_key_hashes.h" |
| 39 #include "net/base/ssl_info.h" | 36 #include "net/base/ssl_info.h" |
| 40 #include "net/base/x509_certificate.h" | 37 #include "net/base/x509_certificate.h" |
| 41 #include "net/http/http_util.h" | 38 #include "net/http/http_util.h" |
| 42 | 39 |
| 43 #if defined(USE_OPENSSL) | 40 #if defined(USE_OPENSSL) |
| 44 #include "crypto/openssl_util.h" | 41 #include "crypto/openssl_util.h" |
| 45 #endif | 42 #endif |
| 46 | 43 |
| 47 namespace net { | 44 namespace net { |
| 48 | 45 |
| 49 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year | 46 const long int TransportSecurityState::kMaxHSTSAgeSecs = 86400 * 365; // 1 year |
| 50 | 47 |
| 48 /* XXX |
| 51 TransportSecurityState::TransportSecurityState(const std::string& hsts_hosts) | 49 TransportSecurityState::TransportSecurityState(const std::string& hsts_hosts) |
| 52 : delegate_(NULL) { | 50 : delegate_(NULL) { |
| 53 if (!hsts_hosts.empty()) { | 51 if (!hsts_hosts.empty()) { |
| 54 bool dirty; | 52 bool dirty; |
| 55 Deserialise(hsts_hosts, &dirty, &forced_hosts_); | 53 Deserialise(hsts_hosts, &dirty, &forced_hosts_); |
| 56 } | 54 } |
| 57 } | 55 }*/ |
| 58 | 56 |
| 59 static std::string HashHost(const std::string& canonicalized_host) { | 57 static std::string HashHost(const std::string& canonicalized_host) { |
| 60 char hashed[crypto::kSHA256Length]; | 58 char hashed[crypto::kSHA256Length]; |
| 61 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); | 59 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); |
| 62 return std::string(hashed, sizeof(hashed)); | 60 return std::string(hashed, sizeof(hashed)); |
| 63 } | 61 } |
| 64 | 62 |
| 63 TransportSecurityState::TransportSecurityState( |
| 64 TransportSecurityState::Delegate* delegate) |
| 65 : delegate_(delegate) { } |
| 66 |
| 67 TransportSecurityState::TransportSecurityState() |
| 68 : delegate_(NULL) { } |
| 69 |
| 65 void TransportSecurityState::SetDelegate( | 70 void TransportSecurityState::SetDelegate( |
| 66 TransportSecurityState::Delegate* delegate) { | 71 TransportSecurityState::Delegate* delegate) { |
| 67 delegate_ = delegate; | 72 delegate_ = delegate; |
| 68 } | 73 } |
| 69 | 74 |
| 70 void TransportSecurityState::EnableHost(const std::string& host, | 75 void TransportSecurityState::EnableHost(const std::string& host, |
| 71 const DomainState& state) { | 76 const DomainState& state) { |
| 72 DCHECK(CalledOnValidThread()); | 77 DCHECK(CalledOnValidThread()); |
| 73 | 78 |
| 74 const std::string canonicalized_host = CanonicalizeHost(host); | 79 const std::string canonicalized_host = CanonicalizeHost(host); |
| 75 if (canonicalized_host.empty()) | 80 if (canonicalized_host.empty()) |
| 76 return; | 81 return; |
| 77 | 82 |
| 83 DomainState existing_state; |
| 84 |
| 78 // Only override a preloaded state if the new state describes a more strict | 85 // Only override a preloaded state if the new state describes a more strict |
| 79 // policy. TODO(palmer): Reconsider this? | 86 // policy. TODO(palmer): Reconsider this? |
| 80 DomainState existing_state; | 87 /*if (GetStaticDomainState(canonicalized_host, true, &existing_state) && |
| 81 if (IsPreloadedSTS(canonicalized_host, true, &existing_state) && | |
| 82 canonicalized_host == CanonicalizeHost(existing_state.domain) && | 88 canonicalized_host == CanonicalizeHost(existing_state.domain) && |
| 83 existing_state.IsMoreStrict(state)) { | 89 existing_state.IsMoreStrict(state)) { |
| 84 return; | 90 return; |
| 85 } | 91 }*/ |
| 86 | 92 |
| 87 // Use the original creation date if we already have this host. | 93 // Use the original creation date if we already have this host. |
| 88 DomainState state_copy(state); | 94 DomainState state_copy(state); |
| 89 if (GetDomainState(&existing_state, host, true) && | 95 if (GetDomainState(host, true, &existing_state) && |
| 90 !existing_state.created.is_null()) { | 96 !existing_state.created.is_null()) { |
| 91 state_copy.created = existing_state.created; | 97 state_copy.created = existing_state.created; |
| 92 } | 98 } |
| 93 | 99 |
| 94 // We don't store these values. | 100 // We don't store this value. |
| 95 state_copy.preloaded = false; | |
| 96 state_copy.domain.clear(); | 101 state_copy.domain.clear(); |
| 97 | 102 |
| 98 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; | 103 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; |
| 99 DirtyNotify(); | 104 DirtyNotify(); |
| 100 } | 105 } |
| 101 | 106 |
| 102 bool TransportSecurityState::DeleteHost(const std::string& host) { | 107 bool TransportSecurityState::DeleteHost(const std::string& host) { |
| 103 DCHECK(CalledOnValidThread()); | 108 DCHECK(CalledOnValidThread()); |
| 104 | 109 |
| 105 const std::string canonicalized_host = CanonicalizeHost(host); | 110 const std::string canonicalized_host = CanonicalizeHost(host); |
| 106 if (canonicalized_host.empty()) | 111 if (canonicalized_host.empty()) |
| 107 return false; | 112 return false; |
| 108 | 113 |
| 109 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find( | 114 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find( |
| 110 HashHost(canonicalized_host)); | 115 HashHost(canonicalized_host)); |
| 111 if (i != enabled_hosts_.end()) { | 116 if (i != enabled_hosts_.end()) { |
| 112 enabled_hosts_.erase(i); | 117 enabled_hosts_.erase(i); |
| 113 DirtyNotify(); | 118 DirtyNotify(); |
| 114 return true; | 119 return true; |
| 115 } | 120 } |
| 116 return false; | 121 return false; |
| 117 } | 122 } |
| 118 | 123 |
| 119 bool TransportSecurityState::HasPinsForHost(DomainState* result, | 124 bool TransportSecurityState::GetDomainState(const std::string& host, |
| 120 const std::string& host, | 125 bool sni_enabled, |
| 121 bool sni_available) { | 126 DomainState* result) { |
| 122 DCHECK(CalledOnValidThread()); | |
| 123 | |
| 124 return HasMetadata(result, host, sni_available) && | |
| 125 (!result->dynamic_spki_hashes.empty() || | |
| 126 !result->preloaded_spki_hashes.empty()); | |
| 127 } | |
| 128 | |
| 129 bool TransportSecurityState::GetDomainState(DomainState* result, | |
| 130 const std::string& host, | |
| 131 bool sni_available) { | |
| 132 DCHECK(CalledOnValidThread()); | |
| 133 | |
| 134 return HasMetadata(result, host, sni_available); | |
| 135 } | |
| 136 | |
| 137 bool TransportSecurityState::HasMetadata(DomainState* result, | |
| 138 const std::string& host, | |
| 139 bool sni_available) { | |
| 140 DCHECK(CalledOnValidThread()); | 127 DCHECK(CalledOnValidThread()); |
| 141 | 128 |
| 142 DomainState state; | 129 DomainState state; |
| 143 const std::string canonicalized_host = CanonicalizeHost(host); | 130 const std::string canonicalized_host = CanonicalizeHost(host); |
| 144 if (canonicalized_host.empty()) | 131 if (canonicalized_host.empty()) |
| 145 return false; | 132 return false; |
| 146 | 133 |
| 147 bool has_preload = IsPreloadedSTS(canonicalized_host, sni_available, &state); | 134 bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled, |
| 135 &state); |
| 148 std::string canonicalized_preload = CanonicalizeHost(state.domain); | 136 std::string canonicalized_preload = CanonicalizeHost(state.domain); |
| 149 | 137 |
| 150 base::Time current_time(base::Time::Now()); | 138 base::Time current_time(base::Time::Now()); |
| 151 | 139 |
| 152 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 140 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
| 153 std::string host_sub_chunk(&canonicalized_host[i], | 141 std::string host_sub_chunk(&canonicalized_host[i], |
| 154 canonicalized_host.size() - i); | 142 canonicalized_host.size() - i); |
| 155 // Exact match of a preload always wins. | 143 // Exact match of a preload always wins. |
| 156 if (has_preload && host_sub_chunk == canonicalized_preload) { | 144 if (has_preload && host_sub_chunk == canonicalized_preload) { |
| 157 *result = state; | 145 *result = state; |
| 158 return true; | 146 return true; |
| 159 } | 147 } |
| 160 | 148 |
| 161 std::map<std::string, DomainState>::iterator j = | 149 std::map<std::string, DomainState>::iterator j = |
| 162 enabled_hosts_.find(HashHost(host_sub_chunk)); | 150 enabled_hosts_.find(HashHost(host_sub_chunk)); |
| 163 if (j == enabled_hosts_.end()) | 151 if (j == enabled_hosts_.end()) |
| 164 continue; | 152 continue; |
| 165 | 153 |
| 166 if (current_time > j->second.expiry && | 154 if (current_time > j->second.upgrade_expiry && |
| 167 current_time > j->second.dynamic_spki_hashes_expiry) { | 155 current_time > j->second.dynamic_spki_hashes_expiry) { |
| 168 enabled_hosts_.erase(j); | 156 enabled_hosts_.erase(j); |
| 169 DirtyNotify(); | 157 DirtyNotify(); |
| 170 continue; | 158 continue; |
| 171 } | 159 } |
| 172 | 160 |
| 173 state = j->second; | 161 state = j->second; |
| 174 state.domain = DNSDomainToString(host_sub_chunk); | 162 state.domain = DNSDomainToString(host_sub_chunk); |
| 175 | 163 |
| 176 // Succeed if we matched the domain exactly or if subdomain matches are | 164 // 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... |
| 284 if (!base::Base64Decode(unquoted, &decoded) || | 272 if (!base::Base64Decode(unquoted, &decoded) || |
| 285 decoded.size() != arraysize(fp.data)) { | 273 decoded.size() != arraysize(fp.data)) { |
| 286 return false; | 274 return false; |
| 287 } | 275 } |
| 288 | 276 |
| 289 memcpy(fp.data, decoded.data(), arraysize(fp.data)); | 277 memcpy(fp.data, decoded.data(), arraysize(fp.data)); |
| 290 fingerprints->push_back(fp); | 278 fingerprints->push_back(fp); |
| 291 return true; | 279 return true; |
| 292 } | 280 } |
| 293 | 281 |
| 294 // static | |
| 295 bool TransportSecurityState::GetPublicKeyHash( | |
| 296 const X509Certificate& cert, SHA1Fingerprint* spki_hash) { | |
| 297 std::string der_bytes; | |
| 298 if (!X509Certificate::GetDEREncoded(cert.os_cert_handle(), &der_bytes)) | |
| 299 return false; | |
| 300 | |
| 301 base::StringPiece spki; | |
| 302 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) | |
| 303 return false; | |
| 304 | |
| 305 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(spki.data()), | |
| 306 spki.size(), spki_hash->data); | |
| 307 | |
| 308 return true; | |
| 309 } | |
| 310 | |
| 311 struct FingerprintsEqualPredicate { | 282 struct FingerprintsEqualPredicate { |
| 312 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : | 283 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : |
| 313 fingerprint_(fingerprint) {} | 284 fingerprint_(fingerprint) {} |
| 314 | 285 |
| 315 bool operator()(const SHA1Fingerprint& other) const { | 286 bool operator()(const SHA1Fingerprint& other) const { |
| 316 return fingerprint_.Equals(other); | 287 return fingerprint_.Equals(other); |
| 317 } | 288 } |
| 318 | 289 |
| 319 const SHA1Fingerprint& fingerprint_; | 290 const SHA1Fingerprint& fingerprint_; |
| 320 }; | 291 }; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 362 if (from_cert_chain.empty()) | 333 if (from_cert_chain.empty()) |
| 363 return false; | 334 return false; |
| 364 | 335 |
| 365 return IsBackupPinPresent(pins, from_cert_chain) && | 336 return IsBackupPinPresent(pins, from_cert_chain) && |
| 366 HashesIntersect(pins, from_cert_chain); | 337 HashesIntersect(pins, from_cert_chain); |
| 367 } | 338 } |
| 368 | 339 |
| 369 // "Public-Key-Pins" ":" | 340 // "Public-Key-Pins" ":" |
| 370 // "max-age" "=" delta-seconds ";" | 341 // "max-age" "=" delta-seconds ";" |
| 371 // "pin-" algo "=" base64 [ ";" ... ] | 342 // "pin-" algo "=" base64 [ ";" ... ] |
| 372 // | 343 bool TransportSecurityState::DomainState::ParsePinsHeader( |
| 373 // static | 344 const base::Time& now, |
| 374 bool TransportSecurityState::ParsePinsHeader(const std::string& value, | 345 const std::string& value, |
| 375 const SSLInfo& ssl_info, | 346 const SSLInfo& ssl_info) { |
| 376 DomainState* state) { | |
| 377 bool parsed_max_age = false; | 347 bool parsed_max_age = false; |
| 378 int max_age = 0; | 348 int max_age_candidate = 0; |
| 379 FingerprintVector pins; | 349 FingerprintVector pins; |
| 380 | 350 |
| 381 std::string source = value; | 351 std::string source = value; |
| 382 | 352 |
| 383 while (!source.empty()) { | 353 while (!source.empty()) { |
| 384 StringPair semicolon = Split(source, ';'); | 354 StringPair semicolon = Split(source, ';'); |
| 385 semicolon.first = Strip(semicolon.first); | 355 semicolon.first = Strip(semicolon.first); |
| 386 semicolon.second = Strip(semicolon.second); | 356 semicolon.second = Strip(semicolon.second); |
| 387 StringPair equals = Split(semicolon.first, '='); | 357 StringPair equals = Split(semicolon.first, '='); |
| 388 equals.first = Strip(equals.first); | 358 equals.first = Strip(equals.first); |
| 389 equals.second = Strip(equals.second); | 359 equals.second = Strip(equals.second); |
| 390 | 360 |
| 391 if (LowerCaseEqualsASCII(equals.first, "max-age")) { | 361 if (LowerCaseEqualsASCII(equals.first, "max-age")) { |
| 392 if (equals.second.empty() || | 362 if (equals.second.empty() || |
| 393 !MaxAgeToInt(equals.second.begin(), equals.second.end(), &max_age)) { | 363 !MaxAgeToInt(equals.second.begin(), equals.second.end(), |
| 364 &max_age_candidate)) { |
| 394 return false; | 365 return false; |
| 395 } | 366 } |
| 396 if (max_age > kMaxHSTSAgeSecs) | 367 if (max_age_candidate > kMaxHSTSAgeSecs) |
| 397 max_age = kMaxHSTSAgeSecs; | 368 max_age_candidate = kMaxHSTSAgeSecs; |
| 398 parsed_max_age = true; | 369 parsed_max_age = true; |
| 399 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { | 370 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { |
| 400 if (!ParseAndAppendPin(equals.second, &pins)) | 371 if (!ParseAndAppendPin(equals.second, &pins)) |
| 401 return false; | 372 return false; |
| 402 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { | 373 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { |
| 403 // TODO(palmer) | 374 // TODO(palmer) |
| 404 } else { | 375 } else { |
| 405 // Silently ignore unknown directives for forward compatibility. | 376 // Silently ignore unknown directives for forward compatibility. |
| 406 } | 377 } |
| 407 | 378 |
| 408 source = semicolon.second; | 379 source = semicolon.second; |
| 409 } | 380 } |
| 410 | 381 |
| 411 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) | 382 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) |
| 412 return false; | 383 return false; |
| 413 | 384 |
| 414 state->max_age = max_age; | 385 dynamic_spki_hashes_expiry = |
| 415 state->dynamic_spki_hashes_expiry = | 386 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 416 base::Time::Now() + base::TimeDelta::FromSeconds(max_age); | |
| 417 | 387 |
| 418 state->dynamic_spki_hashes.clear(); | 388 dynamic_spki_hashes.clear(); |
| 419 if (max_age > 0) { | 389 if (max_age_candidate > 0) { |
| 420 for (FingerprintVector::const_iterator i = pins.begin(); | 390 for (FingerprintVector::const_iterator i = pins.begin(); |
| 421 i != pins.end(); i++) { | 391 i != pins.end(); ++i) { |
| 422 state->dynamic_spki_hashes.push_back(*i); | 392 dynamic_spki_hashes.push_back(*i); |
| 423 } | 393 } |
| 424 } | 394 } |
| 425 | 395 |
| 426 return true; | 396 return true; |
| 427 } | 397 } |
| 428 | 398 |
| 429 // "Strict-Transport-Security" ":" | 399 // "Strict-Transport-Security" ":" |
| 430 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] | 400 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] |
| 431 // | 401 bool TransportSecurityState::DomainState::ParseSTSHeader( |
| 432 // static | 402 const base::Time& now, |
| 433 bool TransportSecurityState::ParseHeader(const std::string& value, | 403 const std::string& value) { |
| 434 int* max_age, | |
| 435 bool* include_subdomains) { | |
| 436 DCHECK(max_age); | |
| 437 DCHECK(include_subdomains); | |
| 438 | |
| 439 int max_age_candidate = 0; | 404 int max_age_candidate = 0; |
| 440 | 405 |
| 441 enum ParserState { | 406 enum ParserState { |
| 442 START, | 407 START, |
| 443 AFTER_MAX_AGE_LABEL, | 408 AFTER_MAX_AGE_LABEL, |
| 444 AFTER_MAX_AGE_EQUALS, | 409 AFTER_MAX_AGE_EQUALS, |
| 445 AFTER_MAX_AGE, | 410 AFTER_MAX_AGE, |
| 446 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER, | 411 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER, |
| 447 AFTER_INCLUDE_SUBDOMAINS, | 412 AFTER_INCLUDE_SUBDOMAINS, |
| 448 } state = START; | 413 } state = START; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 505 } | 470 } |
| 506 } | 471 } |
| 507 | 472 |
| 508 // We've consumed all the input. Let's see what state we ended up in. | 473 // We've consumed all the input. Let's see what state we ended up in. |
| 509 switch (state) { | 474 switch (state) { |
| 510 case START: | 475 case START: |
| 511 case AFTER_MAX_AGE_LABEL: | 476 case AFTER_MAX_AGE_LABEL: |
| 512 case AFTER_MAX_AGE_EQUALS: | 477 case AFTER_MAX_AGE_EQUALS: |
| 513 return false; | 478 return false; |
| 514 case AFTER_MAX_AGE: | 479 case AFTER_MAX_AGE: |
| 515 *max_age = max_age_candidate; | 480 upgrade_expiry = |
| 516 *include_subdomains = false; | 481 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 482 include_subdomains = false; |
| 483 upgrade_mode = MODE_FORCE_HTTPS; |
| 517 return true; | 484 return true; |
| 518 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: | 485 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: |
| 519 return false; | 486 return false; |
| 520 case AFTER_INCLUDE_SUBDOMAINS: | 487 case AFTER_INCLUDE_SUBDOMAINS: |
| 521 *max_age = max_age_candidate; | 488 upgrade_expiry = |
| 522 *include_subdomains = true; | 489 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 490 include_subdomains = true; |
| 491 upgrade_mode = MODE_FORCE_HTTPS; |
| 523 return true; | 492 return true; |
| 524 default: | 493 default: |
| 525 NOTREACHED(); | 494 NOTREACHED(); |
| 526 return false; | 495 return false; |
| 527 } | 496 } |
| 528 } | 497 } |
| 529 | 498 |
| 530 // Side pinning and superfluous certificates: | |
| 531 // | |
| 532 // In SSLClientSocketNSS::DoVerifyCertComplete we look for certificates with a | |
| 533 // Subject of CN=meta. When we find one we'll currently try and parse side | |
| 534 // pinned key from it. | |
| 535 // | |
| 536 // A side pin is a key which can be pinned to, but also can be kept offline and | |
| 537 // still held by the site owner. The CN=meta certificate is just a backwards | |
| 538 // compatiable method of carrying a lump of bytes to the client. (We could use | |
| 539 // a TLS extension just as well, but it's a lot easier for admins to add extra | |
| 540 // certificates to the chain.) | |
| 541 | |
| 542 // A TagMap represents the simple key-value structure that we use. Keys are | |
| 543 // 32-bit ints. Values are byte strings. | |
| 544 typedef std::map<uint32, base::StringPiece> TagMap; | |
| 545 | |
| 546 // ParseTags parses a list of key-value pairs from |in| to |out| and advances | |
| 547 // |in| past the data. The key-value pair data is: | |
| 548 // u16le num_tags | |
| 549 // u32le tag[num_tags] | |
| 550 // u16le lengths[num_tags] | |
| 551 // ...data... | |
| 552 static bool ParseTags(base::StringPiece* in, TagMap *out) { | |
| 553 // Many part of Chrome already assume little-endian. This is just to help | |
| 554 // anyone who should try to port it in the future. | |
| 555 #if defined(__BYTE_ORDER) | |
| 556 // Linux check | |
| 557 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian); | |
| 558 #elif defined(__BIG_ENDIAN__) | |
| 559 // Mac check | |
| 560 #error assumes little endian | |
| 561 #endif | |
| 562 | |
| 563 uint16 num_tags_16; | |
| 564 if (in->size() < sizeof(num_tags_16)) | |
| 565 return false; | |
| 566 | |
| 567 memcpy(&num_tags_16, in->data(), sizeof(num_tags_16)); | |
| 568 in->remove_prefix(sizeof(num_tags_16)); | |
| 569 unsigned num_tags = num_tags_16; | |
| 570 | |
| 571 if (in->size() < 6 * num_tags) | |
| 572 return false; | |
| 573 | |
| 574 const uint32* tags = reinterpret_cast<const uint32*>(in->data()); | |
| 575 const uint16* lens = reinterpret_cast<const uint16*>( | |
| 576 in->data() + 4*num_tags); | |
| 577 in->remove_prefix(6*num_tags); | |
| 578 | |
| 579 uint32 prev_tag = 0; | |
| 580 for (unsigned i = 0; i < num_tags; i++) { | |
| 581 size_t len = lens[i]; | |
| 582 uint32 tag = tags[i]; | |
| 583 | |
| 584 if (in->size() < len) | |
| 585 return false; | |
| 586 // tags must be in ascending order. | |
| 587 if (i > 0 && prev_tag >= tag) | |
| 588 return false; | |
| 589 (*out)[tag] = base::StringPiece(in->data(), len); | |
| 590 in->remove_prefix(len); | |
| 591 prev_tag = tag; | |
| 592 } | |
| 593 | |
| 594 return true; | |
| 595 } | |
| 596 | |
| 597 // GetTag extracts the data associated with |tag| in |tags|. | |
| 598 static bool GetTag(uint32 tag, const TagMap& tags, base::StringPiece* out) { | |
| 599 TagMap::const_iterator i = tags.find(tag); | |
| 600 if (i == tags.end()) | |
| 601 return false; | |
| 602 | |
| 603 *out = i->second; | |
| 604 return true; | |
| 605 } | |
| 606 | |
| 607 // kP256SubjectPublicKeyInfoPrefix can be prepended onto a P256 elliptic curve | |
| 608 // point in X9.62 format in order to make a valid SubjectPublicKeyInfo. The | |
| 609 // ASN.1 interpretation of these bytes is: | |
| 610 // | |
| 611 // 0:d=0 hl=2 l= 89 cons: SEQUENCE | |
| 612 // 2:d=1 hl=2 l= 19 cons: SEQUENCE | |
| 613 // 4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey | |
| 614 // 13:d=2 hl=2 l= 8 prim: OBJECT :prime256v1 | |
| 615 // 23:d=1 hl=2 l= 66 prim: BIT STRING | |
| 616 static const uint8 kP256SubjectPublicKeyInfoPrefix[] = { | |
| 617 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, | |
| 618 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, | |
| 619 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, | |
| 620 0x42, 0x00, | |
| 621 }; | |
| 622 | |
| 623 // VerifySignature returns true iff |sig| is a valid signature of | |
| 624 // |hash| by |pubkey|. The actual implementation is crypto library | |
| 625 // specific. | |
| 626 static bool VerifySignature(const base::StringPiece& pubkey, | |
| 627 const base::StringPiece& sig, | |
| 628 const base::StringPiece& hash); | |
| 629 | |
| 630 #if defined(USE_OPENSSL) | |
| 631 | |
| 632 static EVP_PKEY* DecodeX962P256PublicKey( | |
| 633 const base::StringPiece& pubkey_bytes) { | |
| 634 // The public key is an X9.62 encoded P256 point. | |
| 635 if (pubkey_bytes.size() != 1 + 2*32) | |
| 636 return NULL; | |
| 637 | |
| 638 std::string pubkey_spki( | |
| 639 reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix), | |
| 640 sizeof(kP256SubjectPublicKeyInfoPrefix)); | |
| 641 pubkey_spki += pubkey_bytes.as_string(); | |
| 642 | |
| 643 EVP_PKEY* ret = NULL; | |
| 644 const unsigned char* der_pubkey = | |
| 645 reinterpret_cast<const unsigned char*>(pubkey_spki.data()); | |
| 646 d2i_PUBKEY(&ret, &der_pubkey, pubkey_spki.size()); | |
| 647 return ret; | |
| 648 } | |
| 649 | |
| 650 static bool VerifySignature(const base::StringPiece& pubkey, | |
| 651 const base::StringPiece& sig, | |
| 652 const base::StringPiece& hash) { | |
| 653 crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> secpubkey( | |
| 654 DecodeX962P256PublicKey(pubkey)); | |
| 655 if (!secpubkey.get()) | |
| 656 return false; | |
| 657 | |
| 658 | |
| 659 crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ec_key( | |
| 660 EVP_PKEY_get1_EC_KEY(secpubkey.get())); | |
| 661 if (!ec_key.get()) | |
| 662 return false; | |
| 663 | |
| 664 return ECDSA_verify(0, reinterpret_cast<const unsigned char*>(hash.data()), | |
| 665 hash.size(), | |
| 666 reinterpret_cast<const unsigned char*>(sig.data()), | |
| 667 sig.size(), ec_key.get()) == 1; | |
| 668 } | |
| 669 | |
| 670 #else | |
| 671 | |
| 672 // DecodeX962P256PublicKey parses an uncompressed, X9.62 format, P256 elliptic | |
| 673 // curve point from |pubkey_bytes| and returns it as a SECKEYPublicKey. | |
| 674 static SECKEYPublicKey* DecodeX962P256PublicKey( | |
| 675 const base::StringPiece& pubkey_bytes) { | |
| 676 // The public key is an X9.62 encoded P256 point. | |
| 677 if (pubkey_bytes.size() != 1 + 2*32) | |
| 678 return NULL; | |
| 679 | |
| 680 std::string pubkey_spki( | |
| 681 reinterpret_cast<const char*>(kP256SubjectPublicKeyInfoPrefix), | |
| 682 sizeof(kP256SubjectPublicKeyInfoPrefix)); | |
| 683 pubkey_spki += pubkey_bytes.as_string(); | |
| 684 | |
| 685 SECItem der; | |
| 686 memset(&der, 0, sizeof(der)); | |
| 687 der.data = reinterpret_cast<uint8*>(const_cast<char*>(pubkey_spki.data())); | |
| 688 der.len = pubkey_spki.size(); | |
| 689 | |
| 690 CERTSubjectPublicKeyInfo* spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der); | |
| 691 if (!spki) | |
| 692 return NULL; | |
| 693 SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); | |
| 694 SECKEY_DestroySubjectPublicKeyInfo(spki); | |
| 695 | |
| 696 return public_key; | |
| 697 } | |
| 698 | |
| 699 static bool VerifySignature(const base::StringPiece& pubkey, | |
| 700 const base::StringPiece& sig, | |
| 701 const base::StringPiece& hash) { | |
| 702 SECKEYPublicKey* secpubkey = DecodeX962P256PublicKey(pubkey); | |
| 703 if (!secpubkey) | |
| 704 return false; | |
| 705 | |
| 706 SECItem sigitem; | |
| 707 memset(&sigitem, 0, sizeof(sigitem)); | |
| 708 sigitem.data = reinterpret_cast<uint8*>(const_cast<char*>(sig.data())); | |
| 709 sigitem.len = sig.size(); | |
| 710 | |
| 711 // |decoded_sigitem| is newly allocated, as is the data that it points to. | |
| 712 SECItem* decoded_sigitem = DSAU_DecodeDerSigToLen( | |
| 713 &sigitem, SECKEY_SignatureLen(secpubkey)); | |
| 714 | |
| 715 if (!decoded_sigitem) { | |
| 716 SECKEY_DestroyPublicKey(secpubkey); | |
| 717 return false; | |
| 718 } | |
| 719 | |
| 720 SECItem hashitem; | |
| 721 memset(&hashitem, 0, sizeof(hashitem)); | |
| 722 hashitem.data = reinterpret_cast<unsigned char*>( | |
| 723 const_cast<char*>(hash.data())); | |
| 724 hashitem.len = hash.size(); | |
| 725 | |
| 726 SECStatus rv = PK11_Verify(secpubkey, decoded_sigitem, &hashitem, NULL); | |
| 727 SECKEY_DestroyPublicKey(secpubkey); | |
| 728 SECITEM_FreeItem(decoded_sigitem, PR_TRUE); | |
| 729 return rv == SECSuccess; | |
| 730 } | |
| 731 | |
| 732 #endif // !defined(USE_OPENSSL) | |
| 733 | |
| 734 // These are the tag values that we use. Tags are little-endian on the wire and | |
| 735 // these values correspond to the ASCII of the name. | |
| 736 static const uint32 kTagALGO = 0x4f474c41; | |
| 737 static const uint32 kTagP256 = 0x36353250; | |
| 738 static const uint32 kTagPUBK = 0x4b425550; | |
| 739 static const uint32 kTagSIG = 0x474953; | |
| 740 static const uint32 kTagSPIN = 0x4e495053; | |
| 741 | |
| 742 // static | |
| 743 bool TransportSecurityState::ParseSidePin( | |
| 744 const base::StringPiece& leaf_spki, | |
| 745 const base::StringPiece& in_side_info, | |
| 746 FingerprintVector* out_pub_key_hash) { | |
| 747 base::StringPiece side_info(in_side_info); | |
| 748 | |
| 749 TagMap outer; | |
| 750 if (!ParseTags(&side_info, &outer)) | |
| 751 return false; | |
| 752 // trailing data is not allowed | |
| 753 if (side_info.size()) | |
| 754 return false; | |
| 755 | |
| 756 base::StringPiece side_pin_bytes; | |
| 757 if (!GetTag(kTagSPIN, outer, &side_pin_bytes)) | |
| 758 return false; | |
| 759 | |
| 760 bool have_parsed_a_key = false; | |
| 761 uint8 leaf_spki_hash[crypto::kSHA256Length]; | |
| 762 bool have_leaf_spki_hash = false; | |
| 763 | |
| 764 while (side_pin_bytes.size() > 0) { | |
| 765 TagMap side_pin; | |
| 766 if (!ParseTags(&side_pin_bytes, &side_pin)) | |
| 767 return false; | |
| 768 | |
| 769 base::StringPiece algo, pubkey, sig; | |
| 770 if (!GetTag(kTagALGO, side_pin, &algo) || | |
| 771 !GetTag(kTagPUBK, side_pin, &pubkey) || | |
| 772 !GetTag(kTagSIG, side_pin, &sig)) { | |
| 773 return false; | |
| 774 } | |
| 775 | |
| 776 if (algo.size() != sizeof(kTagP256) || | |
| 777 0 != memcmp(algo.data(), &kTagP256, sizeof(kTagP256))) { | |
| 778 // We don't support anything but P256 at the moment. | |
| 779 continue; | |
| 780 } | |
| 781 | |
| 782 if (!have_leaf_spki_hash) { | |
| 783 crypto::SHA256HashString( | |
| 784 leaf_spki.as_string(), leaf_spki_hash, sizeof(leaf_spki_hash)); | |
| 785 have_leaf_spki_hash = true; | |
| 786 } | |
| 787 | |
| 788 if (VerifySignature(pubkey, sig, base::StringPiece( | |
| 789 reinterpret_cast<const char*>(leaf_spki_hash), | |
| 790 sizeof(leaf_spki_hash)))) { | |
| 791 SHA1Fingerprint fpr; | |
| 792 base::SHA1HashBytes( | |
| 793 reinterpret_cast<const uint8*>(pubkey.data()), | |
| 794 pubkey.size(), | |
| 795 fpr.data); | |
| 796 out_pub_key_hash->push_back(fpr); | |
| 797 have_parsed_a_key = true; | |
| 798 } | |
| 799 } | |
| 800 | |
| 801 return have_parsed_a_key; | |
| 802 } | |
| 803 | |
| 804 // This function converts the binary hashes, which we store in | |
| 805 // |enabled_hosts_|, to a base64 string which we can include in a JSON file. | |
| 806 static std::string HashedDomainToExternalString(const std::string& hashed) { | |
| 807 std::string out; | |
| 808 CHECK(base::Base64Encode(hashed, &out)); | |
| 809 return out; | |
| 810 } | |
| 811 | |
| 812 // This inverts |HashedDomainToExternalString|, above. It turns an external | |
| 813 // string (from a JSON file) into an internal (binary) string. | |
| 814 static std::string ExternalStringToHashedDomain(const std::string& external) { | |
| 815 std::string out; | |
| 816 if (!base::Base64Decode(external, &out) || | |
| 817 out.size() != crypto::kSHA256Length) { | |
| 818 return std::string(); | |
| 819 } | |
| 820 | |
| 821 return out; | |
| 822 } | |
| 823 | |
| 824 static ListValue* SPKIHashesToListValue(const FingerprintVector& hashes) { | |
| 825 ListValue* pins = new ListValue; | |
| 826 | |
| 827 for (FingerprintVector::const_iterator i = hashes.begin(); | |
| 828 i != hashes.end(); ++i) { | |
| 829 std::string hash_str(reinterpret_cast<const char*>(i->data), | |
| 830 sizeof(i->data)); | |
| 831 std::string b64; | |
| 832 base::Base64Encode(hash_str, &b64); | |
| 833 pins->Append(new StringValue("sha1/" + b64)); | |
| 834 } | |
| 835 | |
| 836 return pins; | |
| 837 } | |
| 838 | |
| 839 bool TransportSecurityState::Serialise(std::string* output) { | |
| 840 DCHECK(CalledOnValidThread()); | |
| 841 | |
| 842 DictionaryValue toplevel; | |
| 843 base::Time now = base::Time::Now(); | |
| 844 for (std::map<std::string, DomainState>::const_iterator | |
| 845 i = enabled_hosts_.begin(); i != enabled_hosts_.end(); ++i) { | |
| 846 DictionaryValue* state = new DictionaryValue; | |
| 847 state->SetBoolean("include_subdomains", i->second.include_subdomains); | |
| 848 state->SetDouble("created", i->second.created.ToDoubleT()); | |
| 849 state->SetDouble("expiry", i->second.expiry.ToDoubleT()); | |
| 850 state->SetDouble("dynamic_spki_hashes_expiry", | |
| 851 i->second.dynamic_spki_hashes_expiry.ToDoubleT()); | |
| 852 | |
| 853 switch (i->second.mode) { | |
| 854 case DomainState::MODE_STRICT: | |
| 855 state->SetString("mode", "strict"); | |
| 856 break; | |
| 857 case DomainState::MODE_SPDY_ONLY: | |
| 858 state->SetString("mode", "spdy-only"); | |
| 859 break; | |
| 860 case DomainState::MODE_PINNING_ONLY: | |
| 861 state->SetString("mode", "pinning-only"); | |
| 862 break; | |
| 863 default: | |
| 864 NOTREACHED() << "DomainState with unknown mode"; | |
| 865 delete state; | |
| 866 continue; | |
| 867 } | |
| 868 | |
| 869 state->Set("preloaded_spki_hashes", | |
| 870 SPKIHashesToListValue(i->second.preloaded_spki_hashes)); | |
| 871 | |
| 872 if (now < i->second.dynamic_spki_hashes_expiry) { | |
| 873 state->Set("dynamic_spki_hashes", | |
| 874 SPKIHashesToListValue(i->second.dynamic_spki_hashes)); | |
| 875 } | |
| 876 | |
| 877 toplevel.Set(HashedDomainToExternalString(i->first), state); | |
| 878 } | |
| 879 | |
| 880 base::JSONWriter::WriteWithOptions(&toplevel, | |
| 881 base::JSONWriter::OPTIONS_PRETTY_PRINT, | |
| 882 output); | |
| 883 return true; | |
| 884 } | |
| 885 | |
| 886 bool TransportSecurityState::LoadEntries(const std::string& input, | |
| 887 bool* dirty) { | |
| 888 DCHECK(CalledOnValidThread()); | |
| 889 | |
| 890 enabled_hosts_.clear(); | |
| 891 return Deserialise(input, dirty, &enabled_hosts_); | |
| 892 } | |
| 893 | |
| 894 static bool AddHash(const std::string& type_and_base64, | 499 static bool AddHash(const std::string& type_and_base64, |
| 895 FingerprintVector* out) { | 500 FingerprintVector* out) { |
| 896 SHA1Fingerprint hash; | 501 SHA1Fingerprint hash; |
| 897 | 502 |
| 898 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) | 503 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) |
| 899 return false; | 504 return false; |
| 900 | 505 |
| 901 out->push_back(hash); | 506 out->push_back(hash); |
| 902 return true; | 507 return true; |
| 903 } | 508 } |
| 904 | 509 |
| 905 static void SPKIHashesFromListValue(FingerprintVector* hashes, | |
| 906 const ListValue& pins) { | |
| 907 size_t num_pins = pins.GetSize(); | |
| 908 for (size_t i = 0; i < num_pins; ++i) { | |
| 909 std::string type_and_base64; | |
| 910 if (pins.GetString(i, &type_and_base64)) | |
| 911 AddHash(type_and_base64, hashes); | |
| 912 } | |
| 913 } | |
| 914 | |
| 915 // static | |
| 916 bool TransportSecurityState::Deserialise( | |
| 917 const std::string& input, | |
| 918 bool* dirty, | |
| 919 std::map<std::string, DomainState>* out) { | |
| 920 scoped_ptr<Value> value( | |
| 921 base::JSONReader::Read(input, false /* do not allow trailing commas */)); | |
| 922 if (!value.get() || !value->IsType(Value::TYPE_DICTIONARY)) | |
| 923 return false; | |
| 924 | |
| 925 DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get()); | |
| 926 const base::Time current_time(base::Time::Now()); | |
| 927 bool dirtied = false; | |
| 928 | |
| 929 for (DictionaryValue::key_iterator i = dict_value->begin_keys(); | |
| 930 i != dict_value->end_keys(); ++i) { | |
| 931 DictionaryValue* state; | |
| 932 if (!dict_value->GetDictionaryWithoutPathExpansion(*i, &state)) | |
| 933 continue; | |
| 934 | |
| 935 bool include_subdomains; | |
| 936 std::string mode_string; | |
| 937 double created; | |
| 938 double expiry; | |
| 939 double dynamic_spki_hashes_expiry = 0.0; | |
| 940 | |
| 941 if (!state->GetBoolean("include_subdomains", &include_subdomains) || | |
| 942 !state->GetString("mode", &mode_string) || | |
| 943 !state->GetDouble("expiry", &expiry)) { | |
| 944 continue; | |
| 945 } | |
| 946 | |
| 947 // Don't fail if this key is not present. | |
| 948 (void) state->GetDouble("dynamic_spki_hashes_expiry", | |
| 949 &dynamic_spki_hashes_expiry); | |
| 950 | |
| 951 ListValue* pins_list = NULL; | |
| 952 FingerprintVector preloaded_spki_hashes; | |
| 953 if (state->GetList("preloaded_spki_hashes", &pins_list)) | |
| 954 SPKIHashesFromListValue(&preloaded_spki_hashes, *pins_list); | |
| 955 | |
| 956 FingerprintVector dynamic_spki_hashes; | |
| 957 if (state->GetList("dynamic_spki_hashes", &pins_list)) | |
| 958 SPKIHashesFromListValue(&dynamic_spki_hashes, *pins_list); | |
| 959 | |
| 960 DomainState::Mode mode; | |
| 961 if (mode_string == "strict") { | |
| 962 mode = DomainState::MODE_STRICT; | |
| 963 } else if (mode_string == "spdy-only") { | |
| 964 mode = DomainState::MODE_SPDY_ONLY; | |
| 965 } else if (mode_string == "pinning-only") { | |
| 966 mode = DomainState::MODE_PINNING_ONLY; | |
| 967 } else { | |
| 968 LOG(WARNING) << "Unknown TransportSecurityState mode string found: " | |
| 969 << mode_string; | |
| 970 continue; | |
| 971 } | |
| 972 | |
| 973 base::Time expiry_time = base::Time::FromDoubleT(expiry); | |
| 974 base::Time dynamic_spki_hashes_expiry_time = | |
| 975 base::Time::FromDoubleT(dynamic_spki_hashes_expiry); | |
| 976 base::Time created_time; | |
| 977 if (state->GetDouble("created", &created)) { | |
| 978 created_time = base::Time::FromDoubleT(created); | |
| 979 } else { | |
| 980 // We're migrating an old entry with no creation date. Make sure we | |
| 981 // write the new date back in a reasonable time frame. | |
| 982 dirtied = true; | |
| 983 created_time = base::Time::Now(); | |
| 984 } | |
| 985 | |
| 986 if (expiry_time <= current_time && | |
| 987 dynamic_spki_hashes_expiry_time <= current_time) { | |
| 988 // Make sure we dirty the state if we drop an entry. | |
| 989 dirtied = true; | |
| 990 continue; | |
| 991 } | |
| 992 | |
| 993 std::string hashed = ExternalStringToHashedDomain(*i); | |
| 994 if (hashed.empty()) { | |
| 995 dirtied = true; | |
| 996 continue; | |
| 997 } | |
| 998 | |
| 999 DomainState new_state; | |
| 1000 new_state.mode = mode; | |
| 1001 new_state.created = created_time; | |
| 1002 new_state.expiry = expiry_time; | |
| 1003 new_state.include_subdomains = include_subdomains; | |
| 1004 new_state.preloaded_spki_hashes = preloaded_spki_hashes; | |
| 1005 new_state.dynamic_spki_hashes = dynamic_spki_hashes; | |
| 1006 new_state.dynamic_spki_hashes_expiry = dynamic_spki_hashes_expiry_time; | |
| 1007 (*out)[hashed] = new_state; | |
| 1008 } | |
| 1009 | |
| 1010 *dirty = dirtied; | |
| 1011 return true; | |
| 1012 } | |
| 1013 | |
| 1014 TransportSecurityState::~TransportSecurityState() { | 510 TransportSecurityState::~TransportSecurityState() { |
| 1015 } | 511 } |
| 1016 | 512 |
| 1017 void TransportSecurityState::DirtyNotify() { | 513 void TransportSecurityState::DirtyNotify() { |
| 1018 DCHECK(CalledOnValidThread()); | 514 DCHECK(CalledOnValidThread()); |
| 1019 | 515 |
| 1020 if (delegate_) | 516 if (delegate_) |
| 1021 delegate_->StateIsDirty(this); | 517 delegate_->StateIsDirty(this); |
| 1022 } | 518 } |
| 1023 | 519 |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1117 for (size_t j = 0; j < num_entries; j++) { | 613 for (size_t j = 0; j < num_entries; j++) { |
| 1118 if (entries[j].length == canonicalized_host.size() - i && | 614 if (entries[j].length == canonicalized_host.size() - i && |
| 1119 memcmp(entries[j].dns_name, &canonicalized_host[i], | 615 memcmp(entries[j].dns_name, &canonicalized_host[i], |
| 1120 entries[j].length) == 0) { | 616 entries[j].length) == 0) { |
| 1121 if (!entries[j].include_subdomains && i != 0) { | 617 if (!entries[j].include_subdomains && i != 0) { |
| 1122 *ret = false; | 618 *ret = false; |
| 1123 } else { | 619 } else { |
| 1124 out->include_subdomains = entries[j].include_subdomains; | 620 out->include_subdomains = entries[j].include_subdomains; |
| 1125 *ret = true; | 621 *ret = true; |
| 1126 if (!entries[j].https_required) | 622 if (!entries[j].https_required) |
| 1127 out->mode = TransportSecurityState::DomainState::MODE_PINNING_ONLY; | 623 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; |
| 1128 if (entries[j].pins.required_hashes) { | 624 if (entries[j].pins.required_hashes) { |
| 1129 const char* const* hash = entries[j].pins.required_hashes; | 625 const char* const* hash = entries[j].pins.required_hashes; |
| 1130 while (*hash) { | 626 while (*hash) { |
| 1131 bool ok = AddHash(*hash, &out->preloaded_spki_hashes); | 627 bool ok = AddHash(*hash, &out->static_spki_hashes); |
| 1132 DCHECK(ok) << " failed to parse " << *hash; | 628 DCHECK(ok) << " failed to parse " << *hash; |
| 1133 hash++; | 629 hash++; |
| 1134 } | 630 } |
| 1135 } | 631 } |
| 1136 if (entries[j].pins.excluded_hashes) { | 632 if (entries[j].pins.excluded_hashes) { |
| 1137 const char* const* hash = entries[j].pins.excluded_hashes; | 633 const char* const* hash = entries[j].pins.excluded_hashes; |
| 1138 while (*hash) { | 634 while (*hash) { |
| 1139 bool ok = AddHash(*hash, &out->bad_preloaded_spki_hashes); | 635 bool ok = AddHash(*hash, &out->bad_static_spki_hashes); |
| 1140 DCHECK(ok) << " failed to parse " << *hash; | 636 DCHECK(ok) << " failed to parse " << *hash; |
| 1141 hash++; | 637 hash++; |
| 1142 } | 638 } |
| 1143 } | 639 } |
| 1144 } | 640 } |
| 1145 return true; | 641 return true; |
| 1146 } | 642 } |
| 1147 } | 643 } |
| 1148 return false; | 644 return false; |
| 1149 } | 645 } |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1253 kSPKIHash_UTNUSERFirstHardware, | 749 kSPKIHash_UTNUSERFirstHardware, |
| 1254 kSPKIHash_UTNUSERFirstObject, | 750 kSPKIHash_UTNUSERFirstObject, |
| 1255 kSPKIHash_GTECyberTrustGlobalRoot, | 751 kSPKIHash_GTECyberTrustGlobalRoot, |
| 1256 NULL, | 752 NULL, |
| 1257 }; | 753 }; |
| 1258 #define kTwitterCDNPins { \ | 754 #define kTwitterCDNPins { \ |
| 1259 kTwitterCDNAcceptableCerts, \ | 755 kTwitterCDNAcceptableCerts, \ |
| 1260 kNoRejectedPublicKeys, \ | 756 kNoRejectedPublicKeys, \ |
| 1261 } | 757 } |
| 1262 | 758 |
| 759 // These are the tag values that we use. Tags are little-endian on the wire and |
| 760 // these values correspond to the ASCII of the name. |
| 761 static const uint32 kTagALGO = 0x4f474c41; |
| 762 static const uint32 kTagP256 = 0x36353250; |
| 763 static const uint32 kTagPUBK = 0x4b425550; |
| 764 static const uint32 kTagSIG = 0x474953; |
| 765 static const uint32 kTagSPIN = 0x4e495053; |
| 766 |
| 1263 // kTestAcceptableCerts doesn't actually match any public keys and is used | 767 // kTestAcceptableCerts doesn't actually match any public keys and is used |
| 1264 // with "pinningtest.appspot.com", below, to test if pinning is active. | 768 // with "pinningtest.appspot.com", below, to test if pinning is active. |
| 1265 static const char* const kTestAcceptableCerts[] = { | 769 static const char* const kTestAcceptableCerts[] = { |
| 1266 "sha1/AAAAAAAAAAAAAAAAAAAAAAAAAAA=", | 770 "sha1/AAAAAAAAAAAAAAAAAAAAAAAAAAA=", |
| 1267 NULL, | 771 NULL, |
| 1268 }; | 772 }; |
| 1269 #define kTestPins { \ | 773 #define kTestPins { \ |
| 1270 kTestAcceptableCerts, \ | 774 kTestAcceptableCerts, \ |
| 1271 kNoRejectedPublicKeys, \ | 775 kNoRejectedPublicKeys, \ |
| 1272 } | 776 } |
| (...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1514 return entry; | 1018 return entry; |
| 1515 } | 1019 } |
| 1516 } | 1020 } |
| 1517 } | 1021 } |
| 1518 | 1022 |
| 1519 return NULL; | 1023 return NULL; |
| 1520 } | 1024 } |
| 1521 | 1025 |
| 1522 // static | 1026 // static |
| 1523 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, | 1027 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, |
| 1524 bool sni_available) { | 1028 bool sni_enabled) { |
| 1525 std::string canonicalized_host = CanonicalizeHost(host); | 1029 std::string canonicalized_host = CanonicalizeHost(host); |
| 1526 const struct HSTSPreload* entry = | 1030 const struct HSTSPreload* entry = |
| 1527 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 1031 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
| 1528 | 1032 |
| 1529 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 1033 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
| 1530 return true; | 1034 return true; |
| 1531 | 1035 |
| 1532 if (sni_available) { | 1036 if (sni_enabled) { |
| 1533 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 1037 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, |
| 1534 kNumPreloadedSNISTS); | 1038 kNumPreloadedSNISTS); |
| 1535 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 1039 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) |
| 1536 return true; | 1040 return true; |
| 1537 } | 1041 } |
| 1538 | 1042 |
| 1539 return false; | 1043 return false; |
| 1540 } | 1044 } |
| 1541 | 1045 |
| 1542 // static | 1046 // static |
| 1543 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { | 1047 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { |
| 1544 std::string canonicalized_host = CanonicalizeHost(host); | 1048 std::string canonicalized_host = CanonicalizeHost(host); |
| 1545 | 1049 |
| 1546 const struct HSTSPreload* entry = | 1050 const struct HSTSPreload* entry = |
| 1547 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 1051 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
| 1548 | 1052 |
| 1549 if (!entry) { | 1053 if (!entry) { |
| 1550 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 1054 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, |
| 1551 kNumPreloadedSNISTS); | 1055 kNumPreloadedSNISTS); |
| 1552 } | 1056 } |
| 1553 | 1057 |
| 1554 DCHECK(entry); | 1058 DCHECK(entry); |
| 1555 DCHECK(entry->pins.required_hashes); | 1059 DCHECK(entry->pins.required_hashes); |
| 1556 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | 1060 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); |
| 1557 | 1061 |
| 1558 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | 1062 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", |
| 1559 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | 1063 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); |
| 1560 } | 1064 } |
| 1561 | 1065 |
| 1562 // IsPreloadedSTS returns true if the canonicalized hostname should always be | 1066 bool TransportSecurityState::GetStaticDomainState( |
| 1563 // considered to have STS enabled. | |
| 1564 bool TransportSecurityState::IsPreloadedSTS( | |
| 1565 const std::string& canonicalized_host, | 1067 const std::string& canonicalized_host, |
| 1566 bool sni_available, | 1068 bool sni_enabled, |
| 1567 DomainState* out) { | 1069 DomainState* out) { |
| 1568 DCHECK(CalledOnValidThread()); | 1070 DCHECK(CalledOnValidThread()); |
| 1569 | 1071 |
| 1570 out->preloaded = true; | 1072 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
| 1571 out->mode = DomainState::MODE_STRICT; | |
| 1572 out->include_subdomains = false; | 1073 out->include_subdomains = false; |
| 1573 | 1074 |
| 1574 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 1075 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
| 1575 std::string host_sub_chunk(&canonicalized_host[i], | 1076 std::string host_sub_chunk(&canonicalized_host[i], |
| 1576 canonicalized_host.size() - i); | 1077 canonicalized_host.size() - i); |
| 1577 out->domain = DNSDomainToString(host_sub_chunk); | 1078 out->domain = DNSDomainToString(host_sub_chunk); |
| 1578 std::string hashed_host(HashHost(host_sub_chunk)); | 1079 std::string hashed_host(HashHost(host_sub_chunk)); |
| 1579 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { | 1080 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { |
| 1580 *out = forced_hosts_[hashed_host]; | 1081 *out = forced_hosts_[hashed_host]; |
| 1581 out->domain = DNSDomainToString(host_sub_chunk); | 1082 out->domain = DNSDomainToString(host_sub_chunk); |
| 1582 out->preloaded = true; | |
| 1583 return true; | 1083 return true; |
| 1584 } | 1084 } |
| 1585 bool ret; | 1085 bool ret; |
| 1586 if (HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, | 1086 if (HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, |
| 1587 &ret)) { | 1087 &ret)) { |
| 1588 return ret; | 1088 return ret; |
| 1589 } | 1089 } |
| 1590 if (sni_available && | 1090 if (sni_enabled && |
| 1591 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, | 1091 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, |
| 1592 out, &ret)) { | 1092 out, &ret)) { |
| 1593 return ret; | 1093 return ret; |
| 1594 } | 1094 } |
| 1595 } | 1095 } |
| 1596 | 1096 |
| 1597 return false; | 1097 return false; |
| 1598 } | 1098 } |
| 1599 | 1099 |
| 1600 static std::string HashesToBase64String( | 1100 static std::string HashesToBase64String( |
| 1601 const FingerprintVector& hashes) { | 1101 const FingerprintVector& hashes) { |
| 1602 std::vector<std::string> hashes_strs; | 1102 std::vector<std::string> hashes_strs; |
| 1603 for (FingerprintVector::const_iterator | 1103 for (FingerprintVector::const_iterator |
| 1604 i = hashes.begin(); i != hashes.end(); i++) { | 1104 i = hashes.begin(); i != hashes.end(); i++) { |
| 1605 std::string s; | 1105 std::string s; |
| 1606 const std::string hash_str(reinterpret_cast<const char*>(i->data), | 1106 const std::string hash_str(reinterpret_cast<const char*>(i->data), |
| 1607 sizeof(i->data)); | 1107 sizeof(i->data)); |
| 1608 base::Base64Encode(hash_str, &s); | 1108 base::Base64Encode(hash_str, &s); |
| 1609 hashes_strs.push_back(s); | 1109 hashes_strs.push_back(s); |
| 1610 } | 1110 } |
| 1611 | 1111 |
| 1612 return JoinString(hashes_strs, ','); | 1112 return JoinString(hashes_strs, ','); |
| 1613 } | 1113 } |
| 1614 | 1114 |
| 1615 TransportSecurityState::DomainState::DomainState() | 1115 TransportSecurityState::DomainState::DomainState() |
| 1616 : mode(MODE_STRICT), | 1116 : upgrade_mode(MODE_FORCE_HTTPS), |
| 1617 created(base::Time::Now()), | 1117 created(base::Time::Now()), |
| 1618 include_subdomains(false), | 1118 include_subdomains(false) { |
| 1619 preloaded(false) { | |
| 1620 } | 1119 } |
| 1621 | 1120 |
| 1622 TransportSecurityState::DomainState::~DomainState() { | 1121 TransportSecurityState::DomainState::~DomainState() { |
| 1623 } | 1122 } |
| 1624 | 1123 |
| 1625 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( | 1124 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( |
| 1626 const FingerprintVector& hashes) { | 1125 const FingerprintVector& hashes) const { |
| 1627 | 1126 if (HashesIntersect(bad_static_spki_hashes, hashes)) { |
| 1628 if (HashesIntersect(bad_preloaded_spki_hashes, hashes)) { | |
| 1629 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 1127 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
| 1630 << ". Validated chain: " << HashesToBase64String(hashes) | 1128 << ". Validated chain: " << HashesToBase64String(hashes) |
| 1631 << ", matches one or more bad hashes: " | 1129 << ", matches one or more bad hashes: " |
| 1632 << HashesToBase64String(bad_preloaded_spki_hashes); | 1130 << HashesToBase64String(bad_static_spki_hashes); |
| 1633 return false; | 1131 return false; |
| 1634 } | 1132 } |
| 1635 | 1133 |
| 1636 if (!(dynamic_spki_hashes.empty() && preloaded_spki_hashes.empty()) && | 1134 if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) && |
| 1637 !HashesIntersect(dynamic_spki_hashes, hashes) && | 1135 !HashesIntersect(dynamic_spki_hashes, hashes) && |
| 1638 !HashesIntersect(preloaded_spki_hashes, hashes)) { | 1136 !HashesIntersect(static_spki_hashes, hashes)) { |
| 1639 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 1137 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
| 1640 << ". Validated chain: " << HashesToBase64String(hashes) | 1138 << ". Validated chain: " << HashesToBase64String(hashes) |
| 1641 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) | 1139 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) |
| 1642 << " or: " << HashesToBase64String(preloaded_spki_hashes); | 1140 << " or: " << HashesToBase64String(static_spki_hashes); |
| 1643 | 1141 |
| 1644 return false; | 1142 return false; |
| 1645 } | 1143 } |
| 1646 | 1144 |
| 1647 return true; | 1145 return true; |
| 1648 } | 1146 } |
| 1649 | 1147 |
| 1650 bool TransportSecurityState::DomainState::IsMoreStrict( | |
| 1651 const TransportSecurityState::DomainState& other) { | |
| 1652 if (this->dynamic_spki_hashes.empty() && !other.dynamic_spki_hashes.empty()) | |
| 1653 return false; | |
| 1654 | |
| 1655 if (!this->include_subdomains && other.include_subdomains) | |
| 1656 return false; | |
| 1657 | |
| 1658 return true; | |
| 1659 } | |
| 1660 | |
| 1661 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() | 1148 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() |
| 1662 const { | 1149 const { |
| 1663 return mode == MODE_STRICT; | 1150 return upgrade_mode == MODE_FORCE_HTTPS; |
| 1664 } | 1151 } |
| 1665 | 1152 |
| 1666 } // namespace | 1153 } // namespace |
| OLD | NEW |