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) { | |
58 char hashed[crypto::kSHA256Length]; | |
59 crypto::SHA256HashString(canonicalized_host, hashed, sizeof(hashed)); | |
60 return std::string(hashed, sizeof(hashed)); | |
61 } | |
62 | |
63 // Returns true if the intersection of |a| and |b| is not empty. If either | 54 // Returns true if the intersection of |a| and |b| is not empty. If either |
64 // |a| or |b| is empty, returns false. | 55 // |a| or |b| is empty, returns false. |
65 bool HashesIntersect(const HashValueVector& a, | 56 bool HashesIntersect(const HashValueVector& a, |
66 const HashValueVector& b) { | 57 const HashValueVector& b) { |
67 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { | 58 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { |
68 HashValueVector::const_iterator j = | 59 HashValueVector::const_iterator j = |
69 std::find_if(b.begin(), b.end(), HashValuesEqual(*i)); | 60 std::find_if(b.begin(), b.end(), HashValuesEqual(*i)); |
70 if (j != b.end()) | 61 if (j != b.end()) |
71 return true; | 62 return true; |
72 } | 63 } |
73 return false; | 64 return false; |
74 } | 65 } |
75 | 66 |
76 bool AddHash(const char* sha1_hash, | 67 std::string HashHost(const std::string& host) { |
77 HashValueVector* out) { | 68 std::string lowercase = StringToLowerASCII(host); |
78 HashValue hash(HASH_VALUE_SHA1); | 69 std::string old_style_canonicalized_name; |
79 memcpy(hash.data(), sha1_hash, hash.size()); | 70 if (!DNSDomainFromDot(lowercase, &old_style_canonicalized_name)) |
80 out->push_back(hash); | 71 return std::string(""); |
Ryan Sleevi
2013/03/23 04:24:13
return std::string()
The "" technically incurs ov
unsafe
2013/03/23 08:19:39
Done.
| |
81 return true; | 72 |
82 } | 73 char hashed[crypto::kSHA256Length]; |
74 crypto::SHA256HashString(old_style_canonicalized_name, hashed, | |
75 sizeof(hashed)); | |
76 return std::string(hashed, sizeof(hashed)); | |
77 } | |
78 | |
79 // Iterate over ("www.example.com", "example.com", "com") | |
80 struct DomainNameIterator { | |
81 explicit DomainNameIterator(const std::string& host) { | |
82 name_ = StringToLowerASCII(host); | |
83 index_ = 0; | |
84 } | |
85 | |
86 bool AtEnd() { | |
87 return name_[index_] == 0; | |
88 } | |
89 | |
90 // Advance to NUL char, or after the next '.' | |
91 void Advance() { | |
92 if (AtEnd()) | |
93 return; | |
94 for (index_++; name_[index_] != '.' && name_[index_] != 0; index_++); | |
95 if (name_[index_] == '.') | |
96 index_++; | |
97 } | |
98 | |
99 std::string GetName() { | |
100 return name_.substr(index_); | |
101 } | |
102 | |
103 bool IsFullHostname() { | |
104 return index_ == 0; | |
105 } | |
106 | |
107 std::string name_; // The full hostname, canonicalized to lowercase | |
108 size_t index_; // Index into name_ | |
109 }; | |
83 | 110 |
84 } // namespace | 111 } // namespace |
85 | 112 |
86 TransportSecurityState::TransportSecurityState() | 113 TransportSecurityState::TransportSecurityState() |
87 : delegate_(NULL) { | 114 : delegate_(NULL) { |
88 } | 115 } |
89 | 116 |
90 TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state) | 117 TransportSecurityState::~TransportSecurityState() {} |
91 : iterator_(state.enabled_hosts_.begin()), | |
92 end_(state.enabled_hosts_.end()) { | |
93 } | |
94 | |
95 TransportSecurityState::Iterator::~Iterator() {} | |
96 | 118 |
97 void TransportSecurityState::SetDelegate( | 119 void TransportSecurityState::SetDelegate( |
98 TransportSecurityState::Delegate* delegate) { | 120 TransportSecurityState::Delegate* delegate) { |
99 delegate_ = delegate; | 121 delegate_ = delegate; |
100 } | 122 } |
101 | 123 |
102 void TransportSecurityState::EnableHost(const std::string& host, | 124 void TransportSecurityState::ClearDynamicData() { |
103 const DomainState& state) { | 125 hsts_entries_.clear(); |
126 hpkp_entries_.clear(); | |
127 } | |
128 | |
129 void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) { | |
104 DCHECK(CalledOnValidThread()); | 130 DCHECK(CalledOnValidThread()); |
105 | 131 hsts_entries_.DeleteEntriesSince(time); |
106 const std::string canonicalized_host = CanonicalizeHost(host); | 132 hpkp_entries_.DeleteEntriesSince(time); |
107 if (canonicalized_host.empty()) | 133 CheckDirty(); |
108 return; | |
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 } | 134 } |
128 | 135 |
129 bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) { | 136 bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) { |
130 DCHECK(CalledOnValidThread()); | 137 DCHECK(CalledOnValidThread()); |
131 | 138 bool deleted_hsts = DeleteHSTS(host); |
132 const std::string canonicalized_host = CanonicalizeHost(host); | 139 bool deleted_hpkp = DeleteHPKP(host); |
133 if (canonicalized_host.empty()) | 140 CheckDirty(); |
134 return false; | 141 return deleted_hsts || deleted_hpkp; |
135 | |
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 } | 142 } |
145 | 143 |
146 bool TransportSecurityState::GetDomainState(const std::string& host, | 144 bool TransportSecurityState::GetDomainState(const std::string& host, |
147 bool sni_enabled, | 145 bool sni_enabled, |
148 DomainState* result) { | 146 DomainState* result) const { |
149 DCHECK(CalledOnValidThread()); | 147 DCHECK(CalledOnValidThread()); |
150 | 148 bool found = false; |
151 DomainState state; | 149 const base::Time now = base::Time::Now(); |
152 const std::string canonicalized_host = CanonicalizeHost(host); | 150 DomainState dynamic_state; |
153 if (canonicalized_host.empty()) | 151 |
152 found = GetPreloadDomainState(sni_enabled, now, host, result); | |
153 found = GetDynamicDomainState(now, host, &dynamic_state) || found; | |
Ryan Sleevi
2013/03/23 04:24:13
Why not simply
found |= GetDynamicDomainState
Be
unsafe
2013/03/23 08:19:39
its sort of a logical operation, bit arithmetic lo
| |
154 | |
155 // Merge dynamic state into preload state | |
156 // Currently, HSTS and HPKP are set if either state has them set. | |
157 // However, if both states have HPKP set, the preload pins take precedence. | |
158 // This behavior may change (e.g. for the most-recent to take priority). | |
159 if (!result->should_upgrade_ && dynamic_state.should_upgrade_) { | |
160 result->should_upgrade_ = true; | |
161 } | |
Ryan Sleevi
2013/03/23 04:24:13
style: omit braces for one-liner
unsafe
2013/03/23 08:19:39
Done.
| |
162 if (!result->has_public_key_pins_ && dynamic_state.has_public_key_pins_) { | |
163 result->has_public_key_pins_ = true; | |
164 result->public_key_pins_good_hashes_ = | |
165 dynamic_state.public_key_pins_good_hashes_; | |
Ryan Sleevi
2013/03/23 04:24:13
style: four spaces indent for continuation
unsafe
2013/03/23 08:19:39
Done.
| |
166 } | |
167 return found; | |
168 } | |
169 | |
170 bool TransportSecurityState::GetDynamicDomainState(const base::Time& now, | |
171 const std::string& host, | |
172 DomainState* result) const { | |
173 DynamicEntry hsts_entry; | |
174 HPKPEntry hpkp_entry; | |
175 // Iterate over 'www.example.com", 'example.com", "com" | |
176 for (DomainNameIterator iter(host); !iter.AtEnd(); iter.Advance()) { | |
177 std::string hashed_host = HashHost(iter.GetName()); | |
178 bool is_full_hostname = iter.IsFullHostname(); | |
179 | |
180 // Get HSTS data from map | |
181 if (!result->should_upgrade_ && | |
182 hsts_entries_.GetEntry(now, hashed_host, is_full_hostname, | |
183 &hsts_entry)) { | |
184 result->should_upgrade_ = true; | |
185 } | |
186 | |
187 // Get HPKP data from map | |
188 if (!result->has_public_key_pins_ && | |
189 hpkp_entries_.GetEntry(now, hashed_host, is_full_hostname, | |
190 &hpkp_entry)) { | |
191 result->has_public_key_pins_ = true; | |
192 result->public_key_pins_good_hashes_ = hpkp_entry.good_hashes_; | |
193 } | |
194 | |
195 // If we've got all possible data, exit early | |
196 if (result->should_upgrade_ && result->has_public_key_pins_) | |
197 return true; | |
198 } | |
199 return result->should_upgrade_ || result->has_public_key_pins_; | |
200 } | |
201 | |
202 bool TransportSecurityState::GetPreloadDomainState(bool sni_enabled, | |
203 const base::Time& now, | |
204 const std::string& host, | |
205 DomainState* result) const { | |
206 #ifdef PRELOADS_PRESENT | |
Ryan Sleevi
2013/03/23 04:24:13
pedantry nit: #if defined() for non-header-guard i
unsafe
2013/03/23 08:19:39
Done.
| |
207 const PreloadEntry* entries = kPreloadedEntries; | |
208 size_t num_entries = kNumPreloaded; | |
209 | |
210 if (!IsBuildTimely()) | |
154 return false; | 211 return false; |
155 | 212 |
156 bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled, | 213 for (int count = 0; count < 2; count++) { |
157 &state); | 214 // If sni_enabled, then scan through SNI entries (if necessary) |
158 std::string canonicalized_preload = CanonicalizeHost(state.domain); | 215 if (count == 1) { |
159 | 216 if (!sni_enabled) |
160 base::Time current_time(base::Time::Now()); | 217 break; |
161 | 218 entries = kPreloadedEntriesSNI; |
162 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 219 num_entries = kNumPreloadedSNI; |
163 std::string host_sub_chunk(&canonicalized_host[i], | |
164 canonicalized_host.size() - i); | |
165 // Exact match of a preload always wins. | |
166 if (has_preload && host_sub_chunk == canonicalized_preload) { | |
167 *result = state; | |
168 return true; | |
169 } | 220 } |
170 | 221 |
171 DomainStateMap::iterator j = | 222 for (DomainNameIterator iter(host); !iter.AtEnd(); iter.Advance()) { |
172 enabled_hosts_.find(HashHost(host_sub_chunk)); | 223 std::string name = iter.GetName(); |
173 if (j == enabled_hosts_.end()) | 224 for (size_t index = 0; index < num_entries; index++) { |
174 continue; | 225 const PreloadEntry& entry = entries[index]; |
175 | 226 |
176 if (current_time > j->second.upgrade_expiry && | 227 // If we find a relevant preload entry, populate the |
177 current_time > j->second.dynamic_spki_hashes_expiry) { | 228 // entire DomainState from it and return |
178 enabled_hosts_.erase(j); | 229 if (entry.length == name.size() && |
179 DirtyNotify(); | 230 memcmp(entry.dns_name, name.data(), entry.length) == 0 && |
180 continue; | 231 (iter.IsFullHostname() || entry.include_subdomains)) { |
181 } | 232 |
Ryan Sleevi
2013/03/23 04:24:13
nit: unnecessary newline
unsafe
2013/03/23 08:19:39
Done.
| |
182 | 233 // should_upgrade |
Ryan Sleevi
2013/03/23 04:24:13
the comment here and on 237 seem unnecessary.
unsafe
2013/03/23 08:19:39
Done.
| |
183 state = j->second; | 234 if (entry.https_required) |
184 state.domain = DNSDomainToString(host_sub_chunk); | 235 result->should_upgrade_ = true; |
185 | 236 |
186 // Succeed if we matched the domain exactly or if subdomain matches are | 237 // has_public_key_pins |
187 // allowed. | 238 if (entry.pins.required_hashes || entry.pins.excluded_hashes) |
188 if (i == 0 || j->second.include_subdomains) { | 239 result->has_public_key_pins_ = true; |
189 *result = state; | 240 |
190 return true; | 241 HashValue hash(HASH_VALUE_SHA1); |
191 } | 242 if (entry.pins.required_hashes) { |
192 | 243 const char* const* sha1_hashes = entry.pins.required_hashes; |
193 return false; | 244 while (*sha1_hashes) { |
194 } | 245 memcpy(hash.data(), *sha1_hashes, hash.size()); |
195 | 246 result->public_key_pins_good_hashes_.push_back(hash); |
196 return false; | 247 sha1_hashes++; |
197 } | 248 } |
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 } | 249 } |
570 } | 250 if (entry.pins.excluded_hashes) { |
571 if (entries[j].pins.excluded_hashes) { | 251 const char* const* sha1_hashes = entry.pins.excluded_hashes; |
572 const char* const* sha1_hash = entries[j].pins.excluded_hashes; | 252 while (*sha1_hashes) { |
573 while (*sha1_hash) { | 253 memcpy(hash.data(), *sha1_hashes, hash.size()); |
574 AddHash(*sha1_hash, &out->bad_static_spki_hashes); | 254 result->public_key_pins_bad_hashes_.push_back(hash); |
575 sha1_hash++; | 255 sha1_hashes++; |
256 } | |
576 } | 257 } |
258 | |
259 // is_google_pinned_property | |
260 if (entry.pins.required_hashes == kGoogleAcceptableCerts) | |
261 result->is_google_pinned_property_ = true; | |
262 | |
263 // report_uma_on_pin_failure | |
264 if (entry.second_level_domain_name != DOMAIN_NOT_PINNED) { | |
265 result->report_uma_on_pin_failure_ = true; | |
266 result->second_level_domain_name_ = entry.second_level_domain_name; | |
267 } | |
268 return true; | |
577 } | 269 } |
578 } | 270 } |
579 return true; | |
580 } | 271 } |
581 } | 272 } |
273 #endif | |
582 return false; | 274 return false; |
583 } | 275 } |
584 | 276 |
585 #include "net/base/transport_security_state_static.h" | 277 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) { | 278 const std::string& value) { |
616 base::Time now = base::Time::Now(); | 279 const base::Time now = base::Time::Now(); |
617 TransportSecurityState::DomainState domain_state; | 280 base::TimeDelta max_age; |
618 if (ParseHSTSHeader(now, value, &domain_state.upgrade_expiry, | 281 bool include_subdomains = false; |
619 &domain_state.include_subdomains)) { | 282 if (ParseHSTSHeader(value, &max_age, &include_subdomains)) { |
620 // Handle max-age == 0 | 283 if (max_age.InSeconds() == 0) |
621 if (now == domain_state.upgrade_expiry) | 284 DeleteHSTS(host); |
622 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | |
623 else | 285 else |
624 domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS; | 286 AddHSTS(host, now, now + max_age, include_subdomains); |
625 domain_state.created = now; | 287 } |
626 EnableHost(host, domain_state); | 288 } |
627 return true; | 289 |
628 } | 290 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, | 291 const std::string& value, |
634 const SSLInfo& ssl_info) { | 292 const SSLInfo& ssl_info) { |
635 base::Time now = base::Time::Now(); | 293 const base::Time now = base::Time::Now(); |
636 TransportSecurityState::DomainState domain_state; | 294 base::TimeDelta max_age; |
637 if (ParseHPKPHeader(now, value, ssl_info.public_key_hashes, | 295 HashValueVector public_key_pin_hashes; |
638 &domain_state.dynamic_spki_hashes_expiry, | 296 bool include_subdomains = false; // TODO(trevp) PARSE FROM HEADER |
639 &domain_state.dynamic_spki_hashes)) { | 297 if (ParseHPKPHeader(value, ssl_info.public_key_hashes, &max_age, |
640 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | 298 &public_key_pin_hashes)) { |
641 domain_state.created = now; | 299 if (max_age.InSeconds() == 0) |
642 EnableHost(host, domain_state); | 300 DeleteHPKP(host); |
643 return true; | 301 else |
644 } | 302 AddHPKP(host, now, now + max_age, include_subdomains, |
645 return false; | 303 public_key_pin_hashes); |
Ryan Sleevi
2013/03/23 04:24:13
style nit: because this line wraps, you should hav
unsafe
2013/03/23 08:19:39
Done.
| |
304 } | |
646 } | 305 } |
647 | 306 |
648 bool TransportSecurityState::AddHSTS(const std::string& host, | 307 bool TransportSecurityState::AddHSTS(const std::string& host, |
308 const base::Time& created, | |
649 const base::Time& expiry, | 309 const base::Time& expiry, |
650 bool include_subdomains) { | 310 bool include_subdomains) { |
651 // Copy-and-modify the existing DomainState for this host (if any). | 311 bool retval = AddHSTSHashedHost(HashHost(host), created, expiry, |
652 TransportSecurityState::DomainState domain_state; | 312 include_subdomains); |
Ryan Sleevi
2013/03/23 04:24:13
style nit: indent
style: just return (no need for
unsafe
2013/03/23 08:19:39
Done.
| |
653 const std::string canonicalized_host = CanonicalizeHost(host); | 313 return retval; |
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 } | 314 } |
667 | 315 |
668 bool TransportSecurityState::AddHPKP(const std::string& host, | 316 bool TransportSecurityState::AddHPKP(const std::string& host, |
317 const base::Time& created, | |
669 const base::Time& expiry, | 318 const base::Time& expiry, |
670 bool include_subdomains, | 319 bool include_subdomains, |
671 const HashValueVector& hashes) { | 320 const HashValueVector& hashes) { |
672 // Copy-and-modify the existing DomainState for this host (if any). | 321 return AddHPKPHashedHost(HashHost(host), created, expiry, |
673 TransportSecurityState::DomainState domain_state; | 322 include_subdomains, hashes); |
674 const std::string canonicalized_host = CanonicalizeHost(host); | 323 } |
675 const std::string hashed_host = HashHost(canonicalized_host); | 324 |
676 DomainStateMap::const_iterator i = enabled_hosts_.find( | 325 bool TransportSecurityState::AddHSTSHashedHost(const std::string& hashed_host, |
677 hashed_host); | 326 const base::Time& created, |
678 if (i != enabled_hosts_.end()) | 327 const base::Time& expiry, |
679 domain_state = i->second; | 328 bool include_subdomains) { |
680 | 329 |
681 domain_state.created = base::Time::Now(); | 330 DynamicEntry entry(include_subdomains, created, expiry); |
682 domain_state.include_subdomains = include_subdomains; | 331 bool retval = hsts_entries_.AddEntry(hashed_host, entry); |
Ryan Sleevi
2013/03/23 04:24:13
naming nit: s/retval/result/
unsafe
2013/03/23 08:19:39
Done.
| |
683 domain_state.dynamic_spki_hashes_expiry = expiry; | 332 CheckDirty(); |
684 domain_state.dynamic_spki_hashes = hashes; | 333 return retval; |
685 EnableHost(host, domain_state); | 334 } |
686 return true; | 335 |
687 } | 336 bool TransportSecurityState::AddHPKPHashedHost(const std::string& hashed_host, |
688 | 337 const base::Time& created, |
689 // static | 338 const base::Time& expiry, |
690 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, | 339 bool include_subdomains, |
691 bool sni_enabled) { | 340 const HashValueVector& hashes) { |
692 std::string canonicalized_host = CanonicalizeHost(host); | 341 if (hashes.empty()) |
693 const struct HSTSPreload* entry = | 342 return false; |
694 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 343 HPKPEntry entry(include_subdomains, created, expiry, hashes); |
695 | 344 bool retval = hpkp_entries_.AddEntry(hashed_host, entry); |
696 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 345 CheckDirty(); |
697 return true; | 346 return retval; |
698 | 347 } |
699 if (sni_enabled) { | 348 |
700 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 349 bool TransportSecurityState::DeleteHSTS(const std::string& host) { |
701 kNumPreloadedSNISTS); | 350 bool retval = hsts_entries_.DeleteEntry(HashHost(host)); |
702 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | 351 CheckDirty(); |
703 return true; | 352 return retval; |
704 } | 353 } |
705 | 354 |
706 return false; | 355 bool TransportSecurityState::DeleteHPKP(const std::string& host) { |
707 } | 356 bool retval = hpkp_entries_.DeleteEntry(HashHost(host)); |
708 | 357 CheckDirty(); |
709 // static | 358 return retval; |
710 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { | 359 } |
711 std::string canonicalized_host = CanonicalizeHost(host); | 360 |
712 | 361 const std::map<std::string, TransportSecurityState::DynamicEntry>& |
713 const struct HSTSPreload* entry = | 362 TransportSecurityState::GetHSTSEntries() const { |
714 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | 363 return hsts_entries_; |
715 | 364 } |
716 if (!entry) { | 365 |
717 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | 366 const std::map<std::string, TransportSecurityState::HPKPEntry>& |
718 kNumPreloadedSNISTS); | 367 TransportSecurityState::GetHPKPEntries() const { |
719 } | 368 return hpkp_entries_; |
720 | 369 } |
721 if (!entry) { | 370 |
722 // We don't care to report pin failures for dynamic pins. | |
723 return; | |
724 } | |
725 | |
726 DCHECK(entry); | |
727 DCHECK(entry->pins.required_hashes); | |
728 DCHECK(entry->second_level_domain_name != DOMAIN_NOT_PINNED); | |
729 | |
730 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", | |
731 entry->second_level_domain_name, DOMAIN_NUM_EVENTS); | |
732 } | |
733 | |
734 // static | |
735 bool TransportSecurityState::IsBuildTimely() { | 371 bool TransportSecurityState::IsBuildTimely() { |
736 const base::Time build_time = base::GetBuildTime(); | 372 const base::Time build_time = base::GetBuildTime(); |
737 // We consider built-in information to be timely for 10 weeks. | 373 // We consider built-in information to be timely for 10 weeks. |
738 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; | 374 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; |
739 } | 375 } |
740 | 376 |
741 bool TransportSecurityState::GetStaticDomainState( | 377 void TransportSecurityState::CheckDirty() { |
742 const std::string& canonicalized_host, | |
743 bool sni_enabled, | |
744 DomainState* out) { | |
745 DCHECK(CalledOnValidThread()); | 378 DCHECK(CalledOnValidThread()); |
746 | 379 if (delegate_ && (hsts_entries_.dirty || hpkp_entries_.dirty)) |
747 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; | 380 delegate_->StateIsDirty(this); |
748 out->include_subdomains = false; | 381 hsts_entries_.dirty = false; |
749 | 382 hpkp_entries_.dirty = false; |
750 const bool is_build_timely = IsBuildTimely(); | 383 } |
751 | 384 |
752 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | 385 // DynamicEntry and subclasses (e.g. HPKPEntry) |
753 std::string host_sub_chunk(&canonicalized_host[i], | 386 |
754 canonicalized_host.size() - i); | 387 TransportSecurityState::DynamicEntry::DynamicEntry() |
755 out->domain = DNSDomainToString(host_sub_chunk); | 388 : include_subdomains_(false), created_(), expiry_() { |
Ryan Sleevi
2013/03/23 04:24:13
style nit: each member initializer goes on a singl
| |
756 std::string hashed_host(HashHost(host_sub_chunk)); | 389 } |
757 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { | 390 |
758 *out = forced_hosts_[hashed_host]; | 391 TransportSecurityState::DynamicEntry::~DynamicEntry() { |
759 out->domain = DNSDomainToString(host_sub_chunk); | 392 } |
760 return true; | 393 |
761 } | 394 TransportSecurityState::DynamicEntry::DynamicEntry(bool include_subdomains, |
762 bool ret; | 395 const base::Time& created, |
763 if (is_build_timely && | 396 const base::Time& expiry): |
764 HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, | 397 include_subdomains_(include_subdomains), created_(created), expiry_(expiry) { |
Ryan Sleevi
2013/03/23 04:24:13
style nit:
TransportSecurityState::DynamicEntry:
| |
765 &ret)) { | 398 } |
766 return ret; | 399 |
767 } | 400 TransportSecurityState::HPKPEntry::HPKPEntry() |
768 if (sni_enabled && | 401 : DynamicEntry(), good_hashes_() { |
769 is_build_timely && | 402 } |
770 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, | 403 |
771 out, &ret)) { | 404 TransportSecurityState::HPKPEntry::~HPKPEntry() { |
772 return ret; | 405 } |
773 } | 406 |
774 } | 407 TransportSecurityState::HPKPEntry::HPKPEntry( |
775 | 408 bool include_subdomains, |
776 return false; | 409 const base::Time& created, |
777 } | 410 const base::Time& expiry, |
778 | 411 const HashValueVector& good_hashes): |
779 void TransportSecurityState::AddOrUpdateEnabledHosts( | 412 DynamicEntry(include_subdomains, created, expiry), |
780 const std::string& hashed_host, const DomainState& state) { | 413 good_hashes_(good_hashes) { |
781 enabled_hosts_[hashed_host] = state; | 414 } |
782 } | 415 |
783 | 416 // DomainState |
784 void TransportSecurityState::AddOrUpdateForcedHosts( | |
785 const std::string& hashed_host, const DomainState& state) { | |
786 forced_hosts_[hashed_host] = state; | |
787 } | |
788 | 417 |
789 TransportSecurityState::DomainState::DomainState() | 418 TransportSecurityState::DomainState::DomainState() |
790 : upgrade_mode(MODE_FORCE_HTTPS), | 419 : should_upgrade_(false), has_public_key_pins_(false), |
791 created(base::Time::Now()), | 420 is_google_pinned_property_(false), report_uma_on_pin_failure_(false), |
792 include_subdomains(false) { | 421 public_key_pins_good_hashes_(), public_key_pins_bad_hashes_(), |
422 second_level_domain_name_(DOMAIN_NOT_PINNED) { | |
793 } | 423 } |
794 | 424 |
795 TransportSecurityState::DomainState::~DomainState() { | 425 TransportSecurityState::DomainState::~DomainState() { |
796 } | 426 } |
797 | 427 |
428 bool TransportSecurityState::DomainState::HasPublicKeyPins() const { | |
429 return has_public_key_pins_; | |
430 } | |
431 | |
798 bool TransportSecurityState::DomainState::CheckPublicKeyPins( | 432 bool TransportSecurityState::DomainState::CheckPublicKeyPins( |
799 const HashValueVector& hashes) const { | 433 const HashValueVector& hashes) const { |
800 // Validate that hashes is not empty. By the time this code is called (in | 434 if (HashesIntersect(public_key_pins_bad_hashes_, hashes)) { |
801 // production), that should never happen, but it's good to be defensive. | 435 LOG(ERROR) << "Rejecting public key chain. Validated chain: " |
802 // And, hashes *can* be empty in some test scenarios. | 436 << HashesToBase64String(hashes) |
803 if (hashes.empty()) { | 437 << ", matches one or more bad hashes: " |
804 LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned " | 438 << HashesToBase64String(public_key_pins_bad_hashes_); |
805 "domain " << domain; | |
806 return false; | 439 return false; |
807 } | 440 } |
808 | 441 |
809 if (HashesIntersect(bad_static_spki_hashes, hashes)) { | 442 // If there are no good pins, then any valid chain is acceptable. |
810 LOG(ERROR) << "Rejecting public key chain for domain " << domain | 443 // Otherwise, there has to be a match. |
811 << ". Validated chain: " << HashesToBase64String(hashes) | 444 if (public_key_pins_good_hashes_.empty() || |
812 << ", matches one or more bad hashes: " | 445 HashesIntersect(public_key_pins_good_hashes_, hashes)) |
Ryan Sleevi
2013/03/23 04:24:13
style nit: braces
| |
813 << HashesToBase64String(bad_static_spki_hashes); | |
814 return false; | |
815 } | |
816 | |
817 // If there are no pins, then any valid chain is acceptable. | |
818 if (dynamic_spki_hashes.empty() && static_spki_hashes.empty()) | |
819 return true; | 446 return true; |
820 | 447 |
821 if (HashesIntersect(dynamic_spki_hashes, hashes) || | 448 LOG(ERROR) << "Rejecting public key chain. Validated chain: " |
822 HashesIntersect(static_spki_hashes, hashes)) { | 449 << HashesToBase64String(hashes) |
823 return true; | 450 << ", expected: " |
824 } | 451 << HashesToBase64String(public_key_pins_good_hashes_); |
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; | 452 return false; |
831 } | 453 } |
832 | 454 |
833 bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const { | 455 bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const { |
834 return upgrade_mode == MODE_FORCE_HTTPS; | 456 return should_upgrade_; |
835 } | 457 } |
836 | 458 |
837 bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const { | 459 bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const { |
838 return true; | 460 return should_upgrade_; |
839 } | 461 } |
840 | 462 |
841 bool TransportSecurityState::DomainState::Equals( | 463 bool TransportSecurityState::DomainState::IsGooglePinnedProperty() const { |
842 const DomainState& other) const { | 464 return is_google_pinned_property_; |
843 // TODO(palmer): Implement this | 465 } |
844 (void) other; | 466 |
845 return true; | 467 void TransportSecurityState::DomainState::ReportUMAOnPinFailure() const { |
846 } | 468 if (report_uma_on_pin_failure_) { |
847 | 469 UMA_HISTOGRAM_ENUMERATION("Net.PublicKeyPinFailureDomain", |
848 bool TransportSecurityState::DomainState::HasPublicKeyPins() const { | 470 second_level_domain_name_, DOMAIN_NUM_EVENTS); |
849 return static_spki_hashes.size() > 0 || | 471 } |
850 bad_static_spki_hashes.size() > 0 || | 472 } |
851 dynamic_spki_hashes.size() > 0; | 473 |
474 const HashValueVector& | |
475 TransportSecurityState::DomainState::GetPublicKeyPinsGoodHashes() const { | |
476 return public_key_pins_good_hashes_; | |
477 } | |
478 | |
479 const HashValueVector& | |
480 TransportSecurityState::DomainState::GetPublicKeyPinsBadHashes() const { | |
481 return public_key_pins_bad_hashes_; | |
852 } | 482 } |
853 | 483 |
854 } // namespace | 484 } // namespace |
OLD | NEW |