Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/http/transport_security_state.h" | 5 #include "net/http/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) |
| (...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 263 break; | 263 break; |
| 264 | 264 |
| 265 for (size_t j = 0; j < label_length; ++j) { | 265 for (size_t j = 0; j < label_length; ++j) { |
| 266 new_host[i + 1 + j] = tolower(new_host[i + 1 + j]); | 266 new_host[i + 1 + j] = tolower(new_host[i + 1 + j]); |
| 267 } | 267 } |
| 268 } | 268 } |
| 269 | 269 |
| 270 return new_host; | 270 return new_host; |
| 271 } | 271 } |
| 272 | 272 |
| 273 // |ReportUMAOnPinFailure| uses these to report which domain was associated | 273 // BitReader is a class that allows a bytestring to be read bit-by-bit. |
| 274 // with the public key pinning failure. | 274 class BitReader { |
| 275 // | 275 public: |
| 276 // DO NOT CHANGE THE ORDERING OF THESE NAMES OR REMOVE ANY OF THEM. Add new | 276 BitReader(const uint8 *bytes, size_t num_bits) |
|
palmer
2014/10/02 20:26:36
Style nit: "const uint8* bytes" (throughout).
agl
2014/10/03 00:01:20
Done.
| |
| 277 // domains at the END of the listing (but before DOMAIN_NUM_EVENTS). | 277 : bytes_(bytes), |
| 278 enum SecondLevelDomainName { | 278 num_bits_(num_bits), |
| 279 DOMAIN_NOT_PINNED, | 279 num_bytes_((num_bits + 7) / 8), |
| 280 | 280 used_bytes_(0), |
| 281 DOMAIN_GOOGLE_COM, | 281 current_used_(8) {} |
| 282 DOMAIN_ANDROID_COM, | 282 |
| 283 DOMAIN_GOOGLE_ANALYTICS_COM, | 283 // Next sets |*out| to the next bit from the input. It returns false if no |
| 284 DOMAIN_GOOGLEPLEX_COM, | 284 // more bits are available or true otherwise. |
| 285 DOMAIN_YTIMG_COM, | 285 bool Next(bool *out) { |
| 286 DOMAIN_GOOGLEUSERCONTENT_COM, | 286 if (current_used_ == 8) { |
| 287 DOMAIN_YOUTUBE_COM, | 287 if (used_bytes_ >= num_bytes_) { |
| 288 DOMAIN_GOOGLEAPIS_COM, | 288 return false; |
| 289 DOMAIN_GOOGLEADSERVICES_COM, | 289 } |
| 290 DOMAIN_GOOGLECODE_COM, | 290 current_ = bytes_[used_bytes_++]; |
| 291 DOMAIN_APPSPOT_COM, | 291 current_used_ = 0; |
| 292 DOMAIN_GOOGLESYNDICATION_COM, | 292 } |
| 293 DOMAIN_DOUBLECLICK_NET, | 293 |
| 294 DOMAIN_GSTATIC_COM, | 294 *out = 1 & (current_ >> (7 - current_used_)); |
| 295 DOMAIN_GMAIL_COM, | 295 current_used_++; |
| 296 DOMAIN_GOOGLEMAIL_COM, | 296 return true; |
| 297 DOMAIN_GOOGLEGROUPS_COM, | 297 } |
| 298 | 298 |
| 299 DOMAIN_TORPROJECT_ORG, | 299 // Read sets the |num_bits| least-significant bits of |*out| to the value of |
| 300 | 300 // the next |num_bits| bits from the input. It returns false if there are |
| 301 DOMAIN_TWITTER_COM, | 301 // insufficient bits in the input or true otherwise. |
| 302 DOMAIN_TWIMG_COM, | 302 bool Read(unsigned num_bits, uint32 *out) { |
| 303 | 303 DCHECK_LE(num_bits, 32u); |
| 304 DOMAIN_AKAMAIHD_NET, | 304 |
| 305 | 305 uint32 ret = 0; |
| 306 DOMAIN_TOR2WEB_ORG, | 306 for (unsigned i = 0; i < num_bits; i++) { |
|
palmer
2014/10/02 20:26:36
Style nit: Chromium style is pre-increment (here a
agl
2014/10/03 00:01:20
Done.
| |
| 307 | 307 bool bit; |
| 308 DOMAIN_YOUTU_BE, | 308 if (!Next(&bit)) { |
| 309 DOMAIN_GOOGLECOMMERCE_COM, | 309 return false; |
| 310 DOMAIN_URCHIN_COM, | 310 } |
| 311 DOMAIN_GOO_GL, | 311 ret |= static_cast<uint32>(bit) << (num_bits - 1 - i); |
| 312 DOMAIN_G_CO, | 312 } |
| 313 DOMAIN_GOOGLE_AC, | 313 |
| 314 DOMAIN_GOOGLE_AD, | 314 *out = ret; |
| 315 DOMAIN_GOOGLE_AE, | 315 return true; |
| 316 DOMAIN_GOOGLE_AF, | 316 } |
| 317 DOMAIN_GOOGLE_AG, | 317 |
| 318 DOMAIN_GOOGLE_AM, | 318 // Unary sets |*out| to the result of decoding a unary value from the input. |
| 319 DOMAIN_GOOGLE_AS, | 319 // It returns false if there were insufficient bits in the input and true |
| 320 DOMAIN_GOOGLE_AT, | 320 // otherwise. |
| 321 DOMAIN_GOOGLE_AZ, | 321 bool Unary(size_t *out) { |
| 322 DOMAIN_GOOGLE_BA, | 322 size_t ret = 0; |
| 323 DOMAIN_GOOGLE_BE, | 323 |
| 324 DOMAIN_GOOGLE_BF, | 324 for (;;) { |
| 325 DOMAIN_GOOGLE_BG, | 325 bool bit; |
| 326 DOMAIN_GOOGLE_BI, | 326 if (!Next(&bit)) { |
| 327 DOMAIN_GOOGLE_BJ, | 327 return false; |
| 328 DOMAIN_GOOGLE_BS, | 328 } |
| 329 DOMAIN_GOOGLE_BY, | 329 if (!bit) { |
| 330 DOMAIN_GOOGLE_CA, | 330 break; |
| 331 DOMAIN_GOOGLE_CAT, | 331 } |
| 332 DOMAIN_GOOGLE_CC, | 332 ret++; |
| 333 DOMAIN_GOOGLE_CD, | 333 } |
| 334 DOMAIN_GOOGLE_CF, | 334 |
| 335 DOMAIN_GOOGLE_CG, | 335 *out = ret; |
| 336 DOMAIN_GOOGLE_CH, | 336 return true; |
| 337 DOMAIN_GOOGLE_CI, | 337 } |
| 338 DOMAIN_GOOGLE_CL, | 338 |
| 339 DOMAIN_GOOGLE_CM, | 339 // Seek sets the current offest in the input to bit number |offset|. It |
| 340 DOMAIN_GOOGLE_CN, | 340 // returns true if |offset| is within the range of the input and false |
| 341 DOMAIN_CO_AO, | 341 // otherwise. |
| 342 DOMAIN_CO_BW, | 342 bool Seek(size_t offset) { |
| 343 DOMAIN_CO_CK, | 343 if (offset >= num_bits_) { |
| 344 DOMAIN_CO_CR, | 344 return false; |
| 345 DOMAIN_CO_HU, | 345 } |
| 346 DOMAIN_CO_ID, | 346 used_bytes_ = offset / 8; |
| 347 DOMAIN_CO_IL, | 347 current_ = bytes_[used_bytes_++]; |
| 348 DOMAIN_CO_IM, | 348 current_used_ = offset % 8; |
| 349 DOMAIN_CO_IN, | 349 return true; |
| 350 DOMAIN_CO_JE, | 350 } |
| 351 DOMAIN_CO_JP, | 351 |
| 352 DOMAIN_CO_KE, | 352 private: |
| 353 DOMAIN_CO_KR, | 353 const uint8 *const bytes_; |
| 354 DOMAIN_CO_LS, | 354 const size_t num_bits_; |
| 355 DOMAIN_CO_MA, | 355 const size_t num_bytes_; |
| 356 DOMAIN_CO_MZ, | 356 // used_bytes_ contains the current byte offset in |bytes_|. |
| 357 DOMAIN_CO_NZ, | 357 size_t used_bytes_; |
| 358 DOMAIN_CO_TH, | 358 // current_ contains the current byte of the input. |
| 359 DOMAIN_CO_TZ, | 359 uint8 current_; |
|
palmer
2014/10/02 20:26:36
Nit: I might name this |current_byte_|, and name |
agl
2014/10/03 00:01:20
Done.
| |
| 360 DOMAIN_CO_UG, | 360 // current_used_ contains the number of bits of |current_| that have been |
| 361 DOMAIN_CO_UK, | 361 // read. |
| 362 DOMAIN_CO_UZ, | 362 unsigned current_used_; |
| 363 DOMAIN_CO_VE, | |
| 364 DOMAIN_CO_VI, | |
| 365 DOMAIN_CO_ZA, | |
| 366 DOMAIN_CO_ZM, | |
| 367 DOMAIN_CO_ZW, | |
| 368 DOMAIN_COM_AF, | |
| 369 DOMAIN_COM_AG, | |
| 370 DOMAIN_COM_AI, | |
| 371 DOMAIN_COM_AR, | |
| 372 DOMAIN_COM_AU, | |
| 373 DOMAIN_COM_BD, | |
| 374 DOMAIN_COM_BH, | |
| 375 DOMAIN_COM_BN, | |
| 376 DOMAIN_COM_BO, | |
| 377 DOMAIN_COM_BR, | |
| 378 DOMAIN_COM_BY, | |
| 379 DOMAIN_COM_BZ, | |
| 380 DOMAIN_COM_CN, | |
| 381 DOMAIN_COM_CO, | |
| 382 DOMAIN_COM_CU, | |
| 383 DOMAIN_COM_CY, | |
| 384 DOMAIN_COM_DO, | |
| 385 DOMAIN_COM_EC, | |
| 386 DOMAIN_COM_EG, | |
| 387 DOMAIN_COM_ET, | |
| 388 DOMAIN_COM_FJ, | |
| 389 DOMAIN_COM_GE, | |
| 390 DOMAIN_COM_GH, | |
| 391 DOMAIN_COM_GI, | |
| 392 DOMAIN_COM_GR, | |
| 393 DOMAIN_COM_GT, | |
| 394 DOMAIN_COM_HK, | |
| 395 DOMAIN_COM_IQ, | |
| 396 DOMAIN_COM_JM, | |
| 397 DOMAIN_COM_JO, | |
| 398 DOMAIN_COM_KH, | |
| 399 DOMAIN_COM_KW, | |
| 400 DOMAIN_COM_LB, | |
| 401 DOMAIN_COM_LY, | |
| 402 DOMAIN_COM_MT, | |
| 403 DOMAIN_COM_MX, | |
| 404 DOMAIN_COM_MY, | |
| 405 DOMAIN_COM_NA, | |
| 406 DOMAIN_COM_NF, | |
| 407 DOMAIN_COM_NG, | |
| 408 DOMAIN_COM_NI, | |
| 409 DOMAIN_COM_NP, | |
| 410 DOMAIN_COM_NR, | |
| 411 DOMAIN_COM_OM, | |
| 412 DOMAIN_COM_PA, | |
| 413 DOMAIN_COM_PE, | |
| 414 DOMAIN_COM_PH, | |
| 415 DOMAIN_COM_PK, | |
| 416 DOMAIN_COM_PL, | |
| 417 DOMAIN_COM_PR, | |
| 418 DOMAIN_COM_PY, | |
| 419 DOMAIN_COM_QA, | |
| 420 DOMAIN_COM_RU, | |
| 421 DOMAIN_COM_SA, | |
| 422 DOMAIN_COM_SB, | |
| 423 DOMAIN_COM_SG, | |
| 424 DOMAIN_COM_SL, | |
| 425 DOMAIN_COM_SV, | |
| 426 DOMAIN_COM_TJ, | |
| 427 DOMAIN_COM_TN, | |
| 428 DOMAIN_COM_TR, | |
| 429 DOMAIN_COM_TW, | |
| 430 DOMAIN_COM_UA, | |
| 431 DOMAIN_COM_UY, | |
| 432 DOMAIN_COM_VC, | |
| 433 DOMAIN_COM_VE, | |
| 434 DOMAIN_COM_VN, | |
| 435 DOMAIN_GOOGLE_CV, | |
| 436 DOMAIN_GOOGLE_CZ, | |
| 437 DOMAIN_GOOGLE_DE, | |
| 438 DOMAIN_GOOGLE_DJ, | |
| 439 DOMAIN_GOOGLE_DK, | |
| 440 DOMAIN_GOOGLE_DM, | |
| 441 DOMAIN_GOOGLE_DZ, | |
| 442 DOMAIN_GOOGLE_EE, | |
| 443 DOMAIN_GOOGLE_ES, | |
| 444 DOMAIN_GOOGLE_FI, | |
| 445 DOMAIN_GOOGLE_FM, | |
| 446 DOMAIN_GOOGLE_FR, | |
| 447 DOMAIN_GOOGLE_GA, | |
| 448 DOMAIN_GOOGLE_GE, | |
| 449 DOMAIN_GOOGLE_GG, | |
| 450 DOMAIN_GOOGLE_GL, | |
| 451 DOMAIN_GOOGLE_GM, | |
| 452 DOMAIN_GOOGLE_GP, | |
| 453 DOMAIN_GOOGLE_GR, | |
| 454 DOMAIN_GOOGLE_GY, | |
| 455 DOMAIN_GOOGLE_HK, | |
| 456 DOMAIN_GOOGLE_HN, | |
| 457 DOMAIN_GOOGLE_HR, | |
| 458 DOMAIN_GOOGLE_HT, | |
| 459 DOMAIN_GOOGLE_HU, | |
| 460 DOMAIN_GOOGLE_IE, | |
| 461 DOMAIN_GOOGLE_IM, | |
| 462 DOMAIN_GOOGLE_INFO, | |
| 463 DOMAIN_GOOGLE_IQ, | |
| 464 DOMAIN_GOOGLE_IS, | |
| 465 DOMAIN_GOOGLE_IT, | |
| 466 DOMAIN_IT_AO, | |
| 467 DOMAIN_GOOGLE_JE, | |
| 468 DOMAIN_GOOGLE_JO, | |
| 469 DOMAIN_GOOGLE_JOBS, | |
| 470 DOMAIN_GOOGLE_JP, | |
| 471 DOMAIN_GOOGLE_KG, | |
| 472 DOMAIN_GOOGLE_KI, | |
| 473 DOMAIN_GOOGLE_KZ, | |
| 474 DOMAIN_GOOGLE_LA, | |
| 475 DOMAIN_GOOGLE_LI, | |
| 476 DOMAIN_GOOGLE_LK, | |
| 477 DOMAIN_GOOGLE_LT, | |
| 478 DOMAIN_GOOGLE_LU, | |
| 479 DOMAIN_GOOGLE_LV, | |
| 480 DOMAIN_GOOGLE_MD, | |
| 481 DOMAIN_GOOGLE_ME, | |
| 482 DOMAIN_GOOGLE_MG, | |
| 483 DOMAIN_GOOGLE_MK, | |
| 484 DOMAIN_GOOGLE_ML, | |
| 485 DOMAIN_GOOGLE_MN, | |
| 486 DOMAIN_GOOGLE_MS, | |
| 487 DOMAIN_GOOGLE_MU, | |
| 488 DOMAIN_GOOGLE_MV, | |
| 489 DOMAIN_GOOGLE_MW, | |
| 490 DOMAIN_GOOGLE_NE, | |
| 491 DOMAIN_NE_JP, | |
| 492 DOMAIN_GOOGLE_NET, | |
| 493 DOMAIN_GOOGLE_NL, | |
| 494 DOMAIN_GOOGLE_NO, | |
| 495 DOMAIN_GOOGLE_NR, | |
| 496 DOMAIN_GOOGLE_NU, | |
| 497 DOMAIN_OFF_AI, | |
| 498 DOMAIN_GOOGLE_PK, | |
| 499 DOMAIN_GOOGLE_PL, | |
| 500 DOMAIN_GOOGLE_PN, | |
| 501 DOMAIN_GOOGLE_PS, | |
| 502 DOMAIN_GOOGLE_PT, | |
| 503 DOMAIN_GOOGLE_RO, | |
| 504 DOMAIN_GOOGLE_RS, | |
| 505 DOMAIN_GOOGLE_RU, | |
| 506 DOMAIN_GOOGLE_RW, | |
| 507 DOMAIN_GOOGLE_SC, | |
| 508 DOMAIN_GOOGLE_SE, | |
| 509 DOMAIN_GOOGLE_SH, | |
| 510 DOMAIN_GOOGLE_SI, | |
| 511 DOMAIN_GOOGLE_SK, | |
| 512 DOMAIN_GOOGLE_SM, | |
| 513 DOMAIN_GOOGLE_SN, | |
| 514 DOMAIN_GOOGLE_SO, | |
| 515 DOMAIN_GOOGLE_ST, | |
| 516 DOMAIN_GOOGLE_TD, | |
| 517 DOMAIN_GOOGLE_TG, | |
| 518 DOMAIN_GOOGLE_TK, | |
| 519 DOMAIN_GOOGLE_TL, | |
| 520 DOMAIN_GOOGLE_TM, | |
| 521 DOMAIN_GOOGLE_TN, | |
| 522 DOMAIN_GOOGLE_TO, | |
| 523 DOMAIN_GOOGLE_TP, | |
| 524 DOMAIN_GOOGLE_TT, | |
| 525 DOMAIN_GOOGLE_US, | |
| 526 DOMAIN_GOOGLE_UZ, | |
| 527 DOMAIN_GOOGLE_VG, | |
| 528 DOMAIN_GOOGLE_VU, | |
| 529 DOMAIN_GOOGLE_WS, | |
| 530 | |
| 531 DOMAIN_CHROMIUM_ORG, | |
| 532 | |
| 533 DOMAIN_CRYPTO_CAT, | |
| 534 DOMAIN_LAVABIT_COM, | |
| 535 | |
| 536 DOMAIN_GOOGLETAGMANAGER_COM, | |
| 537 DOMAIN_GOOGLETAGSERVICES_COM, | |
| 538 | |
| 539 DOMAIN_DROPBOX_COM, | |
| 540 DOMAIN_YOUTUBE_NOCOOKIE_COM, | |
| 541 DOMAIN_2MDN_NET, | |
| 542 | |
| 543 // Boundary value for UMA_HISTOGRAM_ENUMERATION: | |
| 544 DOMAIN_NUM_EVENTS | |
| 545 }; | 363 }; |
| 546 | 364 |
| 547 // PublicKeyPins contains a number of SubjectPublicKeyInfo hashes for a site. | 365 // HuffmanDecoder is a very simple Huffman reader. The input Huffman tree is |
| 548 // The validated certificate chain for the site must not include any of | 366 // simply encoded as a series of two-byte structures. The first byte determines |
| 549 // |excluded_hashes| and must include one or more of |required_hashes|. | 367 // the "0" pointer for that node and the second the "1" pointer. Each byte |
| 550 struct PublicKeyPins { | 368 // either has the MSB set, in which case the bottom 7 bits are the value for |
| 551 const char* const* required_hashes; | 369 // that position, or else the bottom seven bits contain the index of a node. |
| 552 const char* const* excluded_hashes; | 370 // |
| 371 // The tree is decoded by walking rather than a table-driven approach. | |
| 372 class HuffmanDecoder { | |
| 373 public: | |
| 374 HuffmanDecoder(const uint8 *tree, size_t tree_bytes) | |
| 375 : tree_(tree), | |
| 376 tree_bytes_(tree_bytes) {} | |
| 377 | |
| 378 bool Decode(BitReader *reader, char *out) { | |
| 379 const uint8 *current = &tree_[tree_bytes_-2]; | |
| 380 | |
| 381 for (;;) { | |
| 382 bool bit; | |
| 383 if (!reader->Next(&bit)) { | |
| 384 return false; | |
| 385 } | |
| 386 | |
| 387 uint8 b = current[bit]; | |
| 388 if (b & 0x80) { | |
| 389 *out = static_cast<char>(b & 0x7f); | |
| 390 return true; | |
| 391 } | |
| 392 | |
| 393 unsigned offset = static_cast<unsigned>(b) * 2; | |
| 394 DCHECK_LT(offset, tree_bytes_); | |
| 395 if (offset >= tree_bytes_) { | |
| 396 return false; | |
| 397 } | |
| 398 | |
| 399 current = &tree_[offset]; | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 private: | |
| 404 const uint8 *const tree_; | |
| 405 const size_t tree_bytes_; | |
| 553 }; | 406 }; |
| 554 | 407 |
| 555 struct HSTSPreload { | 408 #include "net/http/transport_security_state_static.h" |
| 556 uint8 length; | 409 |
| 410 // PreloadResult is the result of resolving a specific name in the preloaded | |
| 411 // data. | |
| 412 struct PreloadResult { | |
| 557 bool include_subdomains; | 413 bool include_subdomains; |
| 558 char dns_name[38]; | 414 bool force_https; |
| 559 bool https_required; | 415 bool has_pins; |
| 560 PublicKeyPins pins; | 416 uint32 pinset_id; |
|
palmer
2014/10/02 20:26:35
Nit: Maybe we could pack this struct every-so-slig
agl
2014/10/03 00:01:20
I've moved the bools to the end but I don't think
| |
| 561 SecondLevelDomainName second_level_domain_name; | 417 uint32 domain_id; |
| 418 // hostname_offset contains the number of bytes from the start of the given | |
| 419 // hostname where the name of the matching entry starts. | |
| 420 size_t hostname_offset; | |
| 562 }; | 421 }; |
| 563 | 422 |
| 564 static bool HasPreload(const struct HSTSPreload* entries, | 423 // DecodeHSTSPreload resolves |hostname| in the preloaded data. It returns |
| 565 size_t num_entries, | 424 // false on internal error and true otherwise. After a successful return, |
|
palmer
2014/10/02 20:26:36
Would any internal errors be due to bugs? What are
agl
2014/10/03 00:01:20
Log the fact, essentially. However, you're correct
| |
| 566 const std::string& canonicalized_host, | 425 // |*out_found| is true iff a relevant entry has been found. If so, |*out| |
| 567 size_t i, | 426 // contains the details. |
| 568 bool enable_static_pins, | 427 // |
| 569 TransportSecurityState::DomainState* out, | 428 // Although this code should be robust, it never processes attacker-controlled |
| 570 bool* ret) { | 429 // data -- it only operates on the preloaded data built into the binary. |
| 571 for (size_t j = 0; j < num_entries; j++) { | 430 // |
| 572 if (entries[j].length == canonicalized_host.size() - i && | 431 // The preloaded data is represented as a trie and matches the hostname |
| 573 memcmp(entries[j].dns_name, &canonicalized_host[i], | 432 // backwards. Each node in the trie starts with a number of characters, which |
| 574 entries[j].length) == 0) { | 433 // must match exactly. After that is a dispatch table which maps the next |
| 575 if (!entries[j].include_subdomains && i != 0) { | 434 // character in the hostname to another node in the trie. |
| 576 *ret = false; | 435 // |
| 436 // In the disable table, the zero character represents the "end of string" | |
|
palmer
2014/10/02 20:26:35
Typo: "dispatch table"?
agl
2014/10/03 00:01:20
Done.
| |
| 437 // (which is the *beginning* of a hostname since we process it backwards). The | |
| 438 // value in that case is special -- rather than an offset to another trie node, | |
| 439 // it contains the HSTS information: whether subdomains are included, pinsets | |
| 440 // etc. If an "end of string" matches a period in the hostname then the | |
| 441 // information is remembered because, if no more specific node is found, then | |
| 442 // that information applies to the hostname. | |
| 443 // | |
| 444 // Dispatch tables are always given in order, but the "end of string" (zero) | |
| 445 // value always comes before an entry for '.'. | |
| 446 bool DecodeHSTSPreload(const std::string& hostname, | |
| 447 bool* out_found, | |
| 448 PreloadResult* out) { | |
| 449 HuffmanDecoder huffman(kHSTSHuffmanTree, sizeof(kHSTSHuffmanTree)); | |
| 450 BitReader reader(kPreloadedHSTSData, kPreloadedHSTSBits); | |
| 451 size_t bit_offset = kHSTSRootPosition; | |
| 452 static const char kEndOfString = 0; | |
| 453 static const char kEndOfTable = 127; | |
| 454 | |
| 455 *out_found = false; | |
| 456 | |
| 457 if (hostname.empty()) { | |
| 458 return true; | |
| 459 } | |
| 460 // hostname_offset contains one more than the index of the current character | |
| 461 // in the hostname that is being considered. It's one greater so that we can | |
| 462 // represent the position just before the beginning (with zero). | |
| 463 size_t hostname_offset = hostname.size(); | |
| 464 | |
| 465 for (;;) { | |
| 466 // Seek to the desired location. | |
| 467 if (!reader.Seek(bit_offset)) { | |
| 468 return false; | |
| 469 } | |
| 470 | |
| 471 // Decode the unary length of the common prefix. | |
| 472 size_t prefix_length; | |
| 473 if (!reader.Unary(&prefix_length)) { | |
| 474 return false; | |
| 475 } | |
| 476 | |
| 477 // Match each character in the prefix. | |
| 478 for (size_t i = 0; i < prefix_length; i++) { | |
| 479 if (hostname_offset == 0) { | |
| 480 // We can't match the terminator with a prefix string. | |
| 481 return true; | |
| 482 } | |
| 483 | |
| 484 char c; | |
| 485 if (!huffman.Decode(&reader, &c)) { | |
| 486 return false; | |
| 487 } | |
| 488 if (hostname[hostname_offset - 1] != c) { | |
| 489 return true; | |
| 490 } | |
| 491 hostname_offset--; | |
| 492 } | |
| 493 | |
| 494 bool is_first_offset = true; | |
| 495 size_t current_offset = 0; | |
| 496 | |
| 497 // Next is the dispatch table. | |
| 498 for (;;) { | |
| 499 char c; | |
| 500 if (!huffman.Decode(&reader, &c)) { | |
| 501 return false; | |
| 502 } | |
| 503 if (c == kEndOfTable) { | |
| 504 // No exact match. | |
| 505 return true; | |
| 506 } | |
| 507 | |
| 508 if (c == kEndOfString) { | |
| 509 PreloadResult tmp; | |
| 510 if (!reader.Next(&tmp.include_subdomains) || | |
| 511 !reader.Next(&tmp.force_https) || | |
| 512 !reader.Next(&tmp.has_pins)) { | |
| 513 return false; | |
| 514 } | |
| 515 | |
| 516 if (tmp.has_pins) { | |
| 517 if (!reader.Read(4, &tmp.pinset_id) || | |
| 518 !reader.Read(9, &tmp.domain_id)) { | |
| 519 return false; | |
| 520 } | |
| 521 } | |
| 522 | |
| 523 tmp.hostname_offset = hostname_offset; | |
| 524 | |
| 525 if (hostname_offset == 0 || hostname[hostname_offset - 1] == '.') { | |
| 526 *out_found = tmp.include_subdomains; | |
| 527 *out = tmp; | |
| 528 } | |
| 529 | |
| 530 if (hostname_offset == 0) { | |
| 531 *out_found = true; | |
| 532 return true; | |
| 533 } | |
| 534 | |
| 535 continue; | |
| 536 } | |
| 537 | |
| 538 // The entries in a dispatch table are in order thus we can tell if there | |
| 539 // will be no match if the current character past the one that we want. | |
| 540 if (hostname_offset == 0 || hostname[hostname_offset-1] < c) { | |
| 541 return true; | |
| 542 } | |
| 543 | |
| 544 if (is_first_offset) { | |
| 545 // The first offset is backwards from the current position. | |
| 546 uint32 jump_delta_bits; | |
| 547 uint32 jump_delta; | |
| 548 if (!reader.Read(5, &jump_delta_bits) || | |
| 549 !reader.Read(jump_delta_bits, &jump_delta)) { | |
| 550 return false; | |
| 551 } | |
| 552 | |
| 553 if (bit_offset <= jump_delta) { | |
| 554 return false; | |
| 555 } | |
| 556 | |
| 557 current_offset = bit_offset - jump_delta; | |
| 558 is_first_offset = false; | |
| 577 } else { | 559 } else { |
| 578 out->sts.include_subdomains = entries[j].include_subdomains; | 560 // Subsequent offsets are forward from the target of the first offset. |
| 579 out->sts.last_observed = base::GetBuildTime(); | 561 uint32 is_long_jump; |
| 580 *ret = true; | 562 if (!reader.Read(1, &is_long_jump)) { |
| 581 out->sts.upgrade_mode = | 563 return false; |
| 582 TransportSecurityState::DomainState::MODE_FORCE_HTTPS; | 564 } |
| 583 if (!entries[j].https_required) | 565 |
| 584 out->sts.upgrade_mode = | 566 uint32 jump_delta; |
| 585 TransportSecurityState::DomainState::MODE_DEFAULT; | 567 if (!is_long_jump) { |
| 586 | 568 if (!reader.Read(7, &jump_delta)) { |
| 587 if (enable_static_pins) { | 569 return false; |
| 588 out->pkp.include_subdomains = entries[j].include_subdomains; | |
| 589 out->pkp.last_observed = base::GetBuildTime(); | |
| 590 if (entries[j].pins.required_hashes) { | |
| 591 const char* const* sha1_hash = entries[j].pins.required_hashes; | |
| 592 while (*sha1_hash) { | |
| 593 AddHash(*sha1_hash, &out->pkp.spki_hashes); | |
| 594 sha1_hash++; | |
| 595 } | |
| 596 } | 570 } |
| 597 if (entries[j].pins.excluded_hashes) { | 571 } else { |
| 598 const char* const* sha1_hash = entries[j].pins.excluded_hashes; | 572 uint32 jump_delta_bits; |
| 599 while (*sha1_hash) { | 573 if (!reader.Read(4, &jump_delta_bits) || |
| 600 AddHash(*sha1_hash, &out->pkp.bad_spki_hashes); | 574 !reader.Read(jump_delta_bits + 8, &jump_delta)) { |
| 601 sha1_hash++; | 575 return false; |
| 602 } | |
| 603 } | 576 } |
| 604 } | 577 } |
| 605 } | 578 |
| 606 return true; | 579 current_offset += jump_delta; |
| 607 } | 580 if (current_offset >= bit_offset) { |
| 608 } | 581 return false; |
| 609 return false; | 582 } |
| 583 } | |
| 584 | |
| 585 DCHECK_LT(0u, hostname_offset); | |
| 586 if (hostname[hostname_offset - 1] == c) { | |
| 587 bit_offset = current_offset; | |
| 588 hostname_offset--; | |
| 589 break; | |
| 590 } | |
| 591 } | |
| 592 } | |
| 610 } | 593 } |
| 611 | 594 |
| 612 #include "net/http/transport_security_state_static.h" | |
| 613 | |
| 614 // Returns the HSTSPreload entry for the |canonicalized_host| in |entries|, | |
| 615 // or NULL if there is none. Prefers exact hostname matches to those that | |
| 616 // match only because HSTSPreload.include_subdomains is true. | |
| 617 // | |
| 618 // |canonicalized_host| should be the hostname as canonicalized by | |
| 619 // CanonicalizeHost. | |
| 620 static const struct HSTSPreload* GetHSTSPreload( | |
| 621 const std::string& canonicalized_host, | |
| 622 const struct HSTSPreload* entries, | |
| 623 size_t num_entries) { | |
| 624 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | |
| 625 for (size_t j = 0; j < num_entries; j++) { | |
| 626 const struct HSTSPreload* entry = entries + j; | |
| 627 | |
| 628 if (i != 0 && !entry->include_subdomains) | |
| 629 continue; | |
| 630 | |
| 631 if (entry->length == canonicalized_host.size() - i && | |
| 632 memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) { | |
| 633 return entry; | |
| 634 } | |
| 635 } | |
| 636 } | |
| 637 | |
| 638 return NULL; | |
| 639 } | |
| 640 | |
| 641 bool TransportSecurityState::AddHSTSHeader(const std::string& host, | 595 bool TransportSecurityState::AddHSTSHeader(const std::string& host, |
| 642 const std::string& value) { | 596 const std::string& value) { |
| 643 DCHECK(CalledOnValidThread()); | 597 DCHECK(CalledOnValidThread()); |
| 644 | 598 |
| 645 base::Time now = base::Time::Now(); | 599 base::Time now = base::Time::Now(); |
| 646 base::TimeDelta max_age; | 600 base::TimeDelta max_age; |
| 647 TransportSecurityState::DomainState domain_state; | 601 TransportSecurityState::DomainState domain_state; |
| 648 GetDynamicDomainState(host, &domain_state); | 602 GetDynamicDomainState(host, &domain_state); |
| 649 if (ParseHSTSHeader(value, &max_age, &domain_state.sts.include_subdomains)) { | 603 if (ParseHSTSHeader(value, &max_age, &domain_state.sts.include_subdomains)) { |
| 650 // Handle max-age == 0. | 604 // Handle max-age == 0. |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 725 domain_state.pkp.last_observed = base::Time::Now(); | 679 domain_state.pkp.last_observed = base::Time::Now(); |
| 726 domain_state.pkp.include_subdomains = include_subdomains; | 680 domain_state.pkp.include_subdomains = include_subdomains; |
| 727 domain_state.pkp.expiry = expiry; | 681 domain_state.pkp.expiry = expiry; |
| 728 domain_state.pkp.spki_hashes = hashes; | 682 domain_state.pkp.spki_hashes = hashes; |
| 729 EnableHost(host, domain_state); | 683 EnableHost(host, domain_state); |
| 730 return true; | 684 return true; |
| 731 } | 685 } |
| 732 | 686 |
| 733 // static | 687 // static |
| 734 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host) { | 688 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host) { |
| 735 std::string canonicalized_host = CanonicalizeHost(host); | 689 bool found; |
| 736 const struct HSTSPreload* entry = | 690 PreloadResult result; |
| 737 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 691 return DecodeHSTSPreload(host, &found, &result) && found && result.has_pins && |
| 738 | 692 kPinsets[result.pinset_id].accepted_pins == kGoogleAcceptableCerts; |
| 739 return entry && entry->pins.required_hashes == kGoogleAcceptableCerts; | |
| 740 } | 693 } |
| 741 | 694 |
| 742 // static | 695 // static |
| 743 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { | 696 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { |
| 744 std::string canonicalized_host = CanonicalizeHost(host); | 697 bool found; |
| 745 | 698 PreloadResult result; |
| 746 const struct HSTSPreload* entry = | 699 if (!DecodeHSTSPreload(host, &found, &result) || |
| 747 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 700 !found || |
| 748 | 701 !result.has_pins) { |
| 749 if (!entry) { | |
| 750 // We don't care to report pin failures for dynamic pins. | |
| 751 return; | 702 return; |
| 752 } | 703 } |
| 753 | 704 |
| 754 DCHECK(entry); | 705 DCHECK(result.domain_id != DOMAIN_NOT_PINNED); |
| 755 DCHECK(entry->pins.required_hashes); | |
| 756 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | |
| 757 | 706 |
| 758 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | 707 UMA_HISTOGRAM_ENUMERATION( |
| 759 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | 708 "Net.PublicKeyPinFailureDomain", result.domain_id, DOMAIN_NUM_EVENTS); |
| 760 } | 709 } |
| 761 | 710 |
| 762 // static | 711 // static |
| 763 bool TransportSecurityState::IsBuildTimely() { | 712 bool TransportSecurityState::IsBuildTimely() { |
| 764 const base::Time build_time = base::GetBuildTime(); | 713 const base::Time build_time = base::GetBuildTime(); |
| 765 // We consider built-in information to be timely for 10 weeks. | 714 // We consider built-in information to be timely for 10 weeks. |
| 766 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; | 715 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; |
| 767 } | 716 } |
| 768 | 717 |
| 769 bool TransportSecurityState::CheckPublicKeyPinsImpl( | 718 bool TransportSecurityState::CheckPublicKeyPinsImpl( |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 780 | 729 |
| 781 // HasPublicKeyPins should have returned true in order for this method | 730 // HasPublicKeyPins should have returned true in order for this method |
| 782 // to have been called, so if we fall through to here, it's an error. | 731 // to have been called, so if we fall through to here, it's an error. |
| 783 return false; | 732 return false; |
| 784 } | 733 } |
| 785 | 734 |
| 786 bool TransportSecurityState::GetStaticDomainState(const std::string& host, | 735 bool TransportSecurityState::GetStaticDomainState(const std::string& host, |
| 787 DomainState* out) const { | 736 DomainState* out) const { |
| 788 DCHECK(CalledOnValidThread()); | 737 DCHECK(CalledOnValidThread()); |
| 789 | 738 |
| 790 const std::string canonicalized_host = CanonicalizeHost(host); | |
| 791 | |
| 792 out->sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS; | 739 out->sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
| 793 out->sts.include_subdomains = false; | 740 out->sts.include_subdomains = false; |
| 794 out->pkp.include_subdomains = false; | 741 out->pkp.include_subdomains = false; |
| 795 | 742 |
| 796 const bool is_build_timely = IsBuildTimely(); | 743 if (!IsBuildTimely()) |
| 744 return false; | |
| 797 | 745 |
| 798 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 746 bool found; |
| 799 std::string host_sub_chunk(&canonicalized_host[i], | 747 PreloadResult result; |
| 800 canonicalized_host.size() - i); | 748 if (!DecodeHSTSPreload(host, &found, &result)) { |
| 801 out->domain = DNSDomainToString(host_sub_chunk); | 749 LOG(ERROR) << "Internal error decoding HSTS data for " << host; |
| 802 bool ret; | 750 return false; |
| 803 if (is_build_timely && HasPreload(kPreloadedSTS, | 751 } |
| 804 kNumPreloadedSTS, | 752 |
| 805 canonicalized_host, | 753 if (!found) |
| 806 i, | 754 return false; |
| 807 enable_static_pins_, | 755 |
| 808 out, | 756 out->domain = host.substr(result.hostname_offset); |
| 809 &ret)) { | 757 out->sts.include_subdomains = result.include_subdomains; |
| 810 return ret; | 758 out->sts.last_observed = base::GetBuildTime(); |
| 759 out->sts.upgrade_mode = | |
| 760 TransportSecurityState::DomainState::MODE_DEFAULT; | |
| 761 if (result.force_https) { | |
| 762 out->sts.upgrade_mode = | |
| 763 TransportSecurityState::DomainState::MODE_FORCE_HTTPS; | |
| 764 } | |
| 765 | |
| 766 if (enable_static_pins_ && result.has_pins) { | |
| 767 out->pkp.include_subdomains = result.include_subdomains; | |
| 768 out->pkp.last_observed = base::GetBuildTime(); | |
| 769 | |
| 770 if (result.pinset_id >= arraysize(kPinsets)) { | |
| 771 return false; | |
| 772 } | |
| 773 const Pinset *pinset = &kPinsets[result.pinset_id]; | |
| 774 | |
| 775 if (pinset->accepted_pins) { | |
| 776 const char* const* sha1_hash = pinset->accepted_pins; | |
| 777 while (*sha1_hash) { | |
| 778 AddHash(*sha1_hash, &out->pkp.spki_hashes); | |
| 779 sha1_hash++; | |
| 780 } | |
| 781 } | |
| 782 if (pinset->rejected_pins) { | |
| 783 const char* const* sha1_hash = pinset->rejected_pins; | |
| 784 while (*sha1_hash) { | |
| 785 AddHash(*sha1_hash, &out->pkp.bad_spki_hashes); | |
| 786 sha1_hash++; | |
| 787 } | |
| 811 } | 788 } |
| 812 } | 789 } |
| 813 | 790 |
| 814 return false; | 791 return true; |
| 815 } | 792 } |
| 816 | 793 |
| 817 bool TransportSecurityState::GetDynamicDomainState(const std::string& host, | 794 bool TransportSecurityState::GetDynamicDomainState(const std::string& host, |
| 818 DomainState* result) { | 795 DomainState* result) { |
| 819 DCHECK(CalledOnValidThread()); | 796 DCHECK(CalledOnValidThread()); |
| 820 | 797 |
| 821 DomainState state; | 798 DomainState state; |
| 822 const std::string canonicalized_host = CanonicalizeHost(host); | 799 const std::string canonicalized_host = CanonicalizeHost(host); |
| 823 if (canonicalized_host.empty()) | 800 if (canonicalized_host.empty()) |
| 824 return false; | 801 return false; |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 918 return pkp.spki_hashes.size() > 0 || pkp.bad_spki_hashes.size() > 0; | 895 return pkp.spki_hashes.size() > 0 || pkp.bad_spki_hashes.size() > 0; |
| 919 } | 896 } |
| 920 | 897 |
| 921 TransportSecurityState::DomainState::PKPState::PKPState() { | 898 TransportSecurityState::DomainState::PKPState::PKPState() { |
| 922 } | 899 } |
| 923 | 900 |
| 924 TransportSecurityState::DomainState::PKPState::~PKPState() { | 901 TransportSecurityState::DomainState::PKPState::~PKPState() { |
| 925 } | 902 } |
| 926 | 903 |
| 927 } // namespace | 904 } // namespace |
| OLD | NEW |