| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/http/transport_security_state.h" | |
| 6 | |
| 7 #if defined(USE_OPENSSL) | |
| 8 #include <openssl/ecdsa.h> | |
| 9 #include <openssl/ssl.h> | |
| 10 #else // !defined(USE_OPENSSL) | |
| 11 #include <cryptohi.h> | |
| 12 #include <hasht.h> | |
| 13 #include <keyhi.h> | |
| 14 #include <nspr.h> | |
| 15 #include <pk11pub.h> | |
| 16 #endif | |
| 17 | |
| 18 #include <algorithm> | |
| 19 | |
| 20 #include "base/base64.h" | |
| 21 #include "base/build_time.h" | |
| 22 #include "base/logging.h" | |
| 23 #include "base/memory/scoped_ptr.h" | |
| 24 #include "base/metrics/histogram.h" | |
| 25 #include "base/metrics/sparse_histogram.h" | |
| 26 #include "base/sha1.h" | |
| 27 #include "base/strings/string_number_conversions.h" | |
| 28 #include "base/strings/string_util.h" | |
| 29 #include "base/strings/utf_string_conversions.h" | |
| 30 #include "base/time/time.h" | |
| 31 #include "base/values.h" | |
| 32 #include "crypto/sha2.h" | |
| 33 #include "net/base/dns_util.h" | |
| 34 #include "net/cert/x509_cert_types.h" | |
| 35 #include "net/cert/x509_certificate.h" | |
| 36 #include "net/http/http_security_headers.h" | |
| 37 #include "net/ssl/ssl_info.h" | |
| 38 #include "url/gurl.h" | |
| 39 | |
| 40 #if defined(USE_OPENSSL) | |
| 41 #include "crypto/openssl_util.h" | |
| 42 #endif | |
| 43 | |
| 44 namespace net { | |
| 45 | |
| 46 namespace { | |
| 47 | |
| 48 std::string HashesToBase64String(const HashValueVector& hashes) { | |
| 49 std::string str; | |
| 50 for (size_t i = 0; i != hashes.size(); ++i) { | |
| 51 if (i != 0) | |
| 52 str += ","; | |
| 53 str += hashes[i].ToString(); | |
| 54 } | |
| 55 return str; | |
| 56 } | |
| 57 | |
| 58 std::string HashHost(const std::string& canonicalized_host) { | |
| 59 char hashed[crypto::kSHA256Length]; | |
| 60 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); | |
| 61 return std::string(hashed, sizeof(hashed)); | |
| 62 } | |
| 63 | |
| 64 // Returns true if the intersection of |a| and |b| is not empty. If either | |
| 65 // |a| or |b| is empty, returns false. | |
| 66 bool HashesIntersect(const HashValueVector& a, | |
| 67 const HashValueVector& b) { | |
| 68 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { | |
| 69 HashValueVector::const_iterator j = | |
| 70 std::find_if(b.begin(), b.end(), HashValuesEqual(*i)); | |
| 71 if (j != b.end()) | |
| 72 return true; | |
| 73 } | |
| 74 return false; | |
| 75 } | |
| 76 | |
| 77 bool AddHash(const char* sha1_hash, | |
| 78 HashValueVector* out) { | |
| 79 HashValue hash(HASH_VALUE_SHA1); | |
| 80 memcpy(hash.data(), sha1_hash, hash.size()); | |
| 81 out->push_back(hash); | |
| 82 return true; | |
| 83 } | |
| 84 | |
| 85 } // namespace | |
| 86 | |
| 87 TransportSecurityState::TransportSecurityState() | |
| 88 : delegate_(NULL), enable_static_pins_(true) { | |
| 89 // Static pinning is only enabled for official builds to make sure that | |
| 90 // others don't end up with pins that cannot be easily updated. | |
| 91 #if !defined(OFFICIAL_BUILD) || defined(OS_ANDROID) || defined(OS_IOS) | |
| 92 enable_static_pins_ = false; | |
| 93 #endif | |
| 94 DCHECK(CalledOnValidThread()); | |
| 95 } | |
| 96 | |
| 97 TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state) | |
| 98 : iterator_(state.enabled_hosts_.begin()), | |
| 99 end_(state.enabled_hosts_.end()) { | |
| 100 } | |
| 101 | |
| 102 TransportSecurityState::Iterator::~Iterator() {} | |
| 103 | |
| 104 bool TransportSecurityState::ShouldSSLErrorsBeFatal(const std::string& host) { | |
| 105 DomainState state; | |
| 106 if (GetStaticDomainState(host, &state)) | |
| 107 return true; | |
| 108 return GetDynamicDomainState(host, &state); | |
| 109 } | |
| 110 | |
| 111 bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host) { | |
| 112 DomainState dynamic_state; | |
| 113 if (GetDynamicDomainState(host, &dynamic_state)) | |
| 114 return dynamic_state.ShouldUpgradeToSSL(); | |
| 115 | |
| 116 DomainState static_state; | |
| 117 if (GetStaticDomainState(host, &static_state) && | |
| 118 static_state.ShouldUpgradeToSSL()) { | |
| 119 return true; | |
| 120 } | |
| 121 | |
| 122 return false; | |
| 123 } | |
| 124 | |
| 125 bool TransportSecurityState::CheckPublicKeyPins( | |
| 126 const std::string& host, | |
| 127 bool is_issued_by_known_root, | |
| 128 const HashValueVector& public_key_hashes, | |
| 129 std::string* pinning_failure_log) { | |
| 130 // Perform pin validation if, and only if, all these conditions obtain: | |
| 131 // | |
| 132 // * the server's certificate chain chains up to a known root (i.e. not a | |
| 133 // user-installed trust anchor); and | |
| 134 // * the server actually has public key pins. | |
| 135 if (!is_issued_by_known_root || !HasPublicKeyPins(host)) { | |
| 136 return true; | |
| 137 } | |
| 138 | |
| 139 bool pins_are_valid = CheckPublicKeyPinsImpl( | |
| 140 host, public_key_hashes, pinning_failure_log); | |
| 141 if (!pins_are_valid) { | |
| 142 LOG(ERROR) << *pinning_failure_log; | |
| 143 ReportUMAOnPinFailure(host); | |
| 144 } | |
| 145 | |
| 146 UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", pins_are_valid); | |
| 147 return pins_are_valid; | |
| 148 } | |
| 149 | |
| 150 bool TransportSecurityState::HasPublicKeyPins(const std::string& host) { | |
| 151 DomainState dynamic_state; | |
| 152 if (GetDynamicDomainState(host, &dynamic_state)) | |
| 153 return dynamic_state.HasPublicKeyPins(); | |
| 154 | |
| 155 DomainState static_state; | |
| 156 if (GetStaticDomainState(host, &static_state)) { | |
| 157 if (static_state.HasPublicKeyPins()) | |
| 158 return true; | |
| 159 } | |
| 160 | |
| 161 return false; | |
| 162 } | |
| 163 | |
| 164 void TransportSecurityState::SetDelegate( | |
| 165 TransportSecurityState::Delegate* delegate) { | |
| 166 DCHECK(CalledOnValidThread()); | |
| 167 delegate_ = delegate; | |
| 168 } | |
| 169 | |
| 170 void TransportSecurityState::AddHSTSInternal( | |
| 171 const std::string& host, | |
| 172 TransportSecurityState::DomainState::UpgradeMode upgrade_mode, | |
| 173 const base::Time& expiry, | |
| 174 bool include_subdomains) { | |
| 175 DCHECK(CalledOnValidThread()); | |
| 176 | |
| 177 // Copy-and-modify the existing DomainState for this host (if any). | |
| 178 DomainState domain_state; | |
| 179 const std::string canonicalized_host = CanonicalizeHost(host); | |
| 180 const std::string hashed_host = HashHost(canonicalized_host); | |
| 181 DomainStateMap::const_iterator i = enabled_hosts_.find(hashed_host); | |
| 182 if (i != enabled_hosts_.end()) | |
| 183 domain_state = i->second; | |
| 184 | |
| 185 domain_state.sts.last_observed = base::Time::Now(); | |
| 186 domain_state.sts.include_subdomains = include_subdomains; | |
| 187 domain_state.sts.expiry = expiry; | |
| 188 domain_state.sts.upgrade_mode = upgrade_mode; | |
| 189 EnableHost(host, domain_state); | |
| 190 } | |
| 191 | |
| 192 void TransportSecurityState::AddHPKPInternal(const std::string& host, | |
| 193 const base::Time& last_observed, | |
| 194 const base::Time& expiry, | |
| 195 bool include_subdomains, | |
| 196 const HashValueVector& hashes) { | |
| 197 DCHECK(CalledOnValidThread()); | |
| 198 | |
| 199 // Copy-and-modify the existing DomainState for this host (if any). | |
| 200 DomainState domain_state; | |
| 201 const std::string canonicalized_host = CanonicalizeHost(host); | |
| 202 const std::string hashed_host = HashHost(canonicalized_host); | |
| 203 DomainStateMap::const_iterator i = enabled_hosts_.find(hashed_host); | |
| 204 if (i != enabled_hosts_.end()) | |
| 205 domain_state = i->second; | |
| 206 | |
| 207 domain_state.pkp.last_observed = last_observed; | |
| 208 domain_state.pkp.expiry = expiry; | |
| 209 domain_state.pkp.include_subdomains = include_subdomains; | |
| 210 domain_state.pkp.spki_hashes = hashes; | |
| 211 EnableHost(host, domain_state); | |
| 212 } | |
| 213 | |
| 214 void TransportSecurityState::EnableHost(const std::string& host, | |
| 215 const DomainState& state) { | |
| 216 DCHECK(CalledOnValidThread()); | |
| 217 | |
| 218 const std::string canonicalized_host = CanonicalizeHost(host); | |
| 219 if (canonicalized_host.empty()) | |
| 220 return; | |
| 221 | |
| 222 DomainState state_copy(state); | |
| 223 // No need to store this value since it is redundant. (|canonicalized_host| | |
| 224 // is the map key.) | |
| 225 state_copy.sts.domain.clear(); | |
| 226 state_copy.pkp.domain.clear(); | |
| 227 | |
| 228 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; | |
| 229 DirtyNotify(); | |
| 230 } | |
| 231 | |
| 232 bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) { | |
| 233 DCHECK(CalledOnValidThread()); | |
| 234 | |
| 235 const std::string canonicalized_host = CanonicalizeHost(host); | |
| 236 if (canonicalized_host.empty()) | |
| 237 return false; | |
| 238 | |
| 239 DomainStateMap::iterator i = enabled_hosts_.find( | |
| 240 HashHost(canonicalized_host)); | |
| 241 if (i != enabled_hosts_.end()) { | |
| 242 enabled_hosts_.erase(i); | |
| 243 DirtyNotify(); | |
| 244 return true; | |
| 245 } | |
| 246 return false; | |
| 247 } | |
| 248 | |
| 249 void TransportSecurityState::ClearDynamicData() { | |
| 250 DCHECK(CalledOnValidThread()); | |
| 251 enabled_hosts_.clear(); | |
| 252 } | |
| 253 | |
| 254 void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) { | |
| 255 DCHECK(CalledOnValidThread()); | |
| 256 | |
| 257 bool dirtied = false; | |
| 258 DomainStateMap::iterator i = enabled_hosts_.begin(); | |
| 259 while (i != enabled_hosts_.end()) { | |
| 260 // Clear STS and PKP state independently. | |
| 261 if (i->second.sts.last_observed >= time) { | |
| 262 dirtied = true; | |
| 263 i->second.sts.upgrade_mode = DomainState::MODE_DEFAULT; | |
| 264 } | |
| 265 if (i->second.pkp.last_observed >= time) { | |
| 266 dirtied = true; | |
| 267 i->second.pkp.spki_hashes.clear(); | |
| 268 i->second.pkp.expiry = base::Time(); | |
| 269 } | |
| 270 | |
| 271 // If both are now invalid, drop the entry altogether. | |
| 272 if (!i->second.ShouldUpgradeToSSL() && !i->second.HasPublicKeyPins()) { | |
| 273 dirtied = true; | |
| 274 enabled_hosts_.erase(i++); | |
| 275 continue; | |
| 276 } | |
| 277 | |
| 278 ++i; | |
| 279 } | |
| 280 | |
| 281 if (dirtied) | |
| 282 DirtyNotify(); | |
| 283 } | |
| 284 | |
| 285 TransportSecurityState::~TransportSecurityState() { | |
| 286 DCHECK(CalledOnValidThread()); | |
| 287 } | |
| 288 | |
| 289 void TransportSecurityState::DirtyNotify() { | |
| 290 DCHECK(CalledOnValidThread()); | |
| 291 | |
| 292 if (delegate_) | |
| 293 delegate_->StateIsDirty(this); | |
| 294 } | |
| 295 | |
| 296 // static | |
| 297 std::string TransportSecurityState::CanonicalizeHost(const std::string& host) { | |
| 298 // We cannot perform the operations as detailed in the spec here as |host| | |
| 299 // has already undergone IDN processing before it reached us. Thus, we check | |
| 300 // that there are no invalid characters in the host and lowercase the result. | |
| 301 | |
| 302 std::string new_host; | |
| 303 if (!DNSDomainFromDot(host, &new_host)) { | |
| 304 // DNSDomainFromDot can fail if any label is > 63 bytes or if the whole | |
| 305 // name is >255 bytes. However, search terms can have those properties. | |
| 306 return std::string(); | |
| 307 } | |
| 308 | |
| 309 for (size_t i = 0; new_host[i]; i += new_host[i] + 1) { | |
| 310 const unsigned label_length = static_cast<unsigned>(new_host[i]); | |
| 311 if (!label_length) | |
| 312 break; | |
| 313 | |
| 314 for (size_t j = 0; j < label_length; ++j) { | |
| 315 new_host[i + 1 + j] = static_cast<char>(tolower(new_host[i + 1 + j])); | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 return new_host; | |
| 320 } | |
| 321 | |
| 322 // BitReader is a class that allows a bytestring to be read bit-by-bit. | |
| 323 class BitReader { | |
| 324 public: | |
| 325 BitReader(const uint8* bytes, size_t num_bits) | |
| 326 : bytes_(bytes), | |
| 327 num_bits_(num_bits), | |
| 328 num_bytes_((num_bits + 7) / 8), | |
| 329 current_byte_index_(0), | |
| 330 num_bits_used_(8) {} | |
| 331 | |
| 332 // Next sets |*out| to the next bit from the input. It returns false if no | |
| 333 // more bits are available or true otherwise. | |
| 334 bool Next(bool* out) { | |
| 335 if (num_bits_used_ == 8) { | |
| 336 if (current_byte_index_ >= num_bytes_) { | |
| 337 return false; | |
| 338 } | |
| 339 current_byte_ = bytes_[current_byte_index_++]; | |
| 340 num_bits_used_ = 0; | |
| 341 } | |
| 342 | |
| 343 *out = 1 & (current_byte_ >> (7 - num_bits_used_)); | |
| 344 num_bits_used_++; | |
| 345 return true; | |
| 346 } | |
| 347 | |
| 348 // Read sets the |num_bits| least-significant bits of |*out| to the value of | |
| 349 // the next |num_bits| bits from the input. It returns false if there are | |
| 350 // insufficient bits in the input or true otherwise. | |
| 351 bool Read(unsigned num_bits, uint32* out) { | |
| 352 DCHECK_LE(num_bits, 32u); | |
| 353 | |
| 354 uint32 ret = 0; | |
| 355 for (unsigned i = 0; i < num_bits; ++i) { | |
| 356 bool bit; | |
| 357 if (!Next(&bit)) { | |
| 358 return false; | |
| 359 } | |
| 360 ret |= static_cast<uint32>(bit) << (num_bits - 1 - i); | |
| 361 } | |
| 362 | |
| 363 *out = ret; | |
| 364 return true; | |
| 365 } | |
| 366 | |
| 367 // Unary sets |*out| to the result of decoding a unary value from the input. | |
| 368 // It returns false if there were insufficient bits in the input and true | |
| 369 // otherwise. | |
| 370 bool Unary(size_t* out) { | |
| 371 size_t ret = 0; | |
| 372 | |
| 373 for (;;) { | |
| 374 bool bit; | |
| 375 if (!Next(&bit)) { | |
| 376 return false; | |
| 377 } | |
| 378 if (!bit) { | |
| 379 break; | |
| 380 } | |
| 381 ret++; | |
| 382 } | |
| 383 | |
| 384 *out = ret; | |
| 385 return true; | |
| 386 } | |
| 387 | |
| 388 // Seek sets the current offest in the input to bit number |offset|. It | |
| 389 // returns true if |offset| is within the range of the input and false | |
| 390 // otherwise. | |
| 391 bool Seek(size_t offset) { | |
| 392 if (offset >= num_bits_) { | |
| 393 return false; | |
| 394 } | |
| 395 current_byte_index_ = offset / 8; | |
| 396 current_byte_ = bytes_[current_byte_index_++]; | |
| 397 num_bits_used_ = offset % 8; | |
| 398 return true; | |
| 399 } | |
| 400 | |
| 401 private: | |
| 402 const uint8* const bytes_; | |
| 403 const size_t num_bits_; | |
| 404 const size_t num_bytes_; | |
| 405 // current_byte_index_ contains the current byte offset in |bytes_|. | |
| 406 size_t current_byte_index_; | |
| 407 // current_byte_ contains the current byte of the input. | |
| 408 uint8 current_byte_; | |
| 409 // num_bits_used_ contains the number of bits of |current_byte_| that have | |
| 410 // been read. | |
| 411 unsigned num_bits_used_; | |
| 412 }; | |
| 413 | |
| 414 // HuffmanDecoder is a very simple Huffman reader. The input Huffman tree is | |
| 415 // simply encoded as a series of two-byte structures. The first byte determines | |
| 416 // the "0" pointer for that node and the second the "1" pointer. Each byte | |
| 417 // either has the MSB set, in which case the bottom 7 bits are the value for | |
| 418 // that position, or else the bottom seven bits contain the index of a node. | |
| 419 // | |
| 420 // The tree is decoded by walking rather than a table-driven approach. | |
| 421 class HuffmanDecoder { | |
| 422 public: | |
| 423 HuffmanDecoder(const uint8* tree, size_t tree_bytes) | |
| 424 : tree_(tree), | |
| 425 tree_bytes_(tree_bytes) {} | |
| 426 | |
| 427 bool Decode(BitReader* reader, char* out) { | |
| 428 const uint8* current = &tree_[tree_bytes_-2]; | |
| 429 | |
| 430 for (;;) { | |
| 431 bool bit; | |
| 432 if (!reader->Next(&bit)) { | |
| 433 return false; | |
| 434 } | |
| 435 | |
| 436 uint8 b = current[bit]; | |
| 437 if (b & 0x80) { | |
| 438 *out = static_cast<char>(b & 0x7f); | |
| 439 return true; | |
| 440 } | |
| 441 | |
| 442 unsigned offset = static_cast<unsigned>(b) * 2; | |
| 443 DCHECK_LT(offset, tree_bytes_); | |
| 444 if (offset >= tree_bytes_) { | |
| 445 return false; | |
| 446 } | |
| 447 | |
| 448 current = &tree_[offset]; | |
| 449 } | |
| 450 } | |
| 451 | |
| 452 private: | |
| 453 const uint8* const tree_; | |
| 454 const size_t tree_bytes_; | |
| 455 }; | |
| 456 | |
| 457 #include "net/http/transport_security_state_static.h" | |
| 458 | |
| 459 // PreloadResult is the result of resolving a specific name in the preloaded | |
| 460 // data. | |
| 461 struct PreloadResult { | |
| 462 uint32 pinset_id; | |
| 463 uint32 domain_id; | |
| 464 // hostname_offset contains the number of bytes from the start of the given | |
| 465 // hostname where the name of the matching entry starts. | |
| 466 size_t hostname_offset; | |
| 467 bool sts_include_subdomains; | |
| 468 bool pkp_include_subdomains; | |
| 469 bool force_https; | |
| 470 bool has_pins; | |
| 471 }; | |
| 472 | |
| 473 // DecodeHSTSPreloadRaw resolves |hostname| in the preloaded data. It returns | |
| 474 // false on internal error and true otherwise. After a successful return, | |
| 475 // |*out_found| is true iff a relevant entry has been found. If so, |*out| | |
| 476 // contains the details. | |
| 477 // | |
| 478 // Don't call this function, call DecodeHSTSPreload, below. | |
| 479 // | |
| 480 // Although this code should be robust, it never processes attacker-controlled | |
| 481 // data -- it only operates on the preloaded data built into the binary. | |
| 482 // | |
| 483 // The preloaded data is represented as a trie and matches the hostname | |
| 484 // backwards. Each node in the trie starts with a number of characters, which | |
| 485 // must match exactly. After that is a dispatch table which maps the next | |
| 486 // character in the hostname to another node in the trie. | |
| 487 // | |
| 488 // In the dispatch table, the zero character represents the "end of string" | |
| 489 // (which is the *beginning* of a hostname since we process it backwards). The | |
| 490 // value in that case is special -- rather than an offset to another trie node, | |
| 491 // it contains the HSTS information: whether subdomains are included, pinsets | |
| 492 // etc. If an "end of string" matches a period in the hostname then the | |
| 493 // information is remembered because, if no more specific node is found, then | |
| 494 // that information applies to the hostname. | |
| 495 // | |
| 496 // Dispatch tables are always given in order, but the "end of string" (zero) | |
| 497 // value always comes before an entry for '.'. | |
| 498 bool DecodeHSTSPreloadRaw(const std::string& hostname, | |
| 499 bool* out_found, | |
| 500 PreloadResult* out) { | |
| 501 HuffmanDecoder huffman(kHSTSHuffmanTree, sizeof(kHSTSHuffmanTree)); | |
| 502 BitReader reader(kPreloadedHSTSData, kPreloadedHSTSBits); | |
| 503 size_t bit_offset = kHSTSRootPosition; | |
| 504 static const char kEndOfString = 0; | |
| 505 static const char kEndOfTable = 127; | |
| 506 | |
| 507 *out_found = false; | |
| 508 | |
| 509 if (hostname.empty()) { | |
| 510 return true; | |
| 511 } | |
| 512 // hostname_offset contains one more than the index of the current character | |
| 513 // in the hostname that is being considered. It's one greater so that we can | |
| 514 // represent the position just before the beginning (with zero). | |
| 515 size_t hostname_offset = hostname.size(); | |
| 516 | |
| 517 for (;;) { | |
| 518 // Seek to the desired location. | |
| 519 if (!reader.Seek(bit_offset)) { | |
| 520 return false; | |
| 521 } | |
| 522 | |
| 523 // Decode the unary length of the common prefix. | |
| 524 size_t prefix_length; | |
| 525 if (!reader.Unary(&prefix_length)) { | |
| 526 return false; | |
| 527 } | |
| 528 | |
| 529 // Match each character in the prefix. | |
| 530 for (size_t i = 0; i < prefix_length; ++i) { | |
| 531 if (hostname_offset == 0) { | |
| 532 // We can't match the terminator with a prefix string. | |
| 533 return true; | |
| 534 } | |
| 535 | |
| 536 char c; | |
| 537 if (!huffman.Decode(&reader, &c)) { | |
| 538 return false; | |
| 539 } | |
| 540 if (hostname[hostname_offset - 1] != c) { | |
| 541 return true; | |
| 542 } | |
| 543 hostname_offset--; | |
| 544 } | |
| 545 | |
| 546 bool is_first_offset = true; | |
| 547 size_t current_offset = 0; | |
| 548 | |
| 549 // Next is the dispatch table. | |
| 550 for (;;) { | |
| 551 char c; | |
| 552 if (!huffman.Decode(&reader, &c)) { | |
| 553 return false; | |
| 554 } | |
| 555 if (c == kEndOfTable) { | |
| 556 // No exact match. | |
| 557 return true; | |
| 558 } | |
| 559 | |
| 560 if (c == kEndOfString) { | |
| 561 PreloadResult tmp; | |
| 562 if (!reader.Next(&tmp.sts_include_subdomains) || | |
| 563 !reader.Next(&tmp.force_https) || | |
| 564 !reader.Next(&tmp.has_pins)) { | |
| 565 return false; | |
| 566 } | |
| 567 | |
| 568 tmp.pkp_include_subdomains = tmp.sts_include_subdomains; | |
| 569 | |
| 570 if (tmp.has_pins) { | |
| 571 if (!reader.Read(4, &tmp.pinset_id) || | |
| 572 !reader.Read(9, &tmp.domain_id) || | |
| 573 (!tmp.sts_include_subdomains && | |
| 574 !reader.Next(&tmp.pkp_include_subdomains))) { | |
| 575 return false; | |
| 576 } | |
| 577 } | |
| 578 | |
| 579 tmp.hostname_offset = hostname_offset; | |
| 580 | |
| 581 if (hostname_offset == 0 || hostname[hostname_offset - 1] == '.') { | |
| 582 *out_found = | |
| 583 tmp.sts_include_subdomains || tmp.pkp_include_subdomains; | |
| 584 *out = tmp; | |
| 585 | |
| 586 if (hostname_offset > 0) { | |
| 587 out->force_https &= tmp.sts_include_subdomains; | |
| 588 } else { | |
| 589 *out_found = true; | |
| 590 return true; | |
| 591 } | |
| 592 } | |
| 593 | |
| 594 continue; | |
| 595 } | |
| 596 | |
| 597 // The entries in a dispatch table are in order thus we can tell if there | |
| 598 // will be no match if the current character past the one that we want. | |
| 599 if (hostname_offset == 0 || hostname[hostname_offset-1] < c) { | |
| 600 return true; | |
| 601 } | |
| 602 | |
| 603 if (is_first_offset) { | |
| 604 // The first offset is backwards from the current position. | |
| 605 uint32 jump_delta_bits; | |
| 606 uint32 jump_delta; | |
| 607 if (!reader.Read(5, &jump_delta_bits) || | |
| 608 !reader.Read(jump_delta_bits, &jump_delta)) { | |
| 609 return false; | |
| 610 } | |
| 611 | |
| 612 if (bit_offset < jump_delta) { | |
| 613 return false; | |
| 614 } | |
| 615 | |
| 616 current_offset = bit_offset - jump_delta; | |
| 617 is_first_offset = false; | |
| 618 } else { | |
| 619 // Subsequent offsets are forward from the target of the first offset. | |
| 620 uint32 is_long_jump; | |
| 621 if (!reader.Read(1, &is_long_jump)) { | |
| 622 return false; | |
| 623 } | |
| 624 | |
| 625 uint32 jump_delta; | |
| 626 if (!is_long_jump) { | |
| 627 if (!reader.Read(7, &jump_delta)) { | |
| 628 return false; | |
| 629 } | |
| 630 } else { | |
| 631 uint32 jump_delta_bits; | |
| 632 if (!reader.Read(4, &jump_delta_bits) || | |
| 633 !reader.Read(jump_delta_bits + 8, &jump_delta)) { | |
| 634 return false; | |
| 635 } | |
| 636 } | |
| 637 | |
| 638 current_offset += jump_delta; | |
| 639 if (current_offset >= bit_offset) { | |
| 640 return false; | |
| 641 } | |
| 642 } | |
| 643 | |
| 644 DCHECK_LT(0u, hostname_offset); | |
| 645 if (hostname[hostname_offset - 1] == c) { | |
| 646 bit_offset = current_offset; | |
| 647 hostname_offset--; | |
| 648 break; | |
| 649 } | |
| 650 } | |
| 651 } | |
| 652 } | |
| 653 | |
| 654 bool DecodeHSTSPreload(const std::string& hostname, | |
| 655 PreloadResult* out) { | |
| 656 bool found; | |
| 657 if (!DecodeHSTSPreloadRaw(hostname, &found, out)) { | |
| 658 DCHECK(false) << "Internal error in DecodeHSTSPreloadRaw for hostname " | |
| 659 << hostname; | |
| 660 return false; | |
| 661 } | |
| 662 | |
| 663 return found; | |
| 664 } | |
| 665 | |
| 666 bool TransportSecurityState::AddHSTSHeader(const std::string& host, | |
| 667 const std::string& value) { | |
| 668 DCHECK(CalledOnValidThread()); | |
| 669 | |
| 670 base::Time now = base::Time::Now(); | |
| 671 base::TimeDelta max_age; | |
| 672 bool include_subdomains; | |
| 673 if (!ParseHSTSHeader(value, &max_age, &include_subdomains)) { | |
| 674 return false; | |
| 675 } | |
| 676 | |
| 677 // Handle max-age == 0. | |
| 678 DomainState::UpgradeMode upgrade_mode; | |
| 679 if (max_age.InSeconds() == 0) { | |
| 680 upgrade_mode = DomainState::MODE_DEFAULT; | |
| 681 } else { | |
| 682 upgrade_mode = DomainState::MODE_FORCE_HTTPS; | |
| 683 } | |
| 684 | |
| 685 AddHSTSInternal(host, upgrade_mode, now + max_age, include_subdomains); | |
| 686 return true; | |
| 687 } | |
| 688 | |
| 689 bool TransportSecurityState::AddHPKPHeader(const std::string& host, | |
| 690 const std::string& value, | |
| 691 const SSLInfo& ssl_info) { | |
| 692 DCHECK(CalledOnValidThread()); | |
| 693 | |
| 694 base::Time now = base::Time::Now(); | |
| 695 base::TimeDelta max_age; | |
| 696 bool include_subdomains; | |
| 697 HashValueVector spki_hashes; | |
| 698 if (!ParseHPKPHeader(value, ssl_info.public_key_hashes, &max_age, | |
| 699 &include_subdomains, &spki_hashes)) { | |
| 700 return false; | |
| 701 } | |
| 702 // Handle max-age == 0. | |
| 703 if (max_age.InSeconds() == 0) | |
| 704 spki_hashes.clear(); | |
| 705 AddHPKPInternal(host, now, now + max_age, include_subdomains, spki_hashes); | |
| 706 return true; | |
| 707 } | |
| 708 | |
| 709 void TransportSecurityState::AddHSTS(const std::string& host, | |
| 710 const base::Time& expiry, | |
| 711 bool include_subdomains) { | |
| 712 DCHECK(CalledOnValidThread()); | |
| 713 AddHSTSInternal(host, DomainState::MODE_FORCE_HTTPS, expiry, | |
| 714 include_subdomains); | |
| 715 } | |
| 716 | |
| 717 void TransportSecurityState::AddHPKP(const std::string& host, | |
| 718 const base::Time& expiry, | |
| 719 bool include_subdomains, | |
| 720 const HashValueVector& hashes) { | |
| 721 DCHECK(CalledOnValidThread()); | |
| 722 AddHPKPInternal(host, base::Time::Now(), expiry, include_subdomains, hashes); | |
| 723 } | |
| 724 | |
| 725 // static | |
| 726 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host) { | |
| 727 PreloadResult result; | |
| 728 return DecodeHSTSPreload(host, &result) && result.has_pins && | |
| 729 kPinsets[result.pinset_id].accepted_pins == kGoogleAcceptableCerts; | |
| 730 } | |
| 731 | |
| 732 // static | |
| 733 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { | |
| 734 PreloadResult result; | |
| 735 if (!DecodeHSTSPreload(host, &result) || | |
| 736 !result.has_pins) { | |
| 737 return; | |
| 738 } | |
| 739 | |
| 740 DCHECK(result.domain_id != DOMAIN_NOT_PINNED); | |
| 741 | |
| 742 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
| 743 "Net.PublicKeyPinFailureDomain", result.domain_id); | |
| 744 } | |
| 745 | |
| 746 // static | |
| 747 bool TransportSecurityState::IsBuildTimely() { | |
| 748 // If the build metadata aren't embedded in the binary then we can't use the | |
| 749 // build time to determine if the build is timely, return true by default. If | |
| 750 // we're building an official build then keep using the build time, even if | |
| 751 // it's invalid it'd be a date in the past and this function will return | |
| 752 // false. | |
| 753 #if defined(DONT_EMBED_BUILD_METADATA) && !defined(OFFICIAL_BUILD) | |
| 754 return true; | |
| 755 #else | |
| 756 const base::Time build_time = base::GetBuildTime(); | |
| 757 // We consider built-in information to be timely for 10 weeks. | |
| 758 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; | |
| 759 #endif | |
| 760 } | |
| 761 | |
| 762 bool TransportSecurityState::CheckPublicKeyPinsImpl( | |
| 763 const std::string& host, | |
| 764 const HashValueVector& hashes, | |
| 765 std::string* failure_log) { | |
| 766 DomainState dynamic_state; | |
| 767 if (GetDynamicDomainState(host, &dynamic_state)) | |
| 768 return dynamic_state.CheckPublicKeyPins(hashes, failure_log); | |
| 769 | |
| 770 DomainState static_state; | |
| 771 if (GetStaticDomainState(host, &static_state)) | |
| 772 return static_state.CheckPublicKeyPins(hashes, failure_log); | |
| 773 | |
| 774 // HasPublicKeyPins should have returned true in order for this method | |
| 775 // to have been called, so if we fall through to here, it's an error. | |
| 776 return false; | |
| 777 } | |
| 778 | |
| 779 bool TransportSecurityState::GetStaticDomainState(const std::string& host, | |
| 780 DomainState* out) const { | |
| 781 DCHECK(CalledOnValidThread()); | |
| 782 | |
| 783 out->sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS; | |
| 784 out->sts.include_subdomains = false; | |
| 785 out->pkp.include_subdomains = false; | |
| 786 | |
| 787 if (!IsBuildTimely()) | |
| 788 return false; | |
| 789 | |
| 790 PreloadResult result; | |
| 791 if (!DecodeHSTSPreload(host, &result)) | |
| 792 return false; | |
| 793 | |
| 794 out->sts.domain = host.substr(result.hostname_offset); | |
| 795 out->pkp.domain = out->sts.domain; | |
| 796 out->sts.include_subdomains = result.sts_include_subdomains; | |
| 797 out->sts.last_observed = base::GetBuildTime(); | |
| 798 out->sts.upgrade_mode = | |
| 799 TransportSecurityState::DomainState::MODE_DEFAULT; | |
| 800 if (result.force_https) { | |
| 801 out->sts.upgrade_mode = | |
| 802 TransportSecurityState::DomainState::MODE_FORCE_HTTPS; | |
| 803 } | |
| 804 | |
| 805 if (enable_static_pins_ && result.has_pins) { | |
| 806 out->pkp.include_subdomains = result.pkp_include_subdomains; | |
| 807 out->pkp.last_observed = base::GetBuildTime(); | |
| 808 | |
| 809 if (result.pinset_id >= arraysize(kPinsets)) | |
| 810 return false; | |
| 811 const Pinset *pinset = &kPinsets[result.pinset_id]; | |
| 812 | |
| 813 if (pinset->accepted_pins) { | |
| 814 const char* const* sha1_hash = pinset->accepted_pins; | |
| 815 while (*sha1_hash) { | |
| 816 AddHash(*sha1_hash, &out->pkp.spki_hashes); | |
| 817 sha1_hash++; | |
| 818 } | |
| 819 } | |
| 820 if (pinset->rejected_pins) { | |
| 821 const char* const* sha1_hash = pinset->rejected_pins; | |
| 822 while (*sha1_hash) { | |
| 823 AddHash(*sha1_hash, &out->pkp.bad_spki_hashes); | |
| 824 sha1_hash++; | |
| 825 } | |
| 826 } | |
| 827 } | |
| 828 | |
| 829 return true; | |
| 830 } | |
| 831 | |
| 832 bool TransportSecurityState::GetDynamicDomainState(const std::string& host, | |
| 833 DomainState* result) { | |
| 834 DCHECK(CalledOnValidThread()); | |
| 835 | |
| 836 DomainState state; | |
| 837 const std::string canonicalized_host = CanonicalizeHost(host); | |
| 838 if (canonicalized_host.empty()) | |
| 839 return false; | |
| 840 | |
| 841 base::Time current_time(base::Time::Now()); | |
| 842 | |
| 843 bool found_sts = false; | |
| 844 bool found_pkp = false; | |
| 845 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | |
| 846 std::string host_sub_chunk(&canonicalized_host[i], | |
| 847 canonicalized_host.size() - i); | |
| 848 DomainStateMap::iterator j = | |
| 849 enabled_hosts_.find(HashHost(host_sub_chunk)); | |
| 850 if (j == enabled_hosts_.end()) | |
| 851 continue; | |
| 852 | |
| 853 // If both halves of the entry are invalid, drop it. | |
| 854 if (current_time > j->second.sts.expiry && | |
| 855 current_time > j->second.pkp.expiry) { | |
| 856 enabled_hosts_.erase(j); | |
| 857 DirtyNotify(); | |
| 858 continue; | |
| 859 } | |
| 860 | |
| 861 // If this is the most specific STS match, add it to the result. | |
| 862 if (!found_sts && (i == 0 || j->second.sts.include_subdomains) && | |
| 863 current_time <= j->second.sts.expiry && | |
| 864 j->second.ShouldUpgradeToSSL()) { | |
| 865 found_sts = true; | |
| 866 state.sts = j->second.sts; | |
| 867 state.sts.domain = DNSDomainToString(host_sub_chunk); | |
| 868 } | |
| 869 | |
| 870 // If this is the most specific PKP match, add it to the result. | |
| 871 if (!found_pkp && (i == 0 || j->second.pkp.include_subdomains) && | |
| 872 current_time <= j->second.pkp.expiry && j->second.HasPublicKeyPins()) { | |
| 873 found_pkp = true; | |
| 874 state.pkp = j->second.pkp; | |
| 875 state.pkp.domain = DNSDomainToString(host_sub_chunk); | |
| 876 } | |
| 877 | |
| 878 if (found_sts && found_pkp) | |
| 879 break; | |
| 880 } | |
| 881 | |
| 882 if (!found_sts && !found_pkp) | |
| 883 return false; | |
| 884 | |
| 885 *result = state; | |
| 886 return true; | |
| 887 } | |
| 888 | |
| 889 void TransportSecurityState::AddOrUpdateEnabledHosts( | |
| 890 const std::string& hashed_host, const DomainState& state) { | |
| 891 DCHECK(CalledOnValidThread()); | |
| 892 enabled_hosts_[hashed_host] = state; | |
| 893 } | |
| 894 | |
| 895 TransportSecurityState::DomainState::DomainState() { | |
| 896 sts.upgrade_mode = MODE_DEFAULT; | |
| 897 sts.include_subdomains = false; | |
| 898 pkp.include_subdomains = false; | |
| 899 } | |
| 900 | |
| 901 TransportSecurityState::DomainState::~DomainState() { | |
| 902 } | |
| 903 | |
| 904 bool TransportSecurityState::DomainState::CheckPublicKeyPins( | |
| 905 const HashValueVector& hashes, std::string* failure_log) const { | |
| 906 // Validate that hashes is not empty. By the time this code is called (in | |
| 907 // production), that should never happen, but it's good to be defensive. | |
| 908 // And, hashes *can* be empty in some test scenarios. | |
| 909 if (hashes.empty()) { | |
| 910 failure_log->append( | |
| 911 "Rejecting empty public key chain for public-key-pinned domains: " + | |
| 912 pkp.domain); | |
| 913 return false; | |
| 914 } | |
| 915 | |
| 916 if (HashesIntersect(pkp.bad_spki_hashes, hashes)) { | |
| 917 failure_log->append("Rejecting public key chain for domain " + pkp.domain + | |
| 918 ". Validated chain: " + HashesToBase64String(hashes) + | |
| 919 ", matches one or more bad hashes: " + | |
| 920 HashesToBase64String(pkp.bad_spki_hashes)); | |
| 921 return false; | |
| 922 } | |
| 923 | |
| 924 // If there are no pins, then any valid chain is acceptable. | |
| 925 if (pkp.spki_hashes.empty()) | |
| 926 return true; | |
| 927 | |
| 928 if (HashesIntersect(pkp.spki_hashes, hashes)) { | |
| 929 return true; | |
| 930 } | |
| 931 | |
| 932 failure_log->append("Rejecting public key chain for domain " + pkp.domain + | |
| 933 ". Validated chain: " + HashesToBase64String(hashes) + | |
| 934 ", expected: " + HashesToBase64String(pkp.spki_hashes)); | |
| 935 return false; | |
| 936 } | |
| 937 | |
| 938 bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const { | |
| 939 return sts.upgrade_mode == MODE_FORCE_HTTPS; | |
| 940 } | |
| 941 | |
| 942 bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const { | |
| 943 // Both HSTS and HPKP cause fatal SSL errors, so enable this on the presense | |
| 944 // of either. (If neither is active, no DomainState will be returned.) | |
| 945 return true; | |
| 946 } | |
| 947 | |
| 948 bool TransportSecurityState::DomainState::HasPublicKeyPins() const { | |
| 949 return pkp.spki_hashes.size() > 0 || pkp.bad_spki_hashes.size() > 0; | |
| 950 } | |
| 951 | |
| 952 TransportSecurityState::DomainState::STSState::STSState() { | |
| 953 } | |
| 954 | |
| 955 TransportSecurityState::DomainState::STSState::~STSState() { | |
| 956 } | |
| 957 | |
| 958 TransportSecurityState::DomainState::PKPState::PKPState() { | |
| 959 } | |
| 960 | |
| 961 TransportSecurityState::DomainState::PKPState::~PKPState() { | |
| 962 } | |
| 963 | |
| 964 } // namespace | |
| OLD | NEW |