Index: net/http/transport_security_state.cc |
diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc |
index b00bc57b2fa649bd3bee604e965932acad09fcf1..f3912e0f64842220b420a7800a3e70f6d18cb0ab 100644 |
--- a/net/http/transport_security_state.cc |
+++ b/net/http/transport_security_state.cc |
@@ -270,372 +270,326 @@ std::string TransportSecurityState::CanonicalizeHost(const std::string& host) { |
return new_host; |
} |
-// |ReportUMAOnPinFailure| uses these to report which domain was associated |
-// with the public key pinning failure. |
-// |
-// DO NOT CHANGE THE ORDERING OF THESE NAMES OR REMOVE ANY OF THEM. Add new |
-// domains at the END of the listing (but before DOMAIN_NUM_EVENTS). |
-enum SecondLevelDomainName { |
- DOMAIN_NOT_PINNED, |
- |
- DOMAIN_GOOGLE_COM, |
- DOMAIN_ANDROID_COM, |
- DOMAIN_GOOGLE_ANALYTICS_COM, |
- DOMAIN_GOOGLEPLEX_COM, |
- DOMAIN_YTIMG_COM, |
- DOMAIN_GOOGLEUSERCONTENT_COM, |
- DOMAIN_YOUTUBE_COM, |
- DOMAIN_GOOGLEAPIS_COM, |
- DOMAIN_GOOGLEADSERVICES_COM, |
- DOMAIN_GOOGLECODE_COM, |
- DOMAIN_APPSPOT_COM, |
- DOMAIN_GOOGLESYNDICATION_COM, |
- DOMAIN_DOUBLECLICK_NET, |
- DOMAIN_GSTATIC_COM, |
- DOMAIN_GMAIL_COM, |
- DOMAIN_GOOGLEMAIL_COM, |
- DOMAIN_GOOGLEGROUPS_COM, |
- |
- DOMAIN_TORPROJECT_ORG, |
- |
- DOMAIN_TWITTER_COM, |
- DOMAIN_TWIMG_COM, |
- |
- DOMAIN_AKAMAIHD_NET, |
- |
- DOMAIN_TOR2WEB_ORG, |
- |
- DOMAIN_YOUTU_BE, |
- DOMAIN_GOOGLECOMMERCE_COM, |
- DOMAIN_URCHIN_COM, |
- DOMAIN_GOO_GL, |
- DOMAIN_G_CO, |
- DOMAIN_GOOGLE_AC, |
- DOMAIN_GOOGLE_AD, |
- DOMAIN_GOOGLE_AE, |
- DOMAIN_GOOGLE_AF, |
- DOMAIN_GOOGLE_AG, |
- DOMAIN_GOOGLE_AM, |
- DOMAIN_GOOGLE_AS, |
- DOMAIN_GOOGLE_AT, |
- DOMAIN_GOOGLE_AZ, |
- DOMAIN_GOOGLE_BA, |
- DOMAIN_GOOGLE_BE, |
- DOMAIN_GOOGLE_BF, |
- DOMAIN_GOOGLE_BG, |
- DOMAIN_GOOGLE_BI, |
- DOMAIN_GOOGLE_BJ, |
- DOMAIN_GOOGLE_BS, |
- DOMAIN_GOOGLE_BY, |
- DOMAIN_GOOGLE_CA, |
- DOMAIN_GOOGLE_CAT, |
- DOMAIN_GOOGLE_CC, |
- DOMAIN_GOOGLE_CD, |
- DOMAIN_GOOGLE_CF, |
- DOMAIN_GOOGLE_CG, |
- DOMAIN_GOOGLE_CH, |
- DOMAIN_GOOGLE_CI, |
- DOMAIN_GOOGLE_CL, |
- DOMAIN_GOOGLE_CM, |
- DOMAIN_GOOGLE_CN, |
- DOMAIN_CO_AO, |
- DOMAIN_CO_BW, |
- DOMAIN_CO_CK, |
- DOMAIN_CO_CR, |
- DOMAIN_CO_HU, |
- DOMAIN_CO_ID, |
- DOMAIN_CO_IL, |
- DOMAIN_CO_IM, |
- DOMAIN_CO_IN, |
- DOMAIN_CO_JE, |
- DOMAIN_CO_JP, |
- DOMAIN_CO_KE, |
- DOMAIN_CO_KR, |
- DOMAIN_CO_LS, |
- DOMAIN_CO_MA, |
- DOMAIN_CO_MZ, |
- DOMAIN_CO_NZ, |
- DOMAIN_CO_TH, |
- DOMAIN_CO_TZ, |
- DOMAIN_CO_UG, |
- DOMAIN_CO_UK, |
- DOMAIN_CO_UZ, |
- DOMAIN_CO_VE, |
- DOMAIN_CO_VI, |
- DOMAIN_CO_ZA, |
- DOMAIN_CO_ZM, |
- DOMAIN_CO_ZW, |
- DOMAIN_COM_AF, |
- DOMAIN_COM_AG, |
- DOMAIN_COM_AI, |
- DOMAIN_COM_AR, |
- DOMAIN_COM_AU, |
- DOMAIN_COM_BD, |
- DOMAIN_COM_BH, |
- DOMAIN_COM_BN, |
- DOMAIN_COM_BO, |
- DOMAIN_COM_BR, |
- DOMAIN_COM_BY, |
- DOMAIN_COM_BZ, |
- DOMAIN_COM_CN, |
- DOMAIN_COM_CO, |
- DOMAIN_COM_CU, |
- DOMAIN_COM_CY, |
- DOMAIN_COM_DO, |
- DOMAIN_COM_EC, |
- DOMAIN_COM_EG, |
- DOMAIN_COM_ET, |
- DOMAIN_COM_FJ, |
- DOMAIN_COM_GE, |
- DOMAIN_COM_GH, |
- DOMAIN_COM_GI, |
- DOMAIN_COM_GR, |
- DOMAIN_COM_GT, |
- DOMAIN_COM_HK, |
- DOMAIN_COM_IQ, |
- DOMAIN_COM_JM, |
- DOMAIN_COM_JO, |
- DOMAIN_COM_KH, |
- DOMAIN_COM_KW, |
- DOMAIN_COM_LB, |
- DOMAIN_COM_LY, |
- DOMAIN_COM_MT, |
- DOMAIN_COM_MX, |
- DOMAIN_COM_MY, |
- DOMAIN_COM_NA, |
- DOMAIN_COM_NF, |
- DOMAIN_COM_NG, |
- DOMAIN_COM_NI, |
- DOMAIN_COM_NP, |
- DOMAIN_COM_NR, |
- DOMAIN_COM_OM, |
- DOMAIN_COM_PA, |
- DOMAIN_COM_PE, |
- DOMAIN_COM_PH, |
- DOMAIN_COM_PK, |
- DOMAIN_COM_PL, |
- DOMAIN_COM_PR, |
- DOMAIN_COM_PY, |
- DOMAIN_COM_QA, |
- DOMAIN_COM_RU, |
- DOMAIN_COM_SA, |
- DOMAIN_COM_SB, |
- DOMAIN_COM_SG, |
- DOMAIN_COM_SL, |
- DOMAIN_COM_SV, |
- DOMAIN_COM_TJ, |
- DOMAIN_COM_TN, |
- DOMAIN_COM_TR, |
- DOMAIN_COM_TW, |
- DOMAIN_COM_UA, |
- DOMAIN_COM_UY, |
- DOMAIN_COM_VC, |
- DOMAIN_COM_VE, |
- DOMAIN_COM_VN, |
- DOMAIN_GOOGLE_CV, |
- DOMAIN_GOOGLE_CZ, |
- DOMAIN_GOOGLE_DE, |
- DOMAIN_GOOGLE_DJ, |
- DOMAIN_GOOGLE_DK, |
- DOMAIN_GOOGLE_DM, |
- DOMAIN_GOOGLE_DZ, |
- DOMAIN_GOOGLE_EE, |
- DOMAIN_GOOGLE_ES, |
- DOMAIN_GOOGLE_FI, |
- DOMAIN_GOOGLE_FM, |
- DOMAIN_GOOGLE_FR, |
- DOMAIN_GOOGLE_GA, |
- DOMAIN_GOOGLE_GE, |
- DOMAIN_GOOGLE_GG, |
- DOMAIN_GOOGLE_GL, |
- DOMAIN_GOOGLE_GM, |
- DOMAIN_GOOGLE_GP, |
- DOMAIN_GOOGLE_GR, |
- DOMAIN_GOOGLE_GY, |
- DOMAIN_GOOGLE_HK, |
- DOMAIN_GOOGLE_HN, |
- DOMAIN_GOOGLE_HR, |
- DOMAIN_GOOGLE_HT, |
- DOMAIN_GOOGLE_HU, |
- DOMAIN_GOOGLE_IE, |
- DOMAIN_GOOGLE_IM, |
- DOMAIN_GOOGLE_INFO, |
- DOMAIN_GOOGLE_IQ, |
- DOMAIN_GOOGLE_IS, |
- DOMAIN_GOOGLE_IT, |
- DOMAIN_IT_AO, |
- DOMAIN_GOOGLE_JE, |
- DOMAIN_GOOGLE_JO, |
- DOMAIN_GOOGLE_JOBS, |
- DOMAIN_GOOGLE_JP, |
- DOMAIN_GOOGLE_KG, |
- DOMAIN_GOOGLE_KI, |
- DOMAIN_GOOGLE_KZ, |
- DOMAIN_GOOGLE_LA, |
- DOMAIN_GOOGLE_LI, |
- DOMAIN_GOOGLE_LK, |
- DOMAIN_GOOGLE_LT, |
- DOMAIN_GOOGLE_LU, |
- DOMAIN_GOOGLE_LV, |
- DOMAIN_GOOGLE_MD, |
- DOMAIN_GOOGLE_ME, |
- DOMAIN_GOOGLE_MG, |
- DOMAIN_GOOGLE_MK, |
- DOMAIN_GOOGLE_ML, |
- DOMAIN_GOOGLE_MN, |
- DOMAIN_GOOGLE_MS, |
- DOMAIN_GOOGLE_MU, |
- DOMAIN_GOOGLE_MV, |
- DOMAIN_GOOGLE_MW, |
- DOMAIN_GOOGLE_NE, |
- DOMAIN_NE_JP, |
- DOMAIN_GOOGLE_NET, |
- DOMAIN_GOOGLE_NL, |
- DOMAIN_GOOGLE_NO, |
- DOMAIN_GOOGLE_NR, |
- DOMAIN_GOOGLE_NU, |
- DOMAIN_OFF_AI, |
- DOMAIN_GOOGLE_PK, |
- DOMAIN_GOOGLE_PL, |
- DOMAIN_GOOGLE_PN, |
- DOMAIN_GOOGLE_PS, |
- DOMAIN_GOOGLE_PT, |
- DOMAIN_GOOGLE_RO, |
- DOMAIN_GOOGLE_RS, |
- DOMAIN_GOOGLE_RU, |
- DOMAIN_GOOGLE_RW, |
- DOMAIN_GOOGLE_SC, |
- DOMAIN_GOOGLE_SE, |
- DOMAIN_GOOGLE_SH, |
- DOMAIN_GOOGLE_SI, |
- DOMAIN_GOOGLE_SK, |
- DOMAIN_GOOGLE_SM, |
- DOMAIN_GOOGLE_SN, |
- DOMAIN_GOOGLE_SO, |
- DOMAIN_GOOGLE_ST, |
- DOMAIN_GOOGLE_TD, |
- DOMAIN_GOOGLE_TG, |
- DOMAIN_GOOGLE_TK, |
- DOMAIN_GOOGLE_TL, |
- DOMAIN_GOOGLE_TM, |
- DOMAIN_GOOGLE_TN, |
- DOMAIN_GOOGLE_TO, |
- DOMAIN_GOOGLE_TP, |
- DOMAIN_GOOGLE_TT, |
- DOMAIN_GOOGLE_US, |
- DOMAIN_GOOGLE_UZ, |
- DOMAIN_GOOGLE_VG, |
- DOMAIN_GOOGLE_VU, |
- DOMAIN_GOOGLE_WS, |
- |
- DOMAIN_CHROMIUM_ORG, |
- |
- DOMAIN_CRYPTO_CAT, |
- DOMAIN_LAVABIT_COM, |
- |
- DOMAIN_GOOGLETAGMANAGER_COM, |
- DOMAIN_GOOGLETAGSERVICES_COM, |
- |
- DOMAIN_DROPBOX_COM, |
- DOMAIN_YOUTUBE_NOCOOKIE_COM, |
- DOMAIN_2MDN_NET, |
- |
- // Boundary value for UMA_HISTOGRAM_ENUMERATION: |
- DOMAIN_NUM_EVENTS |
-}; |
+// BitReader is a class that allows a bytestring to be read bit-by-bit. |
+class BitReader { |
+ public: |
+ 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.
|
+ : bytes_(bytes), |
+ num_bits_(num_bits), |
+ num_bytes_((num_bits + 7) / 8), |
+ used_bytes_(0), |
+ current_used_(8) {} |
+ |
+ // Next sets |*out| to the next bit from the input. It returns false if no |
+ // more bits are available or true otherwise. |
+ bool Next(bool *out) { |
+ if (current_used_ == 8) { |
+ if (used_bytes_ >= num_bytes_) { |
+ return false; |
+ } |
+ current_ = bytes_[used_bytes_++]; |
+ current_used_ = 0; |
+ } |
-// PublicKeyPins contains a number of SubjectPublicKeyInfo hashes for a site. |
-// The validated certificate chain for the site must not include any of |
-// |excluded_hashes| and must include one or more of |required_hashes|. |
-struct PublicKeyPins { |
- const char* const* required_hashes; |
- const char* const* excluded_hashes; |
-}; |
+ *out = 1 & (current_ >> (7 - current_used_)); |
+ current_used_++; |
+ return true; |
+ } |
-struct HSTSPreload { |
- uint8 length; |
- bool include_subdomains; |
- char dns_name[38]; |
- bool https_required; |
- PublicKeyPins pins; |
- SecondLevelDomainName second_level_domain_name; |
+ // Read sets the |num_bits| least-significant bits of |*out| to the value of |
+ // the next |num_bits| bits from the input. It returns false if there are |
+ // insufficient bits in the input or true otherwise. |
+ bool Read(unsigned num_bits, uint32 *out) { |
+ DCHECK_LE(num_bits, 32u); |
+ |
+ uint32 ret = 0; |
+ 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.
|
+ bool bit; |
+ if (!Next(&bit)) { |
+ return false; |
+ } |
+ ret |= static_cast<uint32>(bit) << (num_bits - 1 - i); |
+ } |
+ |
+ *out = ret; |
+ return true; |
+ } |
+ |
+ // Unary sets |*out| to the result of decoding a unary value from the input. |
+ // It returns false if there were insufficient bits in the input and true |
+ // otherwise. |
+ bool Unary(size_t *out) { |
+ size_t ret = 0; |
+ |
+ for (;;) { |
+ bool bit; |
+ if (!Next(&bit)) { |
+ return false; |
+ } |
+ if (!bit) { |
+ break; |
+ } |
+ ret++; |
+ } |
+ |
+ *out = ret; |
+ return true; |
+ } |
+ |
+ // Seek sets the current offest in the input to bit number |offset|. It |
+ // returns true if |offset| is within the range of the input and false |
+ // otherwise. |
+ bool Seek(size_t offset) { |
+ if (offset >= num_bits_) { |
+ return false; |
+ } |
+ used_bytes_ = offset / 8; |
+ current_ = bytes_[used_bytes_++]; |
+ current_used_ = offset % 8; |
+ return true; |
+ } |
+ |
+ private: |
+ const uint8 *const bytes_; |
+ const size_t num_bits_; |
+ const size_t num_bytes_; |
+ // used_bytes_ contains the current byte offset in |bytes_|. |
+ size_t used_bytes_; |
+ // current_ contains the current byte of the input. |
+ 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.
|
+ // current_used_ contains the number of bits of |current_| that have been |
+ // read. |
+ unsigned current_used_; |
}; |
-static bool HasPreload(const struct HSTSPreload* entries, |
- size_t num_entries, |
- const std::string& canonicalized_host, |
- size_t i, |
- bool enable_static_pins, |
- TransportSecurityState::DomainState* out, |
- bool* ret) { |
- for (size_t j = 0; j < num_entries; j++) { |
- if (entries[j].length == canonicalized_host.size() - i && |
- memcmp(entries[j].dns_name, &canonicalized_host[i], |
- entries[j].length) == 0) { |
- if (!entries[j].include_subdomains && i != 0) { |
- *ret = false; |
- } else { |
- out->sts.include_subdomains = entries[j].include_subdomains; |
- out->sts.last_observed = base::GetBuildTime(); |
- *ret = true; |
- out->sts.upgrade_mode = |
- TransportSecurityState::DomainState::MODE_FORCE_HTTPS; |
- if (!entries[j].https_required) |
- out->sts.upgrade_mode = |
- TransportSecurityState::DomainState::MODE_DEFAULT; |
- |
- if (enable_static_pins) { |
- out->pkp.include_subdomains = entries[j].include_subdomains; |
- out->pkp.last_observed = base::GetBuildTime(); |
- if (entries[j].pins.required_hashes) { |
- const char* const* sha1_hash = entries[j].pins.required_hashes; |
- while (*sha1_hash) { |
- AddHash(*sha1_hash, &out->pkp.spki_hashes); |
- sha1_hash++; |
- } |
- } |
- if (entries[j].pins.excluded_hashes) { |
- const char* const* sha1_hash = entries[j].pins.excluded_hashes; |
- while (*sha1_hash) { |
- AddHash(*sha1_hash, &out->pkp.bad_spki_hashes); |
- sha1_hash++; |
- } |
- } |
- } |
+// HuffmanDecoder is a very simple Huffman reader. The input Huffman tree is |
+// simply encoded as a series of two-byte structures. The first byte determines |
+// the "0" pointer for that node and the second the "1" pointer. Each byte |
+// either has the MSB set, in which case the bottom 7 bits are the value for |
+// that position, or else the bottom seven bits contain the index of a node. |
+// |
+// The tree is decoded by walking rather than a table-driven approach. |
+class HuffmanDecoder { |
+ public: |
+ HuffmanDecoder(const uint8 *tree, size_t tree_bytes) |
+ : tree_(tree), |
+ tree_bytes_(tree_bytes) {} |
+ |
+ bool Decode(BitReader *reader, char *out) { |
+ const uint8 *current = &tree_[tree_bytes_-2]; |
+ |
+ for (;;) { |
+ bool bit; |
+ if (!reader->Next(&bit)) { |
+ return false; |
} |
- return true; |
+ |
+ uint8 b = current[bit]; |
+ if (b & 0x80) { |
+ *out = static_cast<char>(b & 0x7f); |
+ return true; |
+ } |
+ |
+ unsigned offset = static_cast<unsigned>(b) * 2; |
+ DCHECK_LT(offset, tree_bytes_); |
+ if (offset >= tree_bytes_) { |
+ return false; |
+ } |
+ |
+ current = &tree_[offset]; |
} |
} |
- return false; |
-} |
+ |
+ private: |
+ const uint8 *const tree_; |
+ const size_t tree_bytes_; |
+}; |
#include "net/http/transport_security_state_static.h" |
-// Returns the HSTSPreload entry for the |canonicalized_host| in |entries|, |
-// or NULL if there is none. Prefers exact hostname matches to those that |
-// match only because HSTSPreload.include_subdomains is true. |
+// PreloadResult is the result of resolving a specific name in the preloaded |
+// data. |
+struct PreloadResult { |
+ bool include_subdomains; |
+ bool force_https; |
+ bool has_pins; |
+ 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
|
+ uint32 domain_id; |
+ // hostname_offset contains the number of bytes from the start of the given |
+ // hostname where the name of the matching entry starts. |
+ size_t hostname_offset; |
+}; |
+ |
+// DecodeHSTSPreload resolves |hostname| in the preloaded data. It returns |
+// 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
|
+// |*out_found| is true iff a relevant entry has been found. If so, |*out| |
+// contains the details. |
// |
-// |canonicalized_host| should be the hostname as canonicalized by |
-// CanonicalizeHost. |
-static const struct HSTSPreload* GetHSTSPreload( |
- const std::string& canonicalized_host, |
- const struct HSTSPreload* entries, |
- size_t num_entries) { |
- for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
- for (size_t j = 0; j < num_entries; j++) { |
- const struct HSTSPreload* entry = entries + j; |
+// Although this code should be robust, it never processes attacker-controlled |
+// data -- it only operates on the preloaded data built into the binary. |
+// |
+// The preloaded data is represented as a trie and matches the hostname |
+// backwards. Each node in the trie starts with a number of characters, which |
+// must match exactly. After that is a dispatch table which maps the next |
+// character in the hostname to another node in the trie. |
+// |
+// 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.
|
+// (which is the *beginning* of a hostname since we process it backwards). The |
+// value in that case is special -- rather than an offset to another trie node, |
+// it contains the HSTS information: whether subdomains are included, pinsets |
+// etc. If an "end of string" matches a period in the hostname then the |
+// information is remembered because, if no more specific node is found, then |
+// that information applies to the hostname. |
+// |
+// Dispatch tables are always given in order, but the "end of string" (zero) |
+// value always comes before an entry for '.'. |
+bool DecodeHSTSPreload(const std::string& hostname, |
+ bool* out_found, |
+ PreloadResult* out) { |
+ HuffmanDecoder huffman(kHSTSHuffmanTree, sizeof(kHSTSHuffmanTree)); |
+ BitReader reader(kPreloadedHSTSData, kPreloadedHSTSBits); |
+ size_t bit_offset = kHSTSRootPosition; |
+ static const char kEndOfString = 0; |
+ static const char kEndOfTable = 127; |
+ |
+ *out_found = false; |
+ |
+ if (hostname.empty()) { |
+ return true; |
+ } |
+ // hostname_offset contains one more than the index of the current character |
+ // in the hostname that is being considered. It's one greater so that we can |
+ // represent the position just before the beginning (with zero). |
+ size_t hostname_offset = hostname.size(); |
+ |
+ for (;;) { |
+ // Seek to the desired location. |
+ if (!reader.Seek(bit_offset)) { |
+ return false; |
+ } |
+ |
+ // Decode the unary length of the common prefix. |
+ size_t prefix_length; |
+ if (!reader.Unary(&prefix_length)) { |
+ return false; |
+ } |
+ |
+ // Match each character in the prefix. |
+ for (size_t i = 0; i < prefix_length; i++) { |
+ if (hostname_offset == 0) { |
+ // We can't match the terminator with a prefix string. |
+ return true; |
+ } |
+ |
+ char c; |
+ if (!huffman.Decode(&reader, &c)) { |
+ return false; |
+ } |
+ if (hostname[hostname_offset - 1] != c) { |
+ return true; |
+ } |
+ hostname_offset--; |
+ } |
+ |
+ bool is_first_offset = true; |
+ size_t current_offset = 0; |
+ |
+ // Next is the dispatch table. |
+ for (;;) { |
+ char c; |
+ if (!huffman.Decode(&reader, &c)) { |
+ return false; |
+ } |
+ if (c == kEndOfTable) { |
+ // No exact match. |
+ return true; |
+ } |
+ |
+ if (c == kEndOfString) { |
+ PreloadResult tmp; |
+ if (!reader.Next(&tmp.include_subdomains) || |
+ !reader.Next(&tmp.force_https) || |
+ !reader.Next(&tmp.has_pins)) { |
+ return false; |
+ } |
+ |
+ if (tmp.has_pins) { |
+ if (!reader.Read(4, &tmp.pinset_id) || |
+ !reader.Read(9, &tmp.domain_id)) { |
+ return false; |
+ } |
+ } |
+ |
+ tmp.hostname_offset = hostname_offset; |
+ |
+ if (hostname_offset == 0 || hostname[hostname_offset - 1] == '.') { |
+ *out_found = tmp.include_subdomains; |
+ *out = tmp; |
+ } |
+ |
+ if (hostname_offset == 0) { |
+ *out_found = true; |
+ return true; |
+ } |
- if (i != 0 && !entry->include_subdomains) |
continue; |
+ } |
- if (entry->length == canonicalized_host.size() - i && |
- memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) { |
- return entry; |
+ // The entries in a dispatch table are in order thus we can tell if there |
+ // will be no match if the current character past the one that we want. |
+ if (hostname_offset == 0 || hostname[hostname_offset-1] < c) { |
+ return true; |
+ } |
+ |
+ if (is_first_offset) { |
+ // The first offset is backwards from the current position. |
+ uint32 jump_delta_bits; |
+ uint32 jump_delta; |
+ if (!reader.Read(5, &jump_delta_bits) || |
+ !reader.Read(jump_delta_bits, &jump_delta)) { |
+ return false; |
+ } |
+ |
+ if (bit_offset <= jump_delta) { |
+ return false; |
+ } |
+ |
+ current_offset = bit_offset - jump_delta; |
+ is_first_offset = false; |
+ } else { |
+ // Subsequent offsets are forward from the target of the first offset. |
+ uint32 is_long_jump; |
+ if (!reader.Read(1, &is_long_jump)) { |
+ return false; |
+ } |
+ |
+ uint32 jump_delta; |
+ if (!is_long_jump) { |
+ if (!reader.Read(7, &jump_delta)) { |
+ return false; |
+ } |
+ } else { |
+ uint32 jump_delta_bits; |
+ if (!reader.Read(4, &jump_delta_bits) || |
+ !reader.Read(jump_delta_bits + 8, &jump_delta)) { |
+ return false; |
+ } |
+ } |
+ |
+ current_offset += jump_delta; |
+ if (current_offset >= bit_offset) { |
+ return false; |
+ } |
+ } |
+ |
+ DCHECK_LT(0u, hostname_offset); |
+ if (hostname[hostname_offset - 1] == c) { |
+ bit_offset = current_offset; |
+ hostname_offset--; |
+ break; |
} |
} |
} |
- |
- return NULL; |
} |
bool TransportSecurityState::AddHSTSHeader(const std::string& host, |
@@ -732,31 +686,26 @@ bool TransportSecurityState::AddHPKP(const std::string& host, |
// static |
bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host) { |
- std::string canonicalized_host = CanonicalizeHost(host); |
- const struct HSTSPreload* entry = |
- GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
- |
- return entry && entry->pins.required_hashes == kGoogleAcceptableCerts; |
+ bool found; |
+ PreloadResult result; |
+ return DecodeHSTSPreload(host, &found, &result) && found && result.has_pins && |
+ kPinsets[result.pinset_id].accepted_pins == kGoogleAcceptableCerts; |
} |
// static |
void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { |
- std::string canonicalized_host = CanonicalizeHost(host); |
- |
- const struct HSTSPreload* entry = |
- GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); |
- |
- if (!entry) { |
- // We don't care to report pin failures for dynamic pins. |
+ bool found; |
+ PreloadResult result; |
+ if (!DecodeHSTSPreload(host, &found, &result) || |
+ !found || |
+ !result.has_pins) { |
return; |
} |
- DCHECK(entry); |
- DCHECK(entry->pins.required_hashes); |
- DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); |
+ DCHECK(result.domain_id != DOMAIN_NOT_PINNED); |
- UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", |
- entry->second_level_domain_name, DOMAIN_NUM_EVENTS); |
+ UMA_HISTOGRAM_ENUMERATION( |
+ "Net.PublicKeyPinFailureDomain", result.domain_id, DOMAIN_NUM_EVENTS); |
} |
// static |
@@ -787,31 +736,59 @@ bool TransportSecurityState::GetStaticDomainState(const std::string& host, |
DomainState* out) const { |
DCHECK(CalledOnValidThread()); |
- const std::string canonicalized_host = CanonicalizeHost(host); |
- |
out->sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS; |
out->sts.include_subdomains = false; |
out->pkp.include_subdomains = false; |
- const bool is_build_timely = IsBuildTimely(); |
+ if (!IsBuildTimely()) |
+ return false; |
- for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { |
- std::string host_sub_chunk(&canonicalized_host[i], |
- canonicalized_host.size() - i); |
- out->domain = DNSDomainToString(host_sub_chunk); |
- bool ret; |
- if (is_build_timely && HasPreload(kPreloadedSTS, |
- kNumPreloadedSTS, |
- canonicalized_host, |
- i, |
- enable_static_pins_, |
- out, |
- &ret)) { |
- return ret; |
+ bool found; |
+ PreloadResult result; |
+ if (!DecodeHSTSPreload(host, &found, &result)) { |
+ LOG(ERROR) << "Internal error decoding HSTS data for " << host; |
+ return false; |
+ } |
+ |
+ if (!found) |
+ return false; |
+ |
+ out->domain = host.substr(result.hostname_offset); |
+ out->sts.include_subdomains = result.include_subdomains; |
+ out->sts.last_observed = base::GetBuildTime(); |
+ out->sts.upgrade_mode = |
+ TransportSecurityState::DomainState::MODE_DEFAULT; |
+ if (result.force_https) { |
+ out->sts.upgrade_mode = |
+ TransportSecurityState::DomainState::MODE_FORCE_HTTPS; |
+ } |
+ |
+ if (enable_static_pins_ && result.has_pins) { |
+ out->pkp.include_subdomains = result.include_subdomains; |
+ out->pkp.last_observed = base::GetBuildTime(); |
+ |
+ if (result.pinset_id >= arraysize(kPinsets)) { |
+ return false; |
+ } |
+ const Pinset *pinset = &kPinsets[result.pinset_id]; |
+ |
+ if (pinset->accepted_pins) { |
+ const char* const* sha1_hash = pinset->accepted_pins; |
+ while (*sha1_hash) { |
+ AddHash(*sha1_hash, &out->pkp.spki_hashes); |
+ sha1_hash++; |
+ } |
+ } |
+ if (pinset->rejected_pins) { |
+ const char* const* sha1_hash = pinset->rejected_pins; |
+ while (*sha1_hash) { |
+ AddHash(*sha1_hash, &out->pkp.bad_spki_hashes); |
+ sha1_hash++; |
+ } |
} |
} |
- return false; |
+ return true; |
} |
bool TransportSecurityState::GetDynamicDomainState(const std::string& host, |