| 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) { |
| 55 } |
| 56 |
| 64 void TransportSecurityState::SetDelegate( | 57 void TransportSecurityState::SetDelegate( |
| 65 TransportSecurityState::Delegate* delegate) { | 58 TransportSecurityState::Delegate* delegate) { |
| 66 delegate_ = delegate; | 59 delegate_ = delegate; |
| 67 } | 60 } |
| 68 | 61 |
| 69 void TransportSecurityState::EnableHost(const std::string& host, | 62 void TransportSecurityState::EnableHost(const std::string& host, |
| 70 const DomainState& state) { | 63 const DomainState& state) { |
| 71 DCHECK(CalledOnValidThread()); | 64 DCHECK(CalledOnValidThread()); |
| 72 | 65 |
| 73 const std::string canonicalized_host = CanonicalizeHost(host); | 66 const std::string canonicalized_host = CanonicalizeHost(host); |
| 74 if (canonicalized_host.empty()) | 67 if (canonicalized_host.empty()) |
| 75 return; | 68 return; |
| 76 | 69 |
| 77 // Only override a preloaded state if the new state describes a more strict | |
| 78 // policy. TODO(palmer): Reconsider this? | |
| 79 DomainState existing_state; | 70 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 | 71 |
| 86 // Use the original creation date if we already have this host. | 72 // Use the original creation date if we already have this host. (But note |
| 73 // that statically-defined states have no |created| date. Therefore, we do |
| 74 // not bother to search the SNI-only static states.) |
| 87 DomainState state_copy(state); | 75 DomainState state_copy(state); |
| 88 if (GetDomainState(&existing_state, host, true) && | 76 if (GetDomainState(host, false /* sni_enabled */, &existing_state) && |
| 89 !existing_state.created.is_null()) { | 77 !existing_state.created.is_null()) { |
| 90 state_copy.created = existing_state.created; | 78 state_copy.created = existing_state.created; |
| 91 } | 79 } |
| 92 | 80 |
| 93 // We don't store these values. | 81 // No need to store this value since it is redundant. (|canonicalized_host| |
| 94 state_copy.preloaded = false; | 82 // is the map key.) |
| 95 state_copy.domain.clear(); | 83 state_copy.domain.clear(); |
| 96 | 84 |
| 97 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; | 85 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; |
| 98 DirtyNotify(); | 86 DirtyNotify(); |
| 99 } | 87 } |
| 100 | 88 |
| 101 bool TransportSecurityState::DeleteHost(const std::string& host) { | 89 bool TransportSecurityState::DeleteHost(const std::string& host) { |
| 102 DCHECK(CalledOnValidThread()); | 90 DCHECK(CalledOnValidThread()); |
| 103 | 91 |
| 104 const std::string canonicalized_host = CanonicalizeHost(host); | 92 const std::string canonicalized_host = CanonicalizeHost(host); |
| 105 if (canonicalized_host.empty()) | 93 if (canonicalized_host.empty()) |
| 106 return false; | 94 return false; |
| 107 | 95 |
| 108 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find( | 96 std::map<std::string, DomainState>::iterator i = enabled_hosts_.find( |
| 109 HashHost(canonicalized_host)); | 97 HashHost(canonicalized_host)); |
| 110 if (i != enabled_hosts_.end()) { | 98 if (i != enabled_hosts_.end()) { |
| 111 enabled_hosts_.erase(i); | 99 enabled_hosts_.erase(i); |
| 112 DirtyNotify(); | 100 DirtyNotify(); |
| 113 return true; | 101 return true; |
| 114 } | 102 } |
| 115 return false; | 103 return false; |
| 116 } | 104 } |
| 117 | 105 |
| 118 bool TransportSecurityState::HasPinsForHost(DomainState* result, | 106 bool TransportSecurityState::GetDomainState(const std::string& host, |
| 119 const std::string& host, | 107 bool sni_enabled, |
| 120 bool sni_available) { | 108 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()); | 109 DCHECK(CalledOnValidThread()); |
| 140 | 110 |
| 141 DomainState state; | 111 DomainState state; |
| 142 const std::string canonicalized_host = CanonicalizeHost(host); | 112 const std::string canonicalized_host = CanonicalizeHost(host); |
| 143 if (canonicalized_host.empty()) | 113 if (canonicalized_host.empty()) |
| 144 return false; | 114 return false; |
| 145 | 115 |
| 146 bool has_preload = IsPreloadedSTS(canonicalized_host, sni_available, &state); | 116 bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled, |
| 117 &state); |
| 147 std::string canonicalized_preload = CanonicalizeHost(state.domain); | 118 std::string canonicalized_preload = CanonicalizeHost(state.domain); |
| 148 | 119 |
| 149 base::Time current_time(base::Time::Now()); | 120 base::Time current_time(base::Time::Now()); |
| 150 | 121 |
| 151 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 122 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
| 152 std::string host_sub_chunk(&canonicalized_host[i], | 123 std::string host_sub_chunk(&canonicalized_host[i], |
| 153 canonicalized_host.size() - i); | 124 canonicalized_host.size() - i); |
| 154 // Exact match of a preload always wins. | 125 // Exact match of a preload always wins. |
| 155 if (has_preload && host_sub_chunk == canonicalized_preload) { | 126 if (has_preload && host_sub_chunk == canonicalized_preload) { |
| 156 *result = state; | 127 *result = state; |
| 157 return true; | 128 return true; |
| 158 } | 129 } |
| 159 | 130 |
| 160 std::map<std::string, DomainState>::iterator j = | 131 std::map<std::string, DomainState>::iterator j = |
| 161 enabled_hosts_.find(HashHost(host_sub_chunk)); | 132 enabled_hosts_.find(HashHost(host_sub_chunk)); |
| 162 if (j == enabled_hosts_.end()) | 133 if (j == enabled_hosts_.end()) |
| 163 continue; | 134 continue; |
| 164 | 135 |
| 165 if (current_time > j->second.expiry && | 136 if (current_time > j->second.upgrade_expiry && |
| 166 current_time > j->second.dynamic_spki_hashes_expiry) { | 137 current_time > j->second.dynamic_spki_hashes_expiry) { |
| 167 enabled_hosts_.erase(j); | 138 enabled_hosts_.erase(j); |
| 168 DirtyNotify(); | 139 DirtyNotify(); |
| 169 continue; | 140 continue; |
| 170 } | 141 } |
| 171 | 142 |
| 172 state = j->second; | 143 state = j->second; |
| 173 state.domain = DNSDomainToString(host_sub_chunk); | 144 state.domain = DNSDomainToString(host_sub_chunk); |
| 174 | 145 |
| 175 // Succeed if we matched the domain exactly or if subdomain matches are | 146 // 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) || | 254 if (!base::Base64Decode(unquoted, &decoded) || |
| 284 decoded.size() != arraysize(fp.data)) { | 255 decoded.size() != arraysize(fp.data)) { |
| 285 return false; | 256 return false; |
| 286 } | 257 } |
| 287 | 258 |
| 288 memcpy(fp.data, decoded.data(), arraysize(fp.data)); | 259 memcpy(fp.data, decoded.data(), arraysize(fp.data)); |
| 289 fingerprints->push_back(fp); | 260 fingerprints->push_back(fp); |
| 290 return true; | 261 return true; |
| 291 } | 262 } |
| 292 | 263 |
| 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 { | 264 struct FingerprintsEqualPredicate { |
| 311 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : | 265 explicit FingerprintsEqualPredicate(const SHA1Fingerprint& fingerprint) : |
| 312 fingerprint_(fingerprint) {} | 266 fingerprint_(fingerprint) {} |
| 313 | 267 |
| 314 bool operator()(const SHA1Fingerprint& other) const { | 268 bool operator()(const SHA1Fingerprint& other) const { |
| 315 return fingerprint_.Equals(other); | 269 return fingerprint_.Equals(other); |
| 316 } | 270 } |
| 317 | 271 |
| 318 const SHA1Fingerprint& fingerprint_; | 272 const SHA1Fingerprint& fingerprint_; |
| 319 }; | 273 }; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 361 if (from_cert_chain.empty()) | 315 if (from_cert_chain.empty()) |
| 362 return false; | 316 return false; |
| 363 | 317 |
| 364 return IsBackupPinPresent(pins, from_cert_chain) && | 318 return IsBackupPinPresent(pins, from_cert_chain) && |
| 365 HashesIntersect(pins, from_cert_chain); | 319 HashesIntersect(pins, from_cert_chain); |
| 366 } | 320 } |
| 367 | 321 |
| 368 // "Public-Key-Pins" ":" | 322 // "Public-Key-Pins" ":" |
| 369 // "max-age" "=" delta-seconds ";" | 323 // "max-age" "=" delta-seconds ";" |
| 370 // "pin-" algo "=" base64 [ ";" ... ] | 324 // "pin-" algo "=" base64 [ ";" ... ] |
| 371 // | 325 bool TransportSecurityState::DomainState::ParsePinsHeader( |
| 372 // static | 326 const base::Time& now, |
| 373 bool TransportSecurityState::ParsePinsHeader(const std::string& value, | 327 const std::string& value, |
| 374 const SSLInfo& ssl_info, | 328 const SSLInfo& ssl_info) { |
| 375 DomainState* state) { | |
| 376 bool parsed_max_age = false; | 329 bool parsed_max_age = false; |
| 377 int max_age = 0; | 330 int max_age_candidate = 0; |
| 378 FingerprintVector pins; | 331 FingerprintVector pins; |
| 379 | 332 |
| 380 std::string source = value; | 333 std::string source = value; |
| 381 | 334 |
| 382 while (!source.empty()) { | 335 while (!source.empty()) { |
| 383 StringPair semicolon = Split(source, ';'); | 336 StringPair semicolon = Split(source, ';'); |
| 384 semicolon.first = Strip(semicolon.first); | 337 semicolon.first = Strip(semicolon.first); |
| 385 semicolon.second = Strip(semicolon.second); | 338 semicolon.second = Strip(semicolon.second); |
| 386 StringPair equals = Split(semicolon.first, '='); | 339 StringPair equals = Split(semicolon.first, '='); |
| 387 equals.first = Strip(equals.first); | 340 equals.first = Strip(equals.first); |
| 388 equals.second = Strip(equals.second); | 341 equals.second = Strip(equals.second); |
| 389 | 342 |
| 390 if (LowerCaseEqualsASCII(equals.first, "max-age")) { | 343 if (LowerCaseEqualsASCII(equals.first, "max-age")) { |
| 391 if (equals.second.empty() || | 344 if (equals.second.empty() || |
| 392 !MaxAgeToInt(equals.second.begin(), equals.second.end(), &max_age)) { | 345 !MaxAgeToInt(equals.second.begin(), equals.second.end(), |
| 346 &max_age_candidate)) { |
| 393 return false; | 347 return false; |
| 394 } | 348 } |
| 395 if (max_age > kMaxHSTSAgeSecs) | 349 if (max_age_candidate > kMaxHSTSAgeSecs) |
| 396 max_age = kMaxHSTSAgeSecs; | 350 max_age_candidate = kMaxHSTSAgeSecs; |
| 397 parsed_max_age = true; | 351 parsed_max_age = true; |
| 398 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { | 352 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha1")) { |
| 399 if (!ParseAndAppendPin(equals.second, &pins)) | 353 if (!ParseAndAppendPin(equals.second, &pins)) |
| 400 return false; | 354 return false; |
| 401 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { | 355 } else if (LowerCaseEqualsASCII(equals.first, "pin-sha256")) { |
| 402 // TODO(palmer) | 356 // TODO(palmer) |
| 403 } else { | 357 } else { |
| 404 // Silently ignore unknown directives for forward compatibility. | 358 // Silently ignore unknown directives for forward compatibility. |
| 405 } | 359 } |
| 406 | 360 |
| 407 source = semicolon.second; | 361 source = semicolon.second; |
| 408 } | 362 } |
| 409 | 363 |
| 410 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) | 364 if (!parsed_max_age || !IsPinListValid(pins, ssl_info)) |
| 411 return false; | 365 return false; |
| 412 | 366 |
| 413 state->max_age = max_age; | 367 dynamic_spki_hashes_expiry = |
| 414 state->dynamic_spki_hashes_expiry = | 368 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 415 base::Time::Now() + base::TimeDelta::FromSeconds(max_age); | |
| 416 | 369 |
| 417 state->dynamic_spki_hashes.clear(); | 370 dynamic_spki_hashes.clear(); |
| 418 if (max_age > 0) { | 371 if (max_age_candidate > 0) { |
| 419 for (FingerprintVector::const_iterator i = pins.begin(); | 372 for (FingerprintVector::const_iterator i = pins.begin(); |
| 420 i != pins.end(); i++) { | 373 i != pins.end(); ++i) { |
| 421 state->dynamic_spki_hashes.push_back(*i); | 374 dynamic_spki_hashes.push_back(*i); |
| 422 } | 375 } |
| 423 } | 376 } |
| 424 | 377 |
| 425 return true; | 378 return true; |
| 426 } | 379 } |
| 427 | 380 |
| 428 // "Strict-Transport-Security" ":" | 381 // "Strict-Transport-Security" ":" |
| 429 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] | 382 // "max-age" "=" delta-seconds [ ";" "includeSubDomains" ] |
| 430 // | 383 bool TransportSecurityState::DomainState::ParseSTSHeader( |
| 431 // static | 384 const base::Time& now, |
| 432 bool TransportSecurityState::ParseHeader(const std::string& value, | 385 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; | 386 int max_age_candidate = 0; |
| 439 | 387 |
| 440 enum ParserState { | 388 enum ParserState { |
| 441 START, | 389 START, |
| 442 AFTER_MAX_AGE_LABEL, | 390 AFTER_MAX_AGE_LABEL, |
| 443 AFTER_MAX_AGE_EQUALS, | 391 AFTER_MAX_AGE_EQUALS, |
| 444 AFTER_MAX_AGE, | 392 AFTER_MAX_AGE, |
| 445 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER, | 393 AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER, |
| 446 AFTER_INCLUDE_SUBDOMAINS, | 394 AFTER_INCLUDE_SUBDOMAINS, |
| 447 } state = START; | 395 } state = START; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 504 } | 452 } |
| 505 } | 453 } |
| 506 | 454 |
| 507 // We've consumed all the input. Let's see what state we ended up in. | 455 // We've consumed all the input. Let's see what state we ended up in. |
| 508 switch (state) { | 456 switch (state) { |
| 509 case START: | 457 case START: |
| 510 case AFTER_MAX_AGE_LABEL: | 458 case AFTER_MAX_AGE_LABEL: |
| 511 case AFTER_MAX_AGE_EQUALS: | 459 case AFTER_MAX_AGE_EQUALS: |
| 512 return false; | 460 return false; |
| 513 case AFTER_MAX_AGE: | 461 case AFTER_MAX_AGE: |
| 514 *max_age = max_age_candidate; | 462 upgrade_expiry = |
| 515 *include_subdomains = false; | 463 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 464 include_subdomains = false; |
| 465 upgrade_mode = MODE_FORCE_HTTPS; |
| 516 return true; | 466 return true; |
| 517 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: | 467 case AFTER_MAX_AGE_INCLUDE_SUB_DOMAINS_DELIMITER: |
| 518 return false; | 468 return false; |
| 519 case AFTER_INCLUDE_SUBDOMAINS: | 469 case AFTER_INCLUDE_SUBDOMAINS: |
| 520 *max_age = max_age_candidate; | 470 upgrade_expiry = |
| 521 *include_subdomains = true; | 471 now + base::TimeDelta::FromSeconds(max_age_candidate); |
| 472 include_subdomains = true; |
| 473 upgrade_mode = MODE_FORCE_HTTPS; |
| 522 return true; | 474 return true; |
| 523 default: | 475 default: |
| 524 NOTREACHED(); | 476 NOTREACHED(); |
| 525 return false; | 477 return false; |
| 526 } | 478 } |
| 527 } | 479 } |
| 528 | 480 |
| 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, | 481 static bool AddHash(const std::string& type_and_base64, |
| 894 FingerprintVector* out) { | 482 FingerprintVector* out) { |
| 895 SHA1Fingerprint hash; | 483 SHA1Fingerprint hash; |
| 896 | 484 |
| 897 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) | 485 if (!TransportSecurityState::ParsePin(type_and_base64, &hash)) |
| 898 return false; | 486 return false; |
| 899 | 487 |
| 900 out->push_back(hash); | 488 out->push_back(hash); |
| 901 return true; | 489 return true; |
| 902 } | 490 } |
| 903 | 491 |
| 904 static void SPKIHashesFromListValue(FingerprintVector* hashes, | 492 TransportSecurityState::~TransportSecurityState() {} |
| 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() { | |
| 1013 } | |
| 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 |
| 731 void TransportSecurityState::AddOrUpdateEnabledHosts(std::string hashed_host, |
| 732 const DomainState& state) { |
| 733 enabled_hosts_[hashed_host] = state; |
| 734 } |
| 735 |
| 736 void TransportSecurityState::AddOrUpdateForcedHosts(std::string hashed_host, |
| 737 const DomainState& state) { |
| 738 forced_hosts_[hashed_host] = state; |
| 739 } |
| 740 |
| 1256 static std::string HashesToBase64String( | 741 static std::string HashesToBase64String( |
| 1257 const FingerprintVector& hashes) { | 742 const FingerprintVector& hashes) { |
| 1258 std::vector<std::string> hashes_strs; | 743 std::vector<std::string> hashes_strs; |
| 1259 for (FingerprintVector::const_iterator | 744 for (FingerprintVector::const_iterator |
| 1260 i = hashes.begin(); i != hashes.end(); i++) { | 745 i = hashes.begin(); i != hashes.end(); i++) { |
| 1261 std::string s; | 746 std::string s; |
| 1262 const std::string hash_str(reinterpret_cast<const char*>(i->data), | 747 const std::string hash_str(reinterpret_cast<const char*>(i->data), |
| 1263 sizeof(i->data)); | 748 sizeof(i->data)); |
| 1264 base::Base64Encode(hash_str, &s); | 749 base::Base64Encode(hash_str, &s); |
| 1265 hashes_strs.push_back(s); | 750 hashes_strs.push_back(s); |
| 1266 } | 751 } |
| 1267 | 752 |
| 1268 return JoinString(hashes_strs, ','); | 753 return JoinString(hashes_strs, ','); |
| 1269 } | 754 } |
| 1270 | 755 |
| 1271 TransportSecurityState::DomainState::DomainState() | 756 TransportSecurityState::DomainState::DomainState() |
| 1272 : mode(MODE_STRICT), | 757 : upgrade_mode(MODE_FORCE_HTTPS), |
| 1273 created(base::Time::Now()), | 758 created(base::Time::Now()), |
| 1274 include_subdomains(false), | 759 include_subdomains(false) { |
| 1275 preloaded(false) { | |
| 1276 } | 760 } |
| 1277 | 761 |
| 1278 TransportSecurityState::DomainState::~DomainState() { | 762 TransportSecurityState::DomainState::~DomainState() { |
| 1279 } | 763 } |
| 1280 | 764 |
| 1281 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( | 765 bool TransportSecurityState::DomainState::IsChainOfPublicKeysPermitted( |
| 1282 const FingerprintVector& hashes) { | 766 const FingerprintVector& hashes) const { |
| 1283 | 767 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 | 768 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
| 1286 << ". Validated chain: " << HashesToBase64String(hashes) | 769 << ". Validated chain: " << HashesToBase64String(hashes) |
| 1287 << ", matches one or more bad hashes: " | 770 << ", matches one or more bad hashes: " |
| 1288 << HashesToBase64String(bad_preloaded_spki_hashes); | 771 << HashesToBase64String(bad_static_spki_hashes); |
| 1289 return false; | 772 return false; |
| 1290 } | 773 } |
| 1291 | 774 |
| 1292 if (!(dynamic_spki_hashes.empty() && preloaded_spki_hashes.empty()) && | 775 if (!(dynamic_spki_hashes.empty() && static_spki_hashes.empty()) && |
| 1293 !HashesIntersect(dynamic_spki_hashes, hashes) && | 776 !HashesIntersect(dynamic_spki_hashes, hashes) && |
| 1294 !HashesIntersect(preloaded_spki_hashes, hashes)) { | 777 !HashesIntersect(static_spki_hashes, hashes)) { |
| 1295 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 778 LOG(ERROR) << "Rejecting public key chain for domain " << domain |
| 1296 << ". Validated chain: " << HashesToBase64String(hashes) | 779 << ". Validated chain: " << HashesToBase64String(hashes) |
| 1297 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) | 780 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) |
| 1298 << " or: " << HashesToBase64String(preloaded_spki_hashes); | 781 << " or: " << HashesToBase64String(static_spki_hashes); |
| 1299 | 782 |
| 1300 return false; | 783 return false; |
| 1301 } | 784 } |
| 1302 | 785 |
| 1303 return true; | 786 return true; |
| 1304 } | 787 } |
| 1305 | 788 |
| 1306 bool TransportSecurityState::DomainState::IsMoreStrict( | 789 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() const { |
| 1307 const TransportSecurityState::DomainState& other) { | 790 return upgrade_mode == MODE_FORCE_HTTPS; |
| 1308 if (this->dynamic_spki_hashes.empty() && !other.dynamic_spki_hashes.empty()) | 791 } |
| 1309 return false; | |
| 1310 | 792 |
| 1311 if (!this->include_subdomains && other.include_subdomains) | 793 bool TransportSecurityState::DomainState::Equals( |
| 1312 return false; | 794 const DomainState& other) const { |
| 1313 | 795 // TODO(palmer): Implement this |
| 796 (void) other; |
| 1314 return true; | 797 return true; |
| 1315 } | 798 } |
| 1316 | 799 |
| 1317 bool TransportSecurityState::DomainState::ShouldRedirectHTTPToHTTPS() | 800 bool TransportSecurityState::DomainState::HasPins() const { |
| 1318 const { | 801 return static_spki_hashes.size() > 0 || |
| 1319 return mode == MODE_STRICT; | 802 bad_static_spki_hashes.size() > 0 || |
| 803 dynamic_spki_hashes.size() > 0; |
| 1320 } | 804 } |
| 1321 | 805 |
| 1322 } // namespace | 806 } // namespace |
| OLD | NEW |