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 | 19 |
20 #include "base/base64.h" | 20 #include "base/base64.h" |
21 #include "base/build_time.h" | 21 #include "base/build_time.h" |
22 #include "base/logging.h" | 22 #include "base/logging.h" |
23 #include "base/memory/scoped_ptr.h" | 23 #include "base/memory/scoped_ptr.h" |
24 #include "base/metrics/histogram.h" | 24 #include "base/metrics/histogram.h" |
25 #include "base/sha1.h" | 25 #include "base/sha1.h" |
26 #include "base/string_number_conversions.h" | |
27 #include "base/string_util.h" | 26 #include "base/string_util.h" |
28 #include "base/time.h" | 27 #include "base/time.h" |
29 #include "base/utf_string_conversions.h" | |
30 #include "base/values.h" | |
31 #include "crypto/sha2.h" | 28 #include "crypto/sha2.h" |
32 #include "googleurl/src/gurl.h" | 29 #include "googleurl/src/gurl.h" |
33 #include "net/base/dns_util.h" | 30 #include "net/base/dns_util.h" |
34 #include "net/base/ssl_info.h" | 31 #include "net/base/ssl_info.h" |
35 #include "net/base/x509_cert_types.h" | 32 #include "net/base/transport_security_state_preload.h" |
36 #include "net/base/x509_certificate.h" | 33 #include "net/base/x509_certificate.h" |
37 #include "net/http/http_security_headers.h" | 34 #include "net/http/http_security_headers.h" |
38 | 35 |
39 #if defined(USE_OPENSSL) | 36 #if defined(USE_OPENSSL) |
40 #include "crypto/openssl_util.h" | 37 #include "crypto/openssl_util.h" |
41 #endif | 38 #endif |
42 | 39 |
43 namespace net { | 40 namespace net { |
44 | 41 |
45 namespace { | 42 namespace { |
46 | 43 |
47 std::string HashesToBase64String(const HashValueVector& hashes) { | 44 std::string HashesToBase64String(const HashValueVector& hashes) { |
48 std::string str; | 45 std::string str; |
49 for (size_t i = 0; i != hashes.size(); ++i) { | 46 for (size_t i = 0; i != hashes.size(); ++i) { |
50 if (i != 0) | 47 if (i != 0) |
51 str += ","; | 48 str += ","; |
52 str += hashes[i].ToString(); | 49 str += hashes[i].ToString(); |
53 } | 50 } |
54 return str; | 51 return str; |
55 } | 52 } |
56 | 53 |
57 std::string HashHost(const std::string& canonicalized_host) { | 54 std::string HashHost(const std::string& host) { |
| 55 std::string lowercase = StringToLowerASCII(host); |
| 56 std::string old_style_canonicalized_name; |
| 57 if (!DNSDomainFromDot(lowercase, &old_style_canonicalized_name)) { |
| 58 old_style_canonicalized_name.clear(); |
| 59 LOG(WARNING) << "Bad DNS name passed to TransportSecurityState"; |
| 60 } |
| 61 |
58 char hashed[crypto::kSHA256Length]; | 62 char hashed[crypto::kSHA256Length]; |
59 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); | 63 crypto::SHA256HashString(old_style_canonicalized_name, hashed, |
| 64 sizeof(hashed)); |
60 return std::string(hashed, sizeof(hashed)); | 65 return std::string(hashed, sizeof(hashed)); |
61 } | 66 } |
62 | 67 |
63 // Returns true if the intersection of |a| and |b| is not empty. If either | 68 // Returns true if the intersection of |a| and |b| is not empty. If either |
64 // |a| or |b| is empty, returns false. | 69 // |a| or |b| is empty, returns false. |
65 bool HashesIntersect(const HashValueVector& a, | 70 bool HashesIntersect(const HashValueVector& a, |
66 const HashValueVector& b) { | 71 const HashValueVector& b) { |
67 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { | 72 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { |
68 HashValueVector::const_iterator j = | 73 HashValueVector::const_iterator j = |
69 std::find_if(b.begin(), b.end(), HashValuesEqual(*i)); | 74 std::find_if(b.begin(), b.end(), HashValuesEqual(*i)); |
70 if (j != b.end()) | 75 if (j != b.end()) |
71 return true; | 76 return true; |
72 } | 77 } |
73 return false; | 78 return false; |
74 } | 79 } |
75 | 80 |
76 bool AddHash(const char* sha1_hash, | 81 // Iterate over ("www.example.com", "example.com", "com") |
77 HashValueVector* out) { | 82 struct DomainNameIterator { |
78 HashValue hash(HASH_VALUE_SHA1); | 83 explicit DomainNameIterator(const std::string& host) { |
79 memcpy(hash.data(), sha1_hash, hash.size()); | 84 name_ = StringToLowerASCII(host); |
80 out->push_back(hash); | 85 index_ = 0; |
| 86 } |
| 87 |
| 88 bool AtEnd() { |
| 89 return index_ == name_.length(); |
| 90 } |
| 91 |
| 92 // Advance to NUL char, or after the next '.' |
| 93 void Advance() { |
| 94 if (AtEnd()) |
| 95 return; |
| 96 for (index_++; name_[index_] != '.' && name_[index_] != 0; ++index_); |
| 97 if (name_[index_] == '.') |
| 98 index_++; |
| 99 } |
| 100 |
| 101 std::string GetName() { |
| 102 return name_.substr(index_); |
| 103 } |
| 104 |
| 105 bool IsFullHostname() { |
| 106 return index_ == 0; |
| 107 } |
| 108 |
| 109 std::string name_; // The full hostname, canonicalized to lowercase |
| 110 size_t index_; // Index into name_ |
| 111 }; |
| 112 |
| 113 // Template functions for maps of DynamicEntries (or subclasses) |
| 114 template <typename T, typename MapType = std::map<std::string, T> > |
| 115 bool GetDynamicEntry(const std::map<std::string, T>& entries, |
| 116 const base::Time& now, |
| 117 const std::string& hashed_host, |
| 118 bool is_full_hostname, |
| 119 T* result_entry) { |
| 120 // Find the entry, and return if relevant and nonexpired |
| 121 typename MapType::const_iterator find_iter = entries.find(hashed_host); |
| 122 if (find_iter == entries.end()) |
| 123 return false; |
| 124 |
| 125 const T& found_entry = find_iter->second; |
| 126 if ((is_full_hostname || found_entry.include_subdomains_) && |
| 127 found_entry.expiry_ > now) { |
| 128 *result_entry = found_entry; |
| 129 return true; |
| 130 } |
| 131 return false; |
| 132 } |
| 133 |
| 134 template<typename T, typename MapType = std::map<std::string, T> > |
| 135 bool AddDynamicEntry(std::map<std::string, T>& entries, |
| 136 const std::string& hashed_host, |
| 137 const T& new_entry) { |
| 138 typename MapType::iterator find_iter = entries.find(hashed_host); |
| 139 if (find_iter != entries.end()) { |
| 140 // Leave 'created' unchanged |
| 141 T& found_entry = find_iter->second; |
| 142 found_entry.expiry_ = new_entry.expiry_; |
| 143 found_entry.include_subdomains_ = new_entry.include_subdomains_; |
| 144 } else { |
| 145 entries[hashed_host] = new_entry; |
| 146 } |
81 return true; | 147 return true; |
82 } | 148 } |
83 | 149 |
| 150 template<typename T, typename MapType = std::map<std::string, T> > |
| 151 bool DeleteDynamicEntry(std::map<std::string, T>& entries, |
| 152 const std::string& hashed_host) { |
| 153 typename MapType::iterator find_iter = entries.find(hashed_host); |
| 154 if (find_iter != entries.end()) { |
| 155 entries.erase(find_iter); |
| 156 return true; |
| 157 } |
| 158 return false; |
| 159 } |
| 160 |
| 161 template<typename T, typename MapType = std::map<std::string, T> > |
| 162 bool DeleteDynamicEntriesSince(std::map<std::string, T>& entries, |
| 163 const base::Time& time) { |
| 164 typename MapType::iterator iter = entries.begin(); |
| 165 bool deleted = false; |
| 166 while (iter != entries.end()) { |
| 167 if (iter->second.created_ >= time) { |
| 168 entries.erase(iter++); |
| 169 deleted = true; |
| 170 } else { |
| 171 iter++; |
| 172 } |
| 173 } |
| 174 return deleted; |
| 175 } |
| 176 |
84 } // namespace | 177 } // namespace |
85 | 178 |
| 179 |
86 TransportSecurityState::TransportSecurityState() | 180 TransportSecurityState::TransportSecurityState() |
87 : delegate_(NULL) { | 181 : delegate_(NULL) { |
88 } | 182 } |
89 | 183 |
90 TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state) | 184 TransportSecurityState::~TransportSecurityState() {} |
91 : iterator_(state.enabled_hosts_.begin()), | |
92 end_(state.enabled_hosts_.end()) { | |
93 } | |
94 | |
95 TransportSecurityState::Iterator::~Iterator() {} | |
96 | 185 |
97 void TransportSecurityState::SetDelegate( | 186 void TransportSecurityState::SetDelegate( |
98 TransportSecurityState::Delegate* delegate) { | 187 TransportSecurityState::Delegate* delegate) { |
99 delegate_ = delegate; | 188 delegate_ = delegate; |
100 } | 189 } |
101 | 190 |
102 void TransportSecurityState::EnableHost(const std::string& host, | 191 void TransportSecurityState::ClearDynamicData() { |
103 const DomainState& state) { | 192 hsts_entries_.clear(); |
| 193 hpkp_entries_.clear(); |
| 194 } |
| 195 |
| 196 void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) { |
104 DCHECK(CalledOnValidThread()); | 197 DCHECK(CalledOnValidThread()); |
105 | 198 bool deleted_hsts = DeleteDynamicEntriesSince(hsts_entries_, time); |
106 const std::string canonicalized_host = CanonicalizeHost(host); | 199 bool deleted_hpkp = DeleteDynamicEntriesSince(hpkp_entries_, time); |
107 if (canonicalized_host.empty()) | 200 if (deleted_hsts || deleted_hpkp) |
108 return; | 201 StateIsDirty(); |
109 | |
110 DomainState existing_state; | |
111 | |
112 // Use the original creation date if we already have this host. (But note | |
113 // that statically-defined states have no |created| date. Therefore, we do | |
114 // not bother to search the SNI-only static states.) | |
115 DomainState state_copy(state); | |
116 if (GetDomainState(host, false /* sni_enabled */, &existing_state) && | |
117 !existing_state.created.is_null()) { | |
118 state_copy.created = existing_state.created; | |
119 } | |
120 | |
121 // No need to store this value since it is redundant. (|canonicalized_host| | |
122 // is the map key.) | |
123 state_copy.domain.clear(); | |
124 | |
125 enabled_hosts_[HashHost(canonicalized_host)] = state_copy; | |
126 DirtyNotify(); | |
127 } | 202 } |
128 | 203 |
129 bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) { | 204 bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) { |
130 DCHECK(CalledOnValidThread()); | 205 DCHECK(CalledOnValidThread()); |
131 | 206 bool deleted_hsts = DeleteHSTS(host); |
132 const std::string canonicalized_host = CanonicalizeHost(host); | 207 bool deleted_hpkp = DeleteHPKP(host); |
133 if (canonicalized_host.empty()) | 208 if (deleted_hsts || deleted_hpkp) |
134 return false; | 209 StateIsDirty(); |
135 | 210 return deleted_hsts || deleted_hpkp; |
136 DomainStateMap::iterator i = enabled_hosts_.find( | |
137 HashHost(canonicalized_host)); | |
138 if (i != enabled_hosts_.end()) { | |
139 enabled_hosts_.erase(i); | |
140 DirtyNotify(); | |
141 return true; | |
142 } | |
143 return false; | |
144 } | 211 } |
145 | 212 |
146 bool TransportSecurityState::GetDomainState(const std::string& host, | 213 bool TransportSecurityState::GetDomainState(const std::string& host, |
147 bool sni_enabled, | 214 bool sni_enabled, |
148 DomainState* result) { | 215 DomainState* result) const { |
149 DCHECK(CalledOnValidThread()); | 216 DCHECK(CalledOnValidThread()); |
150 | 217 bool found = false; |
151 DomainState state; | 218 const base::Time now = base::Time::Now(); |
152 const std::string canonicalized_host = CanonicalizeHost(host); | 219 DomainState dynamic_state; |
153 if (canonicalized_host.empty()) | 220 |
| 221 found = GetPreloadDomainState(sni_enabled, now, host, result); |
| 222 found = GetDynamicDomainState(now, host, &dynamic_state) || found; |
| 223 |
| 224 // Merge dynamic state into preload state |
| 225 // Currently, HSTS and HPKP are set if either state has them set. |
| 226 // However, if both states have HPKP set, the preload pins take precedence. |
| 227 // This behavior may change (e.g. for the most-recent to take priority). |
| 228 if (!result->should_upgrade_ && dynamic_state.should_upgrade_) |
| 229 result->should_upgrade_ = true; |
| 230 if (!result->has_public_key_pins_ && dynamic_state.has_public_key_pins_) { |
| 231 result->has_public_key_pins_ = true; |
| 232 result->public_key_pins_good_hashes_ = |
| 233 dynamic_state.public_key_pins_good_hashes_; |
| 234 } |
| 235 return found; |
| 236 } |
| 237 |
| 238 bool TransportSecurityState::GetDynamicDomainState(const base::Time& now, |
| 239 const std::string& host, |
| 240 DomainState* result) const { |
| 241 HSTSEntry hsts_entry; |
| 242 HPKPEntry hpkp_entry; |
| 243 // Iterate over 'www.example.com", 'example.com", "com" |
| 244 for (DomainNameIterator iter(host); !iter.AtEnd(); iter.Advance()) { |
| 245 std::string hashed_host = HashHost(iter.GetName()); |
| 246 bool is_full_hostname = iter.IsFullHostname(); |
| 247 |
| 248 // Get HSTS data from map |
| 249 if (!result->should_upgrade_ && |
| 250 GetDynamicEntry(hsts_entries_, now, hashed_host, is_full_hostname, |
| 251 &hsts_entry)) { |
| 252 result->should_upgrade_ = true; |
| 253 } |
| 254 |
| 255 // Get HPKP data from map |
| 256 if (!result->has_public_key_pins_ && |
| 257 GetDynamicEntry(hpkp_entries_, now, hashed_host, is_full_hostname, |
| 258 &hpkp_entry)) { |
| 259 result->has_public_key_pins_ = true; |
| 260 result->public_key_pins_good_hashes_ = hpkp_entry.good_hashes_; |
| 261 } |
| 262 |
| 263 // If we've got all possible data, exit early |
| 264 if (result->should_upgrade_ && result->has_public_key_pins_) |
| 265 return true; |
| 266 } |
| 267 return result->should_upgrade_ || result->has_public_key_pins_; |
| 268 } |
| 269 |
| 270 bool TransportSecurityState::GetPreloadDomainState(bool sni_enabled, |
| 271 const base::Time& now, |
| 272 const std::string& host, |
| 273 DomainState* result) const { |
| 274 #if defined(PRELOADS_PRESENT) |
| 275 const PreloadEntry* entries = kPreloadedEntries; |
| 276 size_t num_entries = kNumPreloaded; |
| 277 |
| 278 if (!IsBuildTimely()) |
154 return false; | 279 return false; |
155 | 280 |
156 bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled, | 281 for (int count = 0; count < 2; count++) { |
157 &state); | 282 // If sni_enabled, then scan through SNI entries (if necessary) |
158 std::string canonicalized_preload = CanonicalizeHost(state.domain); | 283 if (count == 1) { |
159 | 284 if (!sni_enabled) |
160 base::Time current_time(base::Time::Now()); | 285 break; |
161 | 286 entries = kPreloadedEntriesSNI; |
162 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 287 num_entries = kNumPreloadedSNI; |
163 std::string host_sub_chunk(&canonicalized_host[i], | 288 } |
164 canonicalized_host.size() - i); | 289 |
165 // Exact match of a preload always wins. | 290 for (DomainNameIterator iter(host); !iter.AtEnd(); iter.Advance()) { |
166 if (has_preload && host_sub_chunk == canonicalized_preload) { | 291 std::string name = iter.GetName(); |
167 *result = state; | 292 for (size_t index = 0; index < num_entries; index++) { |
168 return true; | 293 const PreloadEntry& entry = entries[index]; |
169 } | 294 |
170 | 295 // If we find a relevant preload entry, populate the |
171 DomainStateMap::iterator j = | 296 // entire DomainState from it and return |
172 enabled_hosts_.find(HashHost(host_sub_chunk)); | 297 if (entry.length == name.size() && |
173 if (j == enabled_hosts_.end()) | 298 (iter.IsFullHostname() || entry.include_subdomains) && |
174 continue; | 299 memcmp(entry.dns_name, name.data(), entry.length) == 0) { |
175 | 300 if (entry.https_required) |
176 if (current_time > j->second.upgrade_expiry && | 301 result->should_upgrade_ = true; |
177 current_time > j->second.dynamic_spki_hashes_expiry) { | 302 |
178 enabled_hosts_.erase(j); | 303 if (entry.pins.required_hashes || entry.pins.excluded_hashes) |
179 DirtyNotify(); | 304 result->has_public_key_pins_ = true; |
180 continue; | 305 HashValue hash(HASH_VALUE_SHA1); |
181 } | 306 if (entry.pins.required_hashes) { |
182 | 307 const char* const* sha1_hashes = entry.pins.required_hashes; |
183 state = j->second; | 308 while (*sha1_hashes) { |
184 state.domain = DNSDomainToString(host_sub_chunk); | 309 memcpy(hash.data(), *sha1_hashes, hash.size()); |
185 | 310 result->public_key_pins_good_hashes_.push_back(hash); |
186 // Succeed if we matched the domain exactly or if subdomain matches are | 311 sha1_hashes++; |
187 // allowed. | 312 } |
188 if (i == 0 || j->second.include_subdomains) { | |
189 *result = state; | |
190 return true; | |
191 } | |
192 | |
193 return false; | |
194 } | |
195 | |
196 return false; | |
197 } | |
198 | |
199 void TransportSecurityState::ClearDynamicData() { | |
200 enabled_hosts_.clear(); | |
201 } | |
202 | |
203 void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) { | |
204 DCHECK(CalledOnValidThread()); | |
205 | |
206 bool dirtied = false; | |
207 | |
208 DomainStateMap::iterator i = enabled_hosts_.begin(); | |
209 while (i != enabled_hosts_.end()) { | |
210 if (i->second.created >= time) { | |
211 dirtied = true; | |
212 enabled_hosts_.erase(i++); | |
213 } else { | |
214 i++; | |
215 } | |
216 } | |
217 | |
218 if (dirtied) | |
219 DirtyNotify(); | |
220 } | |
221 | |
222 TransportSecurityState::~TransportSecurityState() {} | |
223 | |
224 void TransportSecurityState::DirtyNotify() { | |
225 DCHECK(CalledOnValidThread()); | |
226 | |
227 if (delegate_) | |
228 delegate_->StateIsDirty(this); | |
229 } | |
230 | |
231 // static | |
232 std::string TransportSecurityState::CanonicalizeHost(const std::string& host) { | |
233 // We cannot perform the operations as detailed in the spec here as |host| | |
234 // has already undergone IDN processing before it reached us. Thus, we check | |
235 // that there are no invalid characters in the host and lowercase the result. | |
236 | |
237 std::string new_host; | |
238 if (!DNSDomainFromDot(host, &new_host)) { | |
239 // DNSDomainFromDot can fail if any label is > 63 bytes or if the whole | |
240 // name is >255 bytes. However, search terms can have those properties. | |
241 return std::string(); | |
242 } | |
243 | |
244 for (size_t i = 0; new_host[i]; i += new_host[i] + 1) { | |
245 const unsigned label_length = static_cast<unsigned>(new_host[i]); | |
246 if (!label_length) | |
247 break; | |
248 | |
249 for (size_t j = 0; j < label_length; ++j) { | |
250 // RFC 3490, 4.1, step 3 | |
251 if (!IsSTD3ASCIIValidCharacter(new_host[i + 1 + j])) | |
252 return std::string(); | |
253 | |
254 new_host[i + 1 + j] = tolower(new_host[i + 1 + j]); | |
255 } | |
256 | |
257 // step 3(b) | |
258 if (new_host[i + 1] == '-' || | |
259 new_host[i + label_length] == '-') { | |
260 return std::string(); | |
261 } | |
262 } | |
263 | |
264 return new_host; | |
265 } | |
266 | |
267 // |ReportUMAOnPinFailure| uses these to report which domain was associated | |
268 // with the public key pinning failure. | |
269 // | |
270 // DO NOT CHANGE THE ORDERING OF THESE NAMES OR REMOVE ANY OF THEM. Add new | |
271 // domains at the END of the listing (but before DOMAIN_NUM_EVENTS). | |
272 enum SecondLevelDomainName { | |
273 DOMAIN_NOT_PINNED, | |
274 | |
275 DOMAIN_GOOGLE_COM, | |
276 DOMAIN_ANDROID_COM, | |
277 DOMAIN_GOOGLE_ANALYTICS_COM, | |
278 DOMAIN_GOOGLEPLEX_COM, | |
279 DOMAIN_YTIMG_COM, | |
280 DOMAIN_GOOGLEUSERCONTENT_COM, | |
281 DOMAIN_YOUTUBE_COM, | |
282 DOMAIN_GOOGLEAPIS_COM, | |
283 DOMAIN_GOOGLEADSERVICES_COM, | |
284 DOMAIN_GOOGLECODE_COM, | |
285 DOMAIN_APPSPOT_COM, | |
286 DOMAIN_GOOGLESYNDICATION_COM, | |
287 DOMAIN_DOUBLECLICK_NET, | |
288 DOMAIN_GSTATIC_COM, | |
289 DOMAIN_GMAIL_COM, | |
290 DOMAIN_GOOGLEMAIL_COM, | |
291 DOMAIN_GOOGLEGROUPS_COM, | |
292 | |
293 DOMAIN_TORPROJECT_ORG, | |
294 | |
295 DOMAIN_TWITTER_COM, | |
296 DOMAIN_TWIMG_COM, | |
297 | |
298 DOMAIN_AKAMAIHD_NET, | |
299 | |
300 DOMAIN_TOR2WEB_ORG, | |
301 | |
302 DOMAIN_YOUTU_BE, | |
303 DOMAIN_GOOGLECOMMERCE_COM, | |
304 DOMAIN_URCHIN_COM, | |
305 DOMAIN_GOO_GL, | |
306 DOMAIN_G_CO, | |
307 DOMAIN_GOOGLE_AC, | |
308 DOMAIN_GOOGLE_AD, | |
309 DOMAIN_GOOGLE_AE, | |
310 DOMAIN_GOOGLE_AF, | |
311 DOMAIN_GOOGLE_AG, | |
312 DOMAIN_GOOGLE_AM, | |
313 DOMAIN_GOOGLE_AS, | |
314 DOMAIN_GOOGLE_AT, | |
315 DOMAIN_GOOGLE_AZ, | |
316 DOMAIN_GOOGLE_BA, | |
317 DOMAIN_GOOGLE_BE, | |
318 DOMAIN_GOOGLE_BF, | |
319 DOMAIN_GOOGLE_BG, | |
320 DOMAIN_GOOGLE_BI, | |
321 DOMAIN_GOOGLE_BJ, | |
322 DOMAIN_GOOGLE_BS, | |
323 DOMAIN_GOOGLE_BY, | |
324 DOMAIN_GOOGLE_CA, | |
325 DOMAIN_GOOGLE_CAT, | |
326 DOMAIN_GOOGLE_CC, | |
327 DOMAIN_GOOGLE_CD, | |
328 DOMAIN_GOOGLE_CF, | |
329 DOMAIN_GOOGLE_CG, | |
330 DOMAIN_GOOGLE_CH, | |
331 DOMAIN_GOOGLE_CI, | |
332 DOMAIN_GOOGLE_CL, | |
333 DOMAIN_GOOGLE_CM, | |
334 DOMAIN_GOOGLE_CN, | |
335 DOMAIN_CO_AO, | |
336 DOMAIN_CO_BW, | |
337 DOMAIN_CO_CK, | |
338 DOMAIN_CO_CR, | |
339 DOMAIN_CO_HU, | |
340 DOMAIN_CO_ID, | |
341 DOMAIN_CO_IL, | |
342 DOMAIN_CO_IM, | |
343 DOMAIN_CO_IN, | |
344 DOMAIN_CO_JE, | |
345 DOMAIN_CO_JP, | |
346 DOMAIN_CO_KE, | |
347 DOMAIN_CO_KR, | |
348 DOMAIN_CO_LS, | |
349 DOMAIN_CO_MA, | |
350 DOMAIN_CO_MZ, | |
351 DOMAIN_CO_NZ, | |
352 DOMAIN_CO_TH, | |
353 DOMAIN_CO_TZ, | |
354 DOMAIN_CO_UG, | |
355 DOMAIN_CO_UK, | |
356 DOMAIN_CO_UZ, | |
357 DOMAIN_CO_VE, | |
358 DOMAIN_CO_VI, | |
359 DOMAIN_CO_ZA, | |
360 DOMAIN_CO_ZM, | |
361 DOMAIN_CO_ZW, | |
362 DOMAIN_COM_AF, | |
363 DOMAIN_COM_AG, | |
364 DOMAIN_COM_AI, | |
365 DOMAIN_COM_AR, | |
366 DOMAIN_COM_AU, | |
367 DOMAIN_COM_BD, | |
368 DOMAIN_COM_BH, | |
369 DOMAIN_COM_BN, | |
370 DOMAIN_COM_BO, | |
371 DOMAIN_COM_BR, | |
372 DOMAIN_COM_BY, | |
373 DOMAIN_COM_BZ, | |
374 DOMAIN_COM_CN, | |
375 DOMAIN_COM_CO, | |
376 DOMAIN_COM_CU, | |
377 DOMAIN_COM_CY, | |
378 DOMAIN_COM_DO, | |
379 DOMAIN_COM_EC, | |
380 DOMAIN_COM_EG, | |
381 DOMAIN_COM_ET, | |
382 DOMAIN_COM_FJ, | |
383 DOMAIN_COM_GE, | |
384 DOMAIN_COM_GH, | |
385 DOMAIN_COM_GI, | |
386 DOMAIN_COM_GR, | |
387 DOMAIN_COM_GT, | |
388 DOMAIN_COM_HK, | |
389 DOMAIN_COM_IQ, | |
390 DOMAIN_COM_JM, | |
391 DOMAIN_COM_JO, | |
392 DOMAIN_COM_KH, | |
393 DOMAIN_COM_KW, | |
394 DOMAIN_COM_LB, | |
395 DOMAIN_COM_LY, | |
396 DOMAIN_COM_MT, | |
397 DOMAIN_COM_MX, | |
398 DOMAIN_COM_MY, | |
399 DOMAIN_COM_NA, | |
400 DOMAIN_COM_NF, | |
401 DOMAIN_COM_NG, | |
402 DOMAIN_COM_NI, | |
403 DOMAIN_COM_NP, | |
404 DOMAIN_COM_NR, | |
405 DOMAIN_COM_OM, | |
406 DOMAIN_COM_PA, | |
407 DOMAIN_COM_PE, | |
408 DOMAIN_COM_PH, | |
409 DOMAIN_COM_PK, | |
410 DOMAIN_COM_PL, | |
411 DOMAIN_COM_PR, | |
412 DOMAIN_COM_PY, | |
413 DOMAIN_COM_QA, | |
414 DOMAIN_COM_RU, | |
415 DOMAIN_COM_SA, | |
416 DOMAIN_COM_SB, | |
417 DOMAIN_COM_SG, | |
418 DOMAIN_COM_SL, | |
419 DOMAIN_COM_SV, | |
420 DOMAIN_COM_TJ, | |
421 DOMAIN_COM_TN, | |
422 DOMAIN_COM_TR, | |
423 DOMAIN_COM_TW, | |
424 DOMAIN_COM_UA, | |
425 DOMAIN_COM_UY, | |
426 DOMAIN_COM_VC, | |
427 DOMAIN_COM_VE, | |
428 DOMAIN_COM_VN, | |
429 DOMAIN_GOOGLE_CV, | |
430 DOMAIN_GOOGLE_CZ, | |
431 DOMAIN_GOOGLE_DE, | |
432 DOMAIN_GOOGLE_DJ, | |
433 DOMAIN_GOOGLE_DK, | |
434 DOMAIN_GOOGLE_DM, | |
435 DOMAIN_GOOGLE_DZ, | |
436 DOMAIN_GOOGLE_EE, | |
437 DOMAIN_GOOGLE_ES, | |
438 DOMAIN_GOOGLE_FI, | |
439 DOMAIN_GOOGLE_FM, | |
440 DOMAIN_GOOGLE_FR, | |
441 DOMAIN_GOOGLE_GA, | |
442 DOMAIN_GOOGLE_GE, | |
443 DOMAIN_GOOGLE_GG, | |
444 DOMAIN_GOOGLE_GL, | |
445 DOMAIN_GOOGLE_GM, | |
446 DOMAIN_GOOGLE_GP, | |
447 DOMAIN_GOOGLE_GR, | |
448 DOMAIN_GOOGLE_GY, | |
449 DOMAIN_GOOGLE_HK, | |
450 DOMAIN_GOOGLE_HN, | |
451 DOMAIN_GOOGLE_HR, | |
452 DOMAIN_GOOGLE_HT, | |
453 DOMAIN_GOOGLE_HU, | |
454 DOMAIN_GOOGLE_IE, | |
455 DOMAIN_GOOGLE_IM, | |
456 DOMAIN_GOOGLE_INFO, | |
457 DOMAIN_GOOGLE_IQ, | |
458 DOMAIN_GOOGLE_IS, | |
459 DOMAIN_GOOGLE_IT, | |
460 DOMAIN_IT_AO, | |
461 DOMAIN_GOOGLE_JE, | |
462 DOMAIN_GOOGLE_JO, | |
463 DOMAIN_GOOGLE_JOBS, | |
464 DOMAIN_GOOGLE_JP, | |
465 DOMAIN_GOOGLE_KG, | |
466 DOMAIN_GOOGLE_KI, | |
467 DOMAIN_GOOGLE_KZ, | |
468 DOMAIN_GOOGLE_LA, | |
469 DOMAIN_GOOGLE_LI, | |
470 DOMAIN_GOOGLE_LK, | |
471 DOMAIN_GOOGLE_LT, | |
472 DOMAIN_GOOGLE_LU, | |
473 DOMAIN_GOOGLE_LV, | |
474 DOMAIN_GOOGLE_MD, | |
475 DOMAIN_GOOGLE_ME, | |
476 DOMAIN_GOOGLE_MG, | |
477 DOMAIN_GOOGLE_MK, | |
478 DOMAIN_GOOGLE_ML, | |
479 DOMAIN_GOOGLE_MN, | |
480 DOMAIN_GOOGLE_MS, | |
481 DOMAIN_GOOGLE_MU, | |
482 DOMAIN_GOOGLE_MV, | |
483 DOMAIN_GOOGLE_MW, | |
484 DOMAIN_GOOGLE_NE, | |
485 DOMAIN_NE_JP, | |
486 DOMAIN_GOOGLE_NET, | |
487 DOMAIN_GOOGLE_NL, | |
488 DOMAIN_GOOGLE_NO, | |
489 DOMAIN_GOOGLE_NR, | |
490 DOMAIN_GOOGLE_NU, | |
491 DOMAIN_OFF_AI, | |
492 DOMAIN_GOOGLE_PK, | |
493 DOMAIN_GOOGLE_PL, | |
494 DOMAIN_GOOGLE_PN, | |
495 DOMAIN_GOOGLE_PS, | |
496 DOMAIN_GOOGLE_PT, | |
497 DOMAIN_GOOGLE_RO, | |
498 DOMAIN_GOOGLE_RS, | |
499 DOMAIN_GOOGLE_RU, | |
500 DOMAIN_GOOGLE_RW, | |
501 DOMAIN_GOOGLE_SC, | |
502 DOMAIN_GOOGLE_SE, | |
503 DOMAIN_GOOGLE_SH, | |
504 DOMAIN_GOOGLE_SI, | |
505 DOMAIN_GOOGLE_SK, | |
506 DOMAIN_GOOGLE_SM, | |
507 DOMAIN_GOOGLE_SN, | |
508 DOMAIN_GOOGLE_SO, | |
509 DOMAIN_GOOGLE_ST, | |
510 DOMAIN_GOOGLE_TD, | |
511 DOMAIN_GOOGLE_TG, | |
512 DOMAIN_GOOGLE_TK, | |
513 DOMAIN_GOOGLE_TL, | |
514 DOMAIN_GOOGLE_TM, | |
515 DOMAIN_GOOGLE_TN, | |
516 DOMAIN_GOOGLE_TO, | |
517 DOMAIN_GOOGLE_TP, | |
518 DOMAIN_GOOGLE_TT, | |
519 DOMAIN_GOOGLE_US, | |
520 DOMAIN_GOOGLE_UZ, | |
521 DOMAIN_GOOGLE_VG, | |
522 DOMAIN_GOOGLE_VU, | |
523 DOMAIN_GOOGLE_WS, | |
524 | |
525 DOMAIN_CHROMIUM_ORG, | |
526 | |
527 DOMAIN_CRYPTO_CAT, | |
528 | |
529 // Boundary value for UMA_HISTOGRAM_ENUMERATION: | |
530 DOMAIN_NUM_EVENTS | |
531 }; | |
532 | |
533 // PublicKeyPins contains a number of SubjectPublicKeyInfo hashes for a site. | |
534 // The validated certificate chain for the site must not include any of | |
535 // |excluded_hashes| and must include one or more of |required_hashes|. | |
536 struct PublicKeyPins { | |
537 const char* const* required_hashes; | |
538 const char* const* excluded_hashes; | |
539 }; | |
540 | |
541 struct HSTSPreload { | |
542 uint8 length; | |
543 bool include_subdomains; | |
544 char dns_name[34]; | |
545 bool https_required; | |
546 PublicKeyPins pins; | |
547 SecondLevelDomainName second_level_domain_name; | |
548 }; | |
549 | |
550 static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries, | |
551 const std::string& canonicalized_host, size_t i, | |
552 TransportSecurityState::DomainState* out, bool* ret) { | |
553 for (size_t j = 0; j < num_entries; j++) { | |
554 if (entries[j].length == canonicalized_host.size() - i && | |
555 memcmp(entries[j].dns_name, &canonicalized_host[i], | |
556 entries[j].length) == 0) { | |
557 if (!entries[j].include_subdomains && i != 0) { | |
558 *ret = false; | |
559 } else { | |
560 out->include_subdomains = entries[j].include_subdomains; | |
561 *ret = true; | |
562 if (!entries[j].https_required) | |
563 out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; | |
564 if (entries[j].pins.required_hashes) { | |
565 const char* const* sha1_hash = entries[j].pins.required_hashes; | |
566 while (*sha1_hash) { | |
567 AddHash(*sha1_hash, &out->static_spki_hashes); | |
568 sha1_hash++; | |
569 } | 313 } |
570 } | 314 if (entry.pins.excluded_hashes) { |
571 if (entries[j].pins.excluded_hashes) { | 315 const char* const* sha1_hashes = entry.pins.excluded_hashes; |
572 const char* const* sha1_hash = entries[j].pins.excluded_hashes; | 316 while (*sha1_hashes) { |
573 while (*sha1_hash) { | 317 memcpy(hash.data(), *sha1_hashes, hash.size()); |
574 AddHash(*sha1_hash, &out->bad_static_spki_hashes); | 318 result->public_key_pins_bad_hashes_.push_back(hash); |
575 sha1_hash++; | 319 sha1_hashes++; |
| 320 } |
576 } | 321 } |
| 322 |
| 323 if (entry.pins.required_hashes == kGoogleAcceptableCerts) |
| 324 result->is_google_pinned_property_ = true; |
| 325 |
| 326 if (entry.second_level_domain_name != DOMAIN_NOT_PINNED) { |
| 327 result->report_uma_on_pin_failure_ = true; |
| 328 result->second_level_domain_name_ = entry.second_level_domain_name; |
| 329 } |
| 330 return true; |
577 } | 331 } |
578 } | 332 } |
579 return true; | 333 } |
580 } | 334 } |
581 } | 335 #endif |
582 return false; | 336 return false; |
583 } | 337 } |
584 | 338 |
585 #include "net/base/transport_security_state_static.h" | 339 void TransportSecurityState::AddHSTSHeader(const std::string& host, |
586 | |
587 // Returns the HSTSPreload entry for the |canonicalized_host| in |entries|, | |
588 // or NULL if there is none. Prefers exact hostname matches to those that | |
589 // match only because HSTSPreload.include_subdomains is true. | |
590 // | |
591 // |canonicalized_host| should be the hostname as canonicalized by | |
592 // CanonicalizeHost. | |
593 static const struct HSTSPreload* GetHSTSPreload( | |
594 const std::string& canonicalized_host, | |
595 const struct HSTSPreload* entries, | |
596 size_t num_entries) { | |
597 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | |
598 for (size_t j = 0; j < num_entries; j++) { | |
599 const struct HSTSPreload* entry = entries + j; | |
600 | |
601 if (i != 0 && !entry->include_subdomains) | |
602 continue; | |
603 | |
604 if (entry->length == canonicalized_host.size() - i && | |
605 memcmp(entry->dns_name, &canonicalized_host[i], entry->length) == 0) { | |
606 return entry; | |
607 } | |
608 } | |
609 } | |
610 | |
611 return NULL; | |
612 } | |
613 | |
614 bool TransportSecurityState::AddHSTSHeader(const std::string& host, | |
615 const std::string& value) { | 340 const std::string& value) { |
616 base::Time now = base::Time::Now(); | 341 const base::Time now = base::Time::Now(); |
617 TransportSecurityState::DomainState domain_state; | 342 base::TimeDelta max_age; |
618 if (ParseHSTSHeader(now, value, &domain_state.upgrade_expiry, | 343 bool include_subdomains = false; |
619 &domain_state.include_subdomains)) { | 344 if (ParseHSTSHeader(value, &max_age, &include_subdomains)) { |
620 // Handle max-age == 0 | 345 if (max_age.InSeconds() == 0) |
621 if (now == domain_state.upgrade_expiry) | 346 DeleteHSTS(host); |
622 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | |
623 else | 347 else |
624 domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS; | 348 AddHSTS(host, now, now + max_age, include_subdomains); |
625 domain_state.created = now; | 349 } |
626 EnableHost(host, domain_state); | 350 } |
627 return true; | 351 |
628 } | 352 void TransportSecurityState::AddHPKPHeader(const std::string& host, |
629 return false; | |
630 } | |
631 | |
632 bool TransportSecurityState::AddHPKPHeader(const std::string& host, | |
633 const std::string& value, | 353 const std::string& value, |
634 const SSLInfo& ssl_info) { | 354 const SSLInfo& ssl_info) { |
635 base::Time now = base::Time::Now(); | 355 const base::Time now = base::Time::Now(); |
636 TransportSecurityState::DomainState domain_state; | 356 base::TimeDelta max_age; |
637 if (ParseHPKPHeader(now, value, ssl_info.public_key_hashes, | 357 HashValueVector public_key_pin_hashes; |
638 &domain_state.dynamic_spki_hashes_expiry, | 358 bool include_subdomains = false; // TODO(trevp) PARSE FROM HEADER |
639 &domain_state.dynamic_spki_hashes)) { | 359 if (ParseHPKPHeader(value, ssl_info.public_key_hashes, &max_age, |
640 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | 360 &public_key_pin_hashes)) { |
641 domain_state.created = now; | 361 if (max_age.InSeconds() == 0) { |
642 EnableHost(host, domain_state); | 362 DeleteHPKP(host); |
643 return true; | 363 } else { |
644 } | 364 AddHPKP(host, now, now + max_age, include_subdomains, |
645 return false; | 365 public_key_pin_hashes); |
| 366 } |
| 367 } |
646 } | 368 } |
647 | 369 |
648 bool TransportSecurityState::AddHSTS(const std::string& host, | 370 bool TransportSecurityState::AddHSTS(const std::string& host, |
| 371 const base::Time& created, |
649 const base::Time& expiry, | 372 const base::Time& expiry, |
650 bool include_subdomains) { | 373 bool include_subdomains) { |
651 // Copy-and-modify the existing DomainState for this host (if any). | 374 return AddHSTSHashedHost(HashHost(host), created, expiry, |
652 TransportSecurityState::DomainState domain_state; | 375 include_subdomains); |
653 const std::string canonicalized_host = CanonicalizeHost(host); | |
654 const std::string hashed_host = HashHost(canonicalized_host); | |
655 DomainStateMap::const_iterator i = enabled_hosts_.find( | |
656 hashed_host); | |
657 if (i != enabled_hosts_.end()) | |
658 domain_state = i->second; | |
659 | |
660 domain_state.created = base::Time::Now(); | |
661 domain_state.include_subdomains = include_subdomains; | |
662 domain_state.upgrade_expiry = expiry; | |
663 domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS; | |
664 EnableHost(host, domain_state); | |
665 return true; | |
666 } | 376 } |
667 | 377 |
668 bool TransportSecurityState::AddHPKP(const std::string& host, | 378 bool TransportSecurityState::AddHPKP(const std::string& host, |
| 379 const base::Time& created, |
669 const base::Time& expiry, | 380 const base::Time& expiry, |
670 bool include_subdomains, | 381 bool include_subdomains, |
671 const HashValueVector& hashes) { | 382 const HashValueVector& hashes) { |
672 // Copy-and-modify the existing DomainState for this host (if any). | 383 return AddHPKPHashedHost(HashHost(host), created, expiry, |
673 TransportSecurityState::DomainState domain_state; | 384 include_subdomains, hashes); |
674 const std::string canonicalized_host = CanonicalizeHost(host); | 385 } |
675 const std::string hashed_host = HashHost(canonicalized_host); | 386 |
676 DomainStateMap::const_iterator i = enabled_hosts_.find( | 387 bool TransportSecurityState::AddHSTSHashedHost(const std::string& hashed_host, |
677 hashed_host); | 388 const base::Time& created, |
678 if (i != enabled_hosts_.end()) | 389 const base::Time& expiry, |
679 domain_state = i->second; | 390 bool include_subdomains) { |
680 | 391 |
681 domain_state.created = base::Time::Now(); | 392 HSTSEntry entry(include_subdomains, created, expiry); |
682 domain_state.include_subdomains = include_subdomains; | 393 if (AddDynamicEntry(hsts_entries_, hashed_host, entry)) { |
683 domain_state.dynamic_spki_hashes_expiry = expiry; | 394 StateIsDirty(); |
684 domain_state.dynamic_spki_hashes = hashes; | 395 return true; |
685 EnableHost(host, domain_state); | 396 } |
686 return true; | 397 return false; |
687 } | 398 } |
688 | 399 |
689 // static | 400 bool TransportSecurityState::AddHPKPHashedHost(const std::string& hashed_host, |
690 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, | 401 const base::Time& created, |
691 bool sni_enabled) { | 402 const base::Time& expiry, |
692 std::string canonicalized_host = CanonicalizeHost(host); | 403 bool include_subdomains, |
693 const struct HSTSPreload* entry = | 404 const HashValueVector& hashes) { |
694 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 405 if (hashes.empty()) |
695 | 406 return false; |
696 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 407 HPKPEntry entry(include_subdomains, created, expiry, hashes); |
697 return true; | 408 if (AddDynamicEntry(hpkp_entries_, hashed_host, entry)) { |
698 | 409 StateIsDirty(); |
699 if (sni_enabled) { | 410 return true; |
700 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 411 } |
701 kNumPreloadedSNISTS); | 412 return false; |
702 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 413 } |
703 return true; | 414 |
704 } | 415 bool TransportSecurityState::DeleteHSTS(const std::string& host) { |
705 | 416 if (DeleteDynamicEntry(hsts_entries_, HashHost(host))) { |
706 return false; | 417 StateIsDirty(); |
707 } | 418 return true; |
708 | 419 } |
709 // static | 420 return false; |
710 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { | 421 } |
711 std::string canonicalized_host = CanonicalizeHost(host); | 422 |
712 | 423 bool TransportSecurityState::DeleteHPKP(const std::string& host) { |
713 const struct HSTSPreload* entry = | 424 if (DeleteDynamicEntry(hpkp_entries_, HashHost(host))) { |
714 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 425 StateIsDirty(); |
715 | 426 return true; |
716 if (!entry) { | 427 } |
717 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 428 return false; |
718 kNumPreloadedSNISTS); | 429 } |
719 } | 430 |
720 | 431 const std::map<std::string, TransportSecurityState::HSTSEntry>& |
721 if (!entry) { | 432 TransportSecurityState::GetHSTSEntries() const { |
722 // We don't care to report pin failures for dynamic pins. | 433 return hsts_entries_; |
723 return; | 434 } |
724 } | 435 |
725 | 436 const std::map<std::string, TransportSecurityState::HPKPEntry>& |
726 DCHECK(entry); | 437 TransportSecurityState::GetHPKPEntries() const { |
727 DCHECK(entry->pins.required_hashes); | 438 return hpkp_entries_; |
728 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | 439 } |
729 | 440 |
730 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | 441 void TransportSecurityState::StateIsDirty() { |
731 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | 442 if (delegate_) |
732 } | 443 delegate_->StateIsDirty(this); |
733 | 444 } |
734 // static | 445 |
735 bool TransportSecurityState::IsBuildTimely() { | 446 bool TransportSecurityState::IsBuildTimely() { |
736 const base::Time build_time = base::GetBuildTime(); | 447 const base::Time build_time = base::GetBuildTime(); |
737 // We consider built-in information to be timely for 10 weeks. | 448 // We consider built-in information to be timely for 10 weeks. |
738 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; | 449 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; |
739 } | 450 } |
740 | 451 |
741 bool TransportSecurityState::GetStaticDomainState( | 452 // HSTSEntry and HPKPEntry |
742 const std::string& canonicalized_host, | 453 |
743 bool sni_enabled, | 454 TransportSecurityState::HSTSEntry::HSTSEntry() |
744 DomainState* out) { | 455 : include_subdomains_(false), |
745 DCHECK(CalledOnValidThread()); | 456 created_(), |
746 | 457 expiry_() { |
747 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; | 458 } |
748 out->include_subdomains = false; | 459 |
749 | 460 TransportSecurityState::HSTSEntry::~HSTSEntry() { |
750 const bool is_build_timely = IsBuildTimely(); | 461 } |
751 | 462 |
752 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 463 TransportSecurityState::HSTSEntry::HSTSEntry(bool include_subdomains, |
753 std::string host_sub_chunk(&canonicalized_host[i], | 464 const base::Time& created, |
754 canonicalized_host.size() - i); | 465 const base::Time& expiry) |
755 out->domain = DNSDomainToString(host_sub_chunk); | 466 : include_subdomains_(include_subdomains), |
756 std::string hashed_host(HashHost(host_sub_chunk)); | 467 created_(created), |
757 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { | 468 expiry_(expiry) { |
758 *out = forced_hosts_[hashed_host]; | 469 } |
759 out->domain = DNSDomainToString(host_sub_chunk); | 470 |
760 return true; | 471 TransportSecurityState::HPKPEntry::HPKPEntry() |
761 } | 472 : include_subdomains_(false), |
762 bool ret; | 473 created_(), |
763 if (is_build_timely && | 474 expiry_(), |
764 HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, | 475 good_hashes_() { |
765 &ret)) { | 476 } |
766 return ret; | 477 |
767 } | 478 TransportSecurityState::HPKPEntry::~HPKPEntry() { |
768 if (sni_enabled && | 479 } |
769 is_build_timely && | 480 |
770 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, | 481 TransportSecurityState::HPKPEntry::HPKPEntry( |
771 out, &ret)) { | 482 bool include_subdomains, |
772 return ret; | 483 const base::Time& created, |
773 } | 484 const base::Time& expiry, |
774 } | 485 const HashValueVector& good_hashes) |
775 | 486 : include_subdomains_(include_subdomains), |
776 return false; | 487 created_(created), |
777 } | 488 expiry_(expiry), |
778 | 489 good_hashes_(good_hashes) { |
779 void TransportSecurityState::AddOrUpdateEnabledHosts( | 490 } |
780 const std::string& hashed_host, const DomainState& state) { | 491 |
781 enabled_hosts_[hashed_host] = state; | 492 // DomainState |
782 } | |
783 | |
784 void TransportSecurityState::AddOrUpdateForcedHosts( | |
785 const std::string& hashed_host, const DomainState& state) { | |
786 forced_hosts_[hashed_host] = state; | |
787 } | |
788 | 493 |
789 TransportSecurityState::DomainState::DomainState() | 494 TransportSecurityState::DomainState::DomainState() |
790 : upgrade_mode(MODE_FORCE_HTTPS), | 495 : should_upgrade_(false), |
791 created(base::Time::Now()), | 496 has_public_key_pins_(false), |
792 include_subdomains(false) { | 497 is_google_pinned_property_(false), |
| 498 report_uma_on_pin_failure_(false), |
| 499 public_key_pins_good_hashes_(), |
| 500 public_key_pins_bad_hashes_(), |
| 501 second_level_domain_name_(DOMAIN_NOT_PINNED) { |
793 } | 502 } |
794 | 503 |
795 TransportSecurityState::DomainState::~DomainState() { | 504 TransportSecurityState::DomainState::~DomainState() { |
796 } | 505 } |
| 506 bool TransportSecurityState::DomainState::HasPublicKeyPins() const { |
| 507 return has_public_key_pins_; |
| 508 } |
797 | 509 |
798 bool TransportSecurityState::DomainState::CheckPublicKeyPins( | 510 bool TransportSecurityState::DomainState::CheckPublicKeyPins( |
799 const HashValueVector& hashes) const { | 511 const HashValueVector& hashes) const { |
800 // Validate that hashes is not empty. By the time this code is called (in | 512 if (HashesIntersect(public_key_pins_bad_hashes_, hashes)) { |
801 // production), that should never happen, but it's good to be defensive. | 513 LOG(ERROR) << "Rejecting public key chain. Validated chain: " |
802 // And, hashes *can* be empty in some test scenarios. | 514 << HashesToBase64String(hashes) |
803 if (hashes.empty()) { | 515 << ", matches one or more bad hashes: " |
804 LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned " | 516 << HashesToBase64String(public_key_pins_bad_hashes_); |
805 "domain " << domain; | |
806 return false; | 517 return false; |
807 } | 518 } |
808 | 519 |
809 if (HashesIntersect(bad_static_spki_hashes, hashes)) { | 520 // If there are no good pins, then any valid chain is acceptable. |
810 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 521 // Otherwise, there has to be a match. |
811 << ". Validated chain: " << HashesToBase64String(hashes) | 522 if (public_key_pins_good_hashes_.empty() || |
812 << ", matches one or more bad hashes: " | 523 HashesIntersect(public_key_pins_good_hashes_, hashes)) { |
813 << HashesToBase64String(bad_static_spki_hashes); | 524 return true; |
814 return false; | 525 } |
815 } | 526 |
816 | 527 LOG(ERROR) << "Rejecting public key chain. Validated chain: " |
817 // If there are no pins, then any valid chain is acceptable. | 528 << HashesToBase64String(hashes) |
818 if (dynamic_spki_hashes.empty() && static_spki_hashes.empty()) | 529 << ", expected: " |
819 return true; | 530 << HashesToBase64String(public_key_pins_good_hashes_); |
820 | |
821 if (HashesIntersect(dynamic_spki_hashes, hashes) || | |
822 HashesIntersect(static_spki_hashes, hashes)) { | |
823 return true; | |
824 } | |
825 | |
826 LOG(ERROR) << "Rejecting public key chain for domain " << domain | |
827 << ". Validated chain: " << HashesToBase64String(hashes) | |
828 << ", expected: " << HashesToBase64String(dynamic_spki_hashes) | |
829 << " or: " << HashesToBase64String(static_spki_hashes); | |
830 return false; | 531 return false; |
831 } | 532 } |
832 | 533 |
833 bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const { | 534 bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const { |
834 return upgrade_mode == MODE_FORCE_HTTPS; | 535 return should_upgrade_; |
835 } | 536 } |
836 | 537 |
837 bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const { | 538 bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const { |
838 return true; | 539 return should_upgrade_; |
839 } | 540 } |
840 | 541 |
841 bool TransportSecurityState::DomainState::Equals( | 542 bool TransportSecurityState::DomainState::IsGooglePinnedProperty() const { |
842 const DomainState& other) const { | 543 return is_google_pinned_property_; |
843 // TODO(palmer): Implement this | 544 } |
844 (void) other; | 545 |
845 return true; | 546 void TransportSecurityState::DomainState::ReportUMAOnPinFailure() const { |
846 } | 547 if (report_uma_on_pin_failure_) { |
847 | 548 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", |
848 bool TransportSecurityState::DomainState::HasPublicKeyPins() const { | 549 second_level_domain_name_, DOMAIN_NUM_EVENTS); |
849 return static_spki_hashes.size() > 0 || | 550 } |
850 bad_static_spki_hashes.size() > 0 || | 551 } |
851 dynamic_spki_hashes.size() > 0; | 552 |
| 553 const HashValueVector& |
| 554 TransportSecurityState::DomainState::GetPublicKeyPinsGoodHashes() const { |
| 555 return public_key_pins_good_hashes_; |
| 556 } |
| 557 |
| 558 const HashValueVector& |
| 559 TransportSecurityState::DomainState::GetPublicKeyPinsBadHashes() const { |
| 560 return public_key_pins_bad_hashes_; |
852 } | 561 } |
853 | 562 |
854 } // namespace | 563 } // namespace |
OLD | NEW |