OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/base/transport_security_state.h" | |
6 | |
7 #if defined(USE_OPENSSL) | |
8 #include <openssl/ecdsa.h> | |
9 #include <openssl/ssl.h> | |
10 #else // !defined(USE_OPENSSL) | |
11 #include <cryptohi.h> | |
12 #include <hasht.h> | |
13 #include <keyhi.h> | |
14 #include <pk11pub.h> | |
15 #include <nspr.h> | |
16 #endif | |
17 | |
18 #include <algorithm> | |
19 | |
20 #include "base/base64.h" | |
21 #include "base/build_time.h" | |
22 #include "base/logging.h" | |
23 #include "base/memory/scoped_ptr.h" | |
24 #include "base/metrics/histogram.h" | |
25 #include "base/sha1.h" | |
26 #include "base/string_number_conversions.h" | |
27 #include "base/string_util.h" | |
28 #include "base/time.h" | |
29 #include "base/utf_string_conversions.h" | |
30 #include "base/values.h" | |
31 #include "crypto/sha2.h" | |
32 #include "googleurl/src/gurl.h" | |
33 #include "net/base/dns_util.h" | |
34 #include "net/base/ssl_info.h" | |
35 #include "net/base/x509_cert_types.h" | |
36 #include "net/base/x509_certificate.h" | |
37 #include "net/http/http_security_headers.h" | |
38 | |
39 #if defined(USE_OPENSSL) | |
40 #include "crypto/openssl_util.h" | |
41 #endif | |
42 | |
43 namespace net { | |
44 | |
45 namespace { | |
46 | |
47 std::string HashesToBase64String(const HashValueVector& hashes) { | |
48 std::string str; | |
49 for (size_t i = 0; i != hashes.size(); ++i) { | |
50 if (i != 0) | |
51 str += ","; | |
52 str += hashes[i].ToString(); | |
53 } | |
54 return str; | |
55 } | |
56 | |
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 | |
64 // |a| or |b| is empty, returns false. | |
65 bool HashesIntersect(const HashValueVector& a, | |
66 const HashValueVector& b) { | |
67 for (HashValueVector::const_iterator i = a.begin(); i != a.end(); ++i) { | |
68 HashValueVector::const_iterator j = | |
69 std::find_if(b.begin(), b.end(), HashValuesEqual(*i)); | |
70 if (j != b.end()) | |
71 return true; | |
72 } | |
73 return false; | |
74 } | |
75 | |
76 bool AddHash(const char* sha1_hash, | |
77 HashValueVector* out) { | |
78 HashValue hash(HASH_VALUE_SHA1); | |
79 memcpy(hash.data(), sha1_hash, hash.size()); | |
80 out->push_back(hash); | |
81 return true; | |
82 } | |
83 | |
84 } // namespace | |
85 | |
86 TransportSecurityState::TransportSecurityState() | |
87 : delegate_(NULL) { | |
88 } | |
89 | |
90 TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state) | |
91 : iterator_(state.enabled_hosts_.begin()), | |
92 end_(state.enabled_hosts_.end()) { | |
93 } | |
94 | |
95 TransportSecurityState::Iterator::~Iterator() {} | |
96 | |
97 void TransportSecurityState::SetDelegate( | |
98 TransportSecurityState::Delegate* delegate) { | |
99 delegate_ = delegate; | |
100 } | |
101 | |
102 void TransportSecurityState::EnableHost(const std::string& host, | |
103 const DomainState& state) { | |
104 DCHECK(CalledOnValidThread()); | |
105 | |
106 const std::string canonicalized_host = CanonicalizeHost(host); | |
107 if (canonicalized_host.empty()) | |
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 } | |
128 | |
129 bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) { | |
130 DCHECK(CalledOnValidThread()); | |
131 | |
132 const std::string canonicalized_host = CanonicalizeHost(host); | |
133 if (canonicalized_host.empty()) | |
134 return false; | |
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 } | |
145 | |
146 bool TransportSecurityState::GetDomainState(const std::string& host, | |
147 bool sni_enabled, | |
148 DomainState* result) { | |
149 DCHECK(CalledOnValidThread()); | |
150 | |
151 DomainState state; | |
152 const std::string canonicalized_host = CanonicalizeHost(host); | |
153 if (canonicalized_host.empty()) | |
154 return false; | |
155 | |
156 bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled, | |
157 &state); | |
158 std::string canonicalized_preload = CanonicalizeHost(state.domain); | |
159 | |
160 base::Time current_time(base::Time::Now()); | |
161 | |
162 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | |
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 } | |
170 | |
171 DomainStateMap::iterator j = | |
172 enabled_hosts_.find(HashHost(host_sub_chunk)); | |
173 if (j == enabled_hosts_.end()) | |
174 continue; | |
175 | |
176 if (current_time > j->second.upgrade_expiry && | |
177 current_time > j->second.dynamic_spki_hashes_expiry) { | |
178 enabled_hosts_.erase(j); | |
179 DirtyNotify(); | |
180 continue; | |
181 } | |
182 | |
183 state = j->second; | |
184 state.domain = DNSDomainToString(host_sub_chunk); | |
185 | |
186 // Succeed if we matched the domain exactly or if subdomain matches are | |
187 // allowed. | |
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 } | |
570 } | |
571 if (entries[j].pins.excluded_hashes) { | |
572 const char* const* sha1_hash = entries[j].pins.excluded_hashes; | |
573 while (*sha1_hash) { | |
574 AddHash(*sha1_hash, &out->bad_static_spki_hashes); | |
575 sha1_hash++; | |
576 } | |
577 } | |
578 } | |
579 return true; | |
580 } | |
581 } | |
582 return false; | |
583 } | |
584 | |
585 #include "net/base/transport_security_state_static.h" | |
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) { | |
616 base::Time now = base::Time::Now(); | |
617 TransportSecurityState::DomainState domain_state; | |
618 if (ParseHSTSHeader(now, value, &domain_state.upgrade_expiry, | |
619 &domain_state.include_subdomains)) { | |
620 // Handle max-age == 0 | |
621 if (now == domain_state.upgrade_expiry) | |
622 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | |
623 else | |
624 domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS; | |
625 domain_state.created = now; | |
626 EnableHost(host, domain_state); | |
627 return true; | |
628 } | |
629 return false; | |
630 } | |
631 | |
632 bool TransportSecurityState::AddHPKPHeader(const std::string& host, | |
633 const std::string& value, | |
634 const SSLInfo& ssl_info) { | |
635 base::Time now = base::Time::Now(); | |
636 TransportSecurityState::DomainState domain_state; | |
637 if (ParseHPKPHeader(now, value, ssl_info.public_key_hashes, | |
638 &domain_state.dynamic_spki_hashes_expiry, | |
639 &domain_state.dynamic_spki_hashes)) { | |
640 domain_state.upgrade_mode = DomainState::MODE_DEFAULT; | |
641 domain_state.created = now; | |
642 EnableHost(host, domain_state); | |
643 return true; | |
644 } | |
645 return false; | |
646 } | |
647 | |
648 bool TransportSecurityState::AddHSTS(const std::string& host, | |
649 const base::Time& expiry, | |
650 bool include_subdomains) { | |
651 // Copy-and-modify the existing DomainState for this host (if any). | |
652 TransportSecurityState::DomainState domain_state; | |
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 } | |
667 | |
668 bool TransportSecurityState::AddHPKP(const std::string& host, | |
669 const base::Time& expiry, | |
670 bool include_subdomains, | |
671 const HashValueVector& hashes) { | |
672 // Copy-and-modify the existing DomainState for this host (if any). | |
673 TransportSecurityState::DomainState domain_state; | |
674 const std::string canonicalized_host = CanonicalizeHost(host); | |
675 const std::string hashed_host = HashHost(canonicalized_host); | |
676 DomainStateMap::const_iterator i = enabled_hosts_.find( | |
677 hashed_host); | |
678 if (i != enabled_hosts_.end()) | |
679 domain_state = i->second; | |
680 | |
681 domain_state.created = base::Time::Now(); | |
682 domain_state.include_subdomains = include_subdomains; | |
683 domain_state.dynamic_spki_hashes_expiry = expiry; | |
684 domain_state.dynamic_spki_hashes = hashes; | |
685 EnableHost(host, domain_state); | |
686 return true; | |
687 } | |
688 | |
689 // static | |
690 bool TransportSecurityState::IsGooglePinnedProperty(const std::string& host, | |
691 bool sni_enabled) { | |
692 std::string canonicalized_host = CanonicalizeHost(host); | |
693 const struct HSTSPreload* entry = | |
694 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | |
695 | |
696 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | |
697 return true; | |
698 | |
699 if (sni_enabled) { | |
700 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | |
701 kNumPreloadedSNISTS); | |
702 if (entry && entry->pins.required_hashes == kGoogleAcceptableCerts) | |
703 return true; | |
704 } | |
705 | |
706 return false; | |
707 } | |
708 | |
709 // static | |
710 void TransportSecurityState::ReportUMAOnPinFailure(const std::string& host) { | |
711 std::string canonicalized_host = CanonicalizeHost(host); | |
712 | |
713 const struct HSTSPreload* entry = | |
714 GetHSTSPreload(canonicalized_host, kPreloadedSTS, kNumPreloadedSTS); | |
715 | |
716 if (!entry) { | |
717 entry = GetHSTSPreload(canonicalized_host, kPreloadedSNISTS, | |
718 kNumPreloadedSNISTS); | |
719 } | |
720 | |
721 if (!entry) { | |
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() { | |
736 const base::Time build_time = base::GetBuildTime(); | |
737 // We consider built-in information to be timely for 10 weeks. | |
738 return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; | |
739 } | |
740 | |
741 bool TransportSecurityState::GetStaticDomainState( | |
742 const std::string& canonicalized_host, | |
743 bool sni_enabled, | |
744 DomainState* out) { | |
745 DCHECK(CalledOnValidThread()); | |
746 | |
747 out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; | |
748 out->include_subdomains = false; | |
749 | |
750 const bool is_build_timely = IsBuildTimely(); | |
751 | |
752 for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { | |
753 std::string host_sub_chunk(&canonicalized_host[i], | |
754 canonicalized_host.size() - i); | |
755 out->domain = DNSDomainToString(host_sub_chunk); | |
756 std::string hashed_host(HashHost(host_sub_chunk)); | |
757 if (forced_hosts_.find(hashed_host) != forced_hosts_.end()) { | |
758 *out = forced_hosts_[hashed_host]; | |
759 out->domain = DNSDomainToString(host_sub_chunk); | |
760 return true; | |
761 } | |
762 bool ret; | |
763 if (is_build_timely && | |
764 HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, | |
765 &ret)) { | |
766 return ret; | |
767 } | |
768 if (sni_enabled && | |
769 is_build_timely && | |
770 HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, | |
771 out, &ret)) { | |
772 return ret; | |
773 } | |
774 } | |
775 | |
776 return false; | |
777 } | |
778 | |
779 void TransportSecurityState::AddOrUpdateEnabledHosts( | |
780 const std::string& hashed_host, const DomainState& state) { | |
781 enabled_hosts_[hashed_host] = state; | |
782 } | |
783 | |
784 void TransportSecurityState::AddOrUpdateForcedHosts( | |
785 const std::string& hashed_host, const DomainState& state) { | |
786 forced_hosts_[hashed_host] = state; | |
787 } | |
788 | |
789 TransportSecurityState::DomainState::DomainState() | |
790 : upgrade_mode(MODE_FORCE_HTTPS), | |
791 created(base::Time::Now()), | |
792 include_subdomains(false) { | |
793 } | |
794 | |
795 TransportSecurityState::DomainState::~DomainState() { | |
796 } | |
797 | |
798 bool TransportSecurityState::DomainState::CheckPublicKeyPins( | |
799 const HashValueVector& hashes) const { | |
800 // Validate that hashes is not empty. By the time this code is called (in | |
801 // production), that should never happen, but it's good to be defensive. | |
802 // And, hashes *can* be empty in some test scenarios. | |
803 if (hashes.empty()) { | |
804 LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned " | |
805 "domain " << domain; | |
806 return false; | |
807 } | |
808 | |
809 if (HashesIntersect(bad_static_spki_hashes, hashes)) { | |
810 LOG(ERROR) << "Rejecting public key chain for domain " << domain | |
811 << ". Validated chain: " << HashesToBase64String(hashes) | |
812 << ", matches one or more bad hashes: " | |
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; | |
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; | |
831 } | |
832 | |
833 bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const { | |
834 return upgrade_mode == MODE_FORCE_HTTPS; | |
835 } | |
836 | |
837 bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const { | |
838 return true; | |
839 } | |
840 | |
841 bool TransportSecurityState::DomainState::Equals( | |
842 const DomainState& other) const { | |
843 // TODO(palmer): Implement this | |
844 (void) other; | |
845 return true; | |
846 } | |
847 | |
848 bool TransportSecurityState::DomainState::HasPublicKeyPins() const { | |
849 return static_spki_hashes.size() > 0 || | |
850 bad_static_spki_hashes.size() > 0 || | |
851 dynamic_spki_hashes.size() > 0; | |
852 } | |
853 | |
854 } // namespace | |
OLD | NEW |